2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <CoreFoundation/CFBase.h>
27 #include <CoreFoundation/CFError.h>
29 #include <Security/SecBasePriv.h>
30 #include <Security/SecOTR.h>
31 #include <Security/SecOTRSession.h>
32 #include <Security/SecureObjectSync/SOSInternal.h>
33 #include <Security/SecureObjectSync/SOSFullPeerInfo.h>
34 #include <Security/SecureObjectSync/SOSPeerInfo.h>
35 #include <Security/SecureObjectSync/SOSPeer.h>
36 #include <Security/SecureObjectSync/SOSCoder.h>
38 #include <utilities/SecCFRelease.h>
39 #include <utilities/SecCFWrappers.h>
40 #include <utilities/SecIOFormat.h>
41 #include <utilities/SecCFError.h>
42 #include <utilities/debugging.h>
44 #include <utilities/der_plist.h>
45 #include <utilities/der_plist_internal.h>
47 #include <corecrypto/ccder.h>
48 #include <utilities/iCloudKeychainTrace.h>
50 #include "AssertMacros.h"
52 struct __OpaqueSOSCoder
{
54 SecOTRSessionRef sessRef
;
55 bool waitingForDataPacket
;
56 CFDataRef pendingResponse
;
59 static const char *SOSCoderString(SOSCoderStatus coderStatus
) {
60 switch (coderStatus
) {
61 case kSOSCoderDataReturned
: return "DataReturned";
62 case kSOSCoderNegotiating
: return "Negotiating";
63 case kSOSCoderNegotiationCompleted
: return "NegotiationCompleted";
64 case kSOSCoderFailure
: return "Failure";
65 case kSOSCoderStaleEvent
: return "StaleEvent";
66 case kSOSCoderTooNew
: return "TooNew";
67 default: return "StatusUnknown";
72 static void logRawCoderMessage(const uint8_t* der, uint8_t* der_end, bool encoding)
75 CFStringRef hexMessage = NULL;
77 CFIndex length = der_end - der;
78 CFDataRef message = CFDataCreate(kCFAllocatorDefault, der, length);
79 hexMessage = CFDataCopyHexString(message);
80 secnoticeq("coder", "%s RAW [%ld] %@", encoding ? "encode" : "decode", length, hexMessage);
81 CFReleaseSafe(message);
83 CFReleaseSafe(hexMessage);
88 static size_t der_sizeof_bool(bool value
) {
89 return ccder_sizeof(CCDER_BOOLEAN
, 1);
92 static uint8_t* der_encode_bool(bool value
, const uint8_t *der
, uint8_t *der_end
) {
93 uint8_t valueByte
= value
;
94 return ccder_encode_tl(CCDER_BOOLEAN
, 1, der
,
95 ccder_encode_body(1, &valueByte
, der
, der_end
));
98 static const uint8_t* der_decode_bool(bool *value
, const uint8_t *der
, const uint8_t *der_end
) {
99 size_t payload_size
= 0;
101 der
= ccder_decode_tl(CCDER_BOOLEAN
, &payload_size
, der
, der_end
);
103 if (payload_size
!= 1) {
108 *value
= (*der
!= 0);
115 static CFMutableDataRef
sessSerialized(SOSCoderRef coder
, CFErrorRef
*error
) {
116 CFMutableDataRef otr_state
= NULL
;
118 if(!coder
|| !coder
->sessRef
) {
119 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType
, NULL
, error
, 0, CFSTR("No session reference."));
123 if ((otr_state
= CFDataCreateMutable(NULL
, 0)) == NULL
) {
124 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, NULL
, error
, 0, CFSTR("Mutable Data allocation failed."));
128 if (errSecSuccess
!= SecOTRSAppendSerialization(coder
->sessRef
, otr_state
)) {
129 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, NULL
, error
, 0, CFSTR("Append Serialization failed."));
130 CFReleaseSafe(otr_state
);
138 static size_t der_sizeof_optional_data(CFDataRef data
) {
139 return data
? der_sizeof_data(data
, NULL
) : 0;
142 static uint8_t* der_encode_optional_data(CFDataRef data
, CFErrorRef
*error
, const uint8_t* der
, uint8_t* der_end
) {
143 return data
? der_encode_data(data
, error
, der
, der_end
) : der_end
;
148 static size_t SOSCoderGetDEREncodedSize(SOSCoderRef coder
, CFErrorRef
*error
) {
149 size_t encoded_size
= 0;
150 CFMutableDataRef otr_state
= sessSerialized(coder
, error
);
153 size_t data_size
= der_sizeof_data(otr_state
, error
);
154 size_t waiting_size
= der_sizeof_bool(coder
->waitingForDataPacket
);
155 size_t pending_size
= der_sizeof_optional_data(coder
->pendingResponse
);
157 if ((data_size
!= 0) && (waiting_size
!= 0))
159 encoded_size
= ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, data_size
+ waiting_size
+ pending_size
);
161 CFReleaseSafe(otr_state
);
167 static uint8_t* SOSCoderEncodeToDER(SOSCoderRef coder
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
) {
168 if(!der_end
) return NULL
;
169 uint8_t* result
= NULL
;
170 CFMutableDataRef otr_state
= sessSerialized(coder
, error
);
173 result
= ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
174 der_encode_data(otr_state
, error
, der
,
175 der_encode_bool(coder
->waitingForDataPacket
, der
,
176 der_encode_optional_data(coder
->pendingResponse
, error
, der
, der_end
))));
177 CFReleaseSafe(otr_state
);
183 CFDataRef
SOSCoderCopyDER(SOSCoderRef coder
, CFErrorRef
* error
) {
184 CFMutableDataRef encoded
= NULL
;
185 size_t encoded_size
= SOSCoderGetDEREncodedSize(coder
, error
);
187 if (encoded_size
> 0) {
188 encoded
= CFDataCreateMutable(NULL
, encoded_size
);
190 CFDataSetLength(encoded
, encoded_size
);
191 uint8_t * der
= CFDataGetMutableBytePtr(encoded
);
192 uint8_t * der_end
= der
+ encoded_size
;
193 if (!SOSCoderEncodeToDER(coder
, error
, der
, der_end
)) {
194 CFReleaseNull(encoded
);
202 SOSCoderRef
SOSCoderCreateFromData(CFDataRef exportedData
, CFErrorRef
*error
) {
203 // TODO: fill in errors for all failure cases
204 //require_action_quiet(coder, xit, SOSCreateError(kSOSErrorSendFailure, CFSTR("No coder for peer"), NULL, error));
206 SOSCoderRef p
= calloc(1, sizeof(struct __OpaqueSOSCoder
));
208 const uint8_t *der
= CFDataGetBytePtr(exportedData
);
209 const uint8_t *der_end
= der
+ CFDataGetLength(exportedData
);
211 CFDataRef otr_data
= NULL
;
214 require(ccder_decode_tag(&tag
, der
, der_end
),fail
);
217 case CCDER_OCTET_STRING
: // TODO: this code is safe to delete?
219 der
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, der_end
);
220 p
->waitingForDataPacket
= false;
224 case CCDER_CONSTRUCTED_SEQUENCE
:
226 const uint8_t *sequence_end
= NULL
;
227 der
= ccder_decode_sequence_tl(&sequence_end
, der
, der_end
);
229 require_action_quiet(sequence_end
== der_end
, fail
, SecCFDERCreateError(kSOSErrorDecodeFailure
, CFSTR("Extra data in SOS coder"), NULL
, error
));
231 der
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, sequence_end
);
232 der
= der_decode_bool(&p
->waitingForDataPacket
, der
, sequence_end
);
233 if (der
!= sequence_end
) { // optionally a pending response
234 der
= der_decode_data(kCFAllocatorDefault
, 0, &p
->pendingResponse
, error
, der
, sequence_end
);
240 SecCFDERCreateError(kSOSErrorDecodeFailure
, CFSTR("Unsupported SOS Coder DER"), NULL
, error
);
246 p
->sessRef
= SecOTRSessionCreateFromData(NULL
, otr_data
);
247 require(p
->sessRef
, fail
);
249 CFReleaseSafe(otr_data
);
254 CFReleaseSafe(otr_data
);
259 SOSCoderRef
SOSCoderCreate(SOSPeerInfoRef peerInfo
, SOSFullPeerInfoRef myPeerInfo
, CFBooleanRef useCompact
, CFErrorRef
*error
) {
260 CFAllocatorRef allocator
= CFGetAllocator(peerInfo
);
262 SOSCoderRef coder
= calloc(1, sizeof(struct __OpaqueSOSCoder
));
263 CFErrorRef localError
= NULL
;
265 SecOTRFullIdentityRef myRef
= NULL
;
266 SecOTRPublicIdentityRef peerRef
= NULL
;
267 SecKeyRef privateKey
= NULL
;
268 SecKeyRef publicKey
= NULL
;
270 if (myPeerInfo
&& peerInfo
) {
271 privateKey
= SOSFullPeerInfoCopyDeviceKey(myPeerInfo
, &localError
);
272 require_quiet(privateKey
, errOut
);
274 myRef
= SecOTRFullIdentityCreateFromSecKeyRef(allocator
, privateKey
, &localError
);
275 require_quiet(myRef
, errOut
);
277 CFReleaseNull(privateKey
);
279 publicKey
= SOSPeerInfoCopyPubKey(peerInfo
);
281 peerRef
= SecOTRPublicIdentityCreateFromSecKeyRef(allocator
, publicKey
, &localError
);
282 require_quiet(peerRef
, errOut
);
284 if(useCompact
== kCFBooleanTrue
)
285 coder
->sessRef
= SecOTRSessionCreateFromIDAndFlags(allocator
, myRef
, peerRef
, kSecOTRUseAppleCustomMessageFormat
);
288 coder
->sessRef
= SecOTRSessionCreateFromID(allocator
, myRef
, peerRef
);
290 require(coder
->sessRef
, errOut
);
292 coder
->waitingForDataPacket
= false;
293 coder
->pendingResponse
= NULL
;
295 CFReleaseNull(publicKey
);
296 CFReleaseNull(privateKey
);
297 CFReleaseNull(myRef
);
298 CFReleaseNull(peerRef
);
300 secnotice("coder", "NULL Coder requested, no transport security");
303 SOSCoderStart(coder
, NULL
);
308 secerror("Coder create failed: %@\n", localError
? localError
: (CFTypeRef
)CFSTR("No local error in SOSCoderCreate"));
309 secerror("Coder create failed: %@\n", error
? *error
: (CFTypeRef
)CFSTR("WTF NULL?"));
310 CFReleaseNull(myRef
);
311 CFReleaseNull(peerRef
);
312 CFReleaseNull(publicKey
);
313 CFReleaseNull(privateKey
);
319 void SOSCoderDispose(SOSCoderRef coder
)
322 CFReleaseNull(coder
->sessRef
);
323 CFReleaseNull(coder
->pendingResponse
);
324 CFReleaseNull(coder
->peer_id
);
330 void SOSCoderReset(SOSCoderRef coder
)
332 SecOTRSessionReset(coder
->sessRef
);
333 coder
->waitingForDataPacket
= false;
334 CFReleaseNull(coder
->pendingResponse
);
337 CFDataRef
SOSCoderCopyPendingResponse(SOSCoderRef coder
)
339 return coder
->pendingResponse
? CFDataCreateCopy(kCFAllocatorDefault
, coder
->pendingResponse
) : NULL
;
342 void SOSCoderConsumeResponse(SOSCoderRef coder
)
344 CFReleaseNull(coder
->pendingResponse
);
347 static bool SOSOTRSAppendStartPacket(SecOTRSessionRef session
, CFMutableDataRef appendPacket
, CFErrorRef
*error
) {
348 OSStatus otrStatus
= SecOTRSAppendStartPacket(session
, appendPacket
);
349 if (otrStatus
!= errSecSuccess
) {
350 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("append start packet returned: %" PRIdOSStatus
), otrStatus
);
352 return otrStatus
== errSecSuccess
;
355 // Start OTR negotiation if we haven't already done so.
357 SOSCoderStart(SOSCoderRef coder
, CFErrorRef
*error
) {
358 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
359 CFStringRef beginState
= NULL
;
360 SOSCoderStatus result
= kSOSCoderFailure
;
361 CFMutableDataRef startPacket
= NULL
;
363 require_action_quiet(coder
->sessRef
, coderFailure
, CFStringAppend(action
, CFSTR("*** no otr session ***")));
364 beginState
= CFCopyDescription(coder
->sessRef
);
365 require_action_quiet(!coder
->waitingForDataPacket
, negotiatingOut
, CFStringAppend(action
, CFSTR("waiting for peer to send first data packet")));
366 require_action_quiet(!SecOTRSGetIsReadyForMessages(coder
->sessRef
), coderFailure
, CFStringAppend(action
, CFSTR("otr session ready"));
367 result
= kSOSCoderDataReturned
);
368 require_action_quiet(SecOTRSGetIsIdle(coder
->sessRef
), negotiatingOut
, CFStringAppend(action
, CFSTR("otr negotiating already")));
369 require_action_quiet(startPacket
= CFDataCreateMutable(kCFAllocatorDefault
, 0), coderFailure
, SOSCreateError(kSOSErrorAllocationFailure
, CFSTR("alloc failed"), NULL
, error
));
370 require_quiet(SOSOTRSAppendStartPacket(coder
->sessRef
, startPacket
, error
), coderFailure
);
371 CFRetainAssign(coder
->pendingResponse
, startPacket
);
374 result
= kSOSCoderNegotiating
;
377 if (result
== kSOSCoderFailure
&& error
&& *error
)
378 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
379 secinfo("coder", "%@ %s %@ %@ returned %s", beginState
,
380 SecOTRPacketTypeString(startPacket
), action
, coder
->sessRef
, SOSCoderString(result
));
381 CFReleaseNull(startPacket
);
382 CFReleaseSafe(beginState
);
390 SOSCoderResendDH(SOSCoderRef coder
, CFErrorRef
*error
) {
391 if(coder
->sessRef
== NULL
) return kSOSCoderDataReturned
;
392 CFMutableDataRef startPacket
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
393 SOSCoderStatus result
= kSOSCoderFailure
;
394 require_noerr_quiet(SecOTRSAppendRestartPacket(coder
->sessRef
, startPacket
), exit
);
395 secnotice("coder", "Resending OTR Start %@", startPacket
);
396 CFRetainAssign(coder
->pendingResponse
, startPacket
);
397 result
= kSOSCoderNegotiating
;
399 CFReleaseNull(startPacket
);
404 static SOSCoderStatus
nullCoder(CFDataRef from
, CFMutableDataRef
*to
) {
405 *to
= CFDataCreateMutableCopy(NULL
, CFDataGetLength(from
), from
);
406 return kSOSCoderDataReturned
;
409 SOSCoderStatus
SOSCoderUnwrap(SOSCoderRef coder
, CFDataRef codedMessage
, CFMutableDataRef
*message
,
410 CFStringRef clientId
, CFErrorRef
*error
) {
411 if(codedMessage
== NULL
) return kSOSCoderDataReturned
;
412 if(coder
->sessRef
== NULL
) return nullCoder(codedMessage
, message
);
413 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
414 /* This should be the "normal" case. We just use OTR to unwrap the received message. */
415 SOSCoderStatus result
= kSOSCoderFailure
;
417 CFStringRef beginState
= CFCopyDescription(coder
->sessRef
);
418 enum SecOTRSMessageKind kind
= SecOTRSGetMessageKind(coder
->sessRef
, codedMessage
);
421 case kOTRNegotiationPacket
: {
422 /* If we're in here we haven't completed negotiating a session. Use SecOTRSProcessPacket() to go through
423 the negotiation steps and immediately send a reply back if necessary using the sendBlock. This
424 assumes the sendBlock is still available.
426 CFMutableDataRef response
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
427 OSStatus ppstatus
= errSecSuccess
;
429 switch (ppstatus
= SecOTRSProcessPacket(coder
->sessRef
, codedMessage
, response
)) {
431 if (CFDataGetLength(response
) > 1) {
432 CFStringAppendFormat(action
, NULL
, CFSTR("Sending OTR Response %s"), SecOTRPacketTypeString(response
));
433 CFRetainAssign(coder
->pendingResponse
, response
);
434 result
= kSOSCoderNegotiating
;
435 if (SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
436 CFStringAppend(action
, CFSTR(" begin waiting for data packet"));
437 coder
->waitingForDataPacket
= true;
439 } else if(!SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
440 CFStringAppend(action
, CFSTR("stuck?"));
441 result
= kSOSCoderNegotiating
;
443 CFStringAppend(action
, CFSTR("completed negotiation"));
444 result
= kSOSCoderNegotiationCompleted
;
445 coder
->waitingForDataPacket
= false;
449 CFStringAppend(action
, CFSTR("resending dh"));
450 result
= SOSCoderResendDH(coder
, error
);
453 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ Cannot negotiate session (%ld)"), clientId
, (long)ppstatus
);
454 result
= kSOSCoderFailure
;
458 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ Cannot allocate CFData"), clientId
);
459 result
= kSOSCoderFailure
;
462 CFReleaseNull(response
);
468 if(!SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
469 CFStringAppend(action
, CFSTR("not ready for data; resending DH packet"));
470 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed
, 1);
471 result
= SOSCoderResendDH(coder
, error
);
473 if (coder
->waitingForDataPacket
) {
474 CFStringAppend(action
, CFSTR("got data packet we were waiting for "));
475 coder
->waitingForDataPacket
= false;
477 CFMutableDataRef exposed
= CFDataCreateMutable(0, 0);
478 OSStatus otrResult
= SecOTRSVerifyAndExposeMessage(coder
->sessRef
, codedMessage
, exposed
);
479 CFStringAppend(action
, CFSTR("verify and expose message"));
481 if (otrResult
== errSecOTRTooOld
) {
482 CFStringAppend(action
, CFSTR(" too old"));
483 result
= kSOSCoderStaleEvent
;
485 else if(otrResult
== errSecOTRIDTooNew
){
486 CFStringAppend(action
, CFSTR(" too new"));
487 result
= kSOSCoderTooNew
;
489 SecError(otrResult
, error
, CFSTR("%@ Cannot expose message: %" PRIdOSStatus
), clientId
, otrResult
);
490 secerror("%@ Decode OTR Protected Packet: %@", clientId
, error
? *error
: NULL
);
491 result
= kSOSCoderFailure
;
494 CFStringAppend(action
, CFSTR("decoded OTR protected packet"));
497 result
= kSOSCoderDataReturned
;
499 CFReleaseNull(exposed
);
504 secerror("%@ Unknown packet type: %@", clientId
, codedMessage
);
505 SOSCreateError(kSOSErrorDecodeFailure
, CFSTR("Unknown packet type"), (error
!= NULL
) ? *error
: NULL
, error
);
506 result
= kSOSCoderFailure
;
511 if (result
== kSOSCoderFailure
&& error
&& *error
)
512 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
513 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId
, beginState
,
514 SecOTRPacketTypeString(codedMessage
), action
, coder
->sessRef
, SOSCoderString(result
));
515 CFReleaseSafe(beginState
);
522 SOSCoderStatus
SOSCoderWrap(SOSCoderRef coder
, CFDataRef message
, CFMutableDataRef
*codedMessage
, CFStringRef clientId
, CFErrorRef
*error
) {
523 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
524 SOSCoderStatus result
= kSOSCoderDataReturned
;
525 CFStringRef beginState
= NULL
;
526 CFMutableDataRef encoded
= NULL
;
527 OSStatus otrStatus
= 0;
529 require_action_quiet(coder
->sessRef
, errOut
,
530 CFStringAppend(action
, CFSTR("*** using null coder ***"));
531 result
= nullCoder(message
, codedMessage
));
532 beginState
= CFCopyDescription(coder
->sessRef
);
533 require_action_quiet(SecOTRSGetIsReadyForMessages(coder
->sessRef
), errOut
,
534 CFStringAppend(action
, CFSTR("not ready"));
535 result
= kSOSCoderNegotiating
);
536 require_action_quiet(!coder
->waitingForDataPacket
, errOut
,
537 CFStringAppend(action
, CFSTR("waiting for peer to send data packet first"));
538 result
= kSOSCoderNegotiating
);
539 require_action_quiet(encoded
= CFDataCreateMutable(kCFAllocatorDefault
, 0), errOut
,
540 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, NULL
, error
, NULL
, CFSTR("%@ alloc failed"), clientId
);
541 result
= kSOSCoderFailure
);
542 require_noerr_action_quiet(otrStatus
= SecOTRSSignAndProtectMessage(coder
->sessRef
, message
, encoded
), errOut
,
543 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ cannot protect message: %" PRIdOSStatus
), clientId
, otrStatus
);
544 CFReleaseNull(encoded
);
545 result
= kSOSCoderFailure
);
546 *codedMessage
= encoded
;
550 if (result
== kSOSCoderFailure
&& error
&& *error
)
551 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
552 secinfo("coder", "%@ %@ %s %@ %@ returned %s", clientId
, beginState
,
553 SecOTRPacketTypeString(encoded
), action
, coder
->sessRef
, SOSCoderString(result
));
554 CFReleaseSafe(beginState
);
560 bool SOSCoderCanWrap(SOSCoderRef coder
) {
561 return coder
->sessRef
&& SecOTRSGetIsReadyForMessages(coder
->sessRef
) && !coder
->waitingForDataPacket
;