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 <SecureObjectSync/SOSInternal.h>
33 #include <SecureObjectSync/SOSFullPeerInfo.h>
34 #include <SecureObjectSync/SOSPeerInfo.h>
35 #include <SecureObjectSync/SOSPeer.h>
36 #include <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 default: return "StatusUnknown";
71 static void logRawCoderMessage(const uint8_t* der, uint8_t* der_end, bool encoding)
74 CFStringRef hexMessage = NULL;
76 CFIndex length = der_end - der;
77 CFDataRef message = CFDataCreate(kCFAllocatorDefault, der, length);
78 hexMessage = CFDataCopyHexString(message);
79 secnoticeq("coder", "%s RAW [%ld] %@", encoding ? "encode" : "decode", length, hexMessage);
80 CFReleaseSafe(message);
82 CFReleaseSafe(hexMessage);
87 static size_t der_sizeof_bool(bool value
) {
88 return ccder_sizeof(CCDER_BOOLEAN
, 1);
91 static uint8_t* der_encode_bool(bool value
, const uint8_t *der
, uint8_t *der_end
) {
92 uint8_t valueByte
= value
;
93 return ccder_encode_tl(CCDER_BOOLEAN
, 1, der
,
94 ccder_encode_body(1, &valueByte
, der
, der_end
));
97 static const uint8_t* der_decode_bool(bool *value
, const uint8_t *der
, const uint8_t *der_end
) {
98 size_t payload_size
= 0;
100 der
= ccder_decode_tl(CCDER_BOOLEAN
, &payload_size
, der
, der_end
);
102 if (payload_size
!= 1) {
107 *value
= (*der
!= 0);
114 static CFMutableDataRef
sessSerialized(SOSCoderRef coder
, CFErrorRef
*error
) {
115 CFMutableDataRef otr_state
= NULL
;
117 if(!coder
|| !coder
->sessRef
) {
118 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType
, NULL
, error
, 0, CFSTR("No session reference."));
122 if ((otr_state
= CFDataCreateMutable(NULL
, 0)) == NULL
) {
123 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, NULL
, error
, 0, CFSTR("Mutable Data allocation failed."));
127 if (errSecSuccess
!= SecOTRSAppendSerialization(coder
->sessRef
, otr_state
)) {
128 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, NULL
, error
, 0, CFSTR("Append Serialization failed."));
129 CFReleaseSafe(otr_state
);
137 static size_t der_sizeof_optional_data(CFDataRef data
) {
138 return data
? der_sizeof_data(data
, NULL
) : 0;
141 static uint8_t* der_encode_optional_data(CFDataRef data
, CFErrorRef
*error
, const uint8_t* der
, uint8_t* der_end
) {
142 return data
? der_encode_data(data
, error
, der
, der_end
) : der_end
;
147 static size_t SOSCoderGetDEREncodedSize(SOSCoderRef coder
, CFErrorRef
*error
) {
148 size_t encoded_size
= 0;
149 CFMutableDataRef otr_state
= sessSerialized(coder
, error
);
152 size_t data_size
= der_sizeof_data(otr_state
, error
);
153 size_t waiting_size
= der_sizeof_bool(coder
->waitingForDataPacket
);
154 size_t pending_size
= der_sizeof_optional_data(coder
->pendingResponse
);
156 if ((data_size
!= 0) && (waiting_size
!= 0))
158 encoded_size
= ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, data_size
+ waiting_size
+ pending_size
);
160 CFReleaseSafe(otr_state
);
166 static uint8_t* SOSCoderEncodeToDER(SOSCoderRef coder
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
) {
167 if(!der_end
) return NULL
;
168 uint8_t* result
= NULL
;
169 CFMutableDataRef otr_state
= sessSerialized(coder
, error
);
172 result
= ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
173 der_encode_data(otr_state
, error
, der
,
174 der_encode_bool(coder
->waitingForDataPacket
, der
,
175 der_encode_optional_data(coder
->pendingResponse
, error
, der
, der_end
))));
176 CFReleaseSafe(otr_state
);
182 CFDataRef
SOSCoderCopyDER(SOSCoderRef coder
, CFErrorRef
* error
) {
183 CFMutableDataRef encoded
= NULL
;
184 size_t encoded_size
= SOSCoderGetDEREncodedSize(coder
, error
);
186 if (encoded_size
> 0) {
187 encoded
= CFDataCreateMutable(NULL
, encoded_size
);
189 CFDataSetLength(encoded
, encoded_size
);
190 uint8_t * der
= CFDataGetMutableBytePtr(encoded
);
191 uint8_t * der_end
= der
+ encoded_size
;
192 if (!SOSCoderEncodeToDER(coder
, error
, der
, der_end
)) {
193 CFReleaseNull(encoded
);
201 SOSCoderRef
SOSCoderCreateFromData(CFDataRef exportedData
, CFErrorRef
*error
) {
203 SOSCoderRef p
= calloc(1, sizeof(struct __OpaqueSOSCoder
));
205 const uint8_t *der
= CFDataGetBytePtr(exportedData
);
206 const uint8_t *der_end
= der
+ CFDataGetLength(exportedData
);
208 CFDataRef otr_data
= NULL
;
211 require(ccder_decode_tag(&tag
, der
, der_end
),fail
);
214 case CCDER_OCTET_STRING
: // TODO: this code is safe to delete?
216 der
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, der_end
);
217 p
->waitingForDataPacket
= false;
221 case CCDER_CONSTRUCTED_SEQUENCE
:
223 const uint8_t *sequence_end
= NULL
;
224 der
= ccder_decode_sequence_tl(&sequence_end
, der
, der_end
);
226 require_action_quiet(sequence_end
== der_end
, fail
, SecCFDERCreateError(kSOSErrorDecodeFailure
, CFSTR("Extra data in SOS coder"), NULL
, error
));
228 der
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, sequence_end
);
229 der
= der_decode_bool(&p
->waitingForDataPacket
, der
, sequence_end
);
230 if (der
!= sequence_end
) { // optionally a pending response
231 der
= der_decode_data(kCFAllocatorDefault
, 0, &p
->pendingResponse
, error
, der
, sequence_end
);
237 SecCFDERCreateError(kSOSErrorDecodeFailure
, CFSTR("Unsupported SOS Coder DER"), NULL
, error
);
243 p
->sessRef
= SecOTRSessionCreateFromData(NULL
, otr_data
);
244 require(p
->sessRef
, fail
);
246 CFReleaseSafe(otr_data
);
251 CFReleaseSafe(otr_data
);
256 SOSCoderRef
SOSCoderCreate(SOSPeerInfoRef peerInfo
, SOSFullPeerInfoRef myPeerInfo
, CFErrorRef
*error
) {
257 CFAllocatorRef allocator
= CFGetAllocator(peerInfo
);
259 SOSCoderRef coder
= calloc(1, sizeof(struct __OpaqueSOSCoder
));
260 CFErrorRef localError
= NULL
;
262 SecOTRFullIdentityRef myRef
= NULL
;
263 SecOTRPublicIdentityRef peerRef
= NULL
;
264 SecKeyRef privateKey
= NULL
;
265 SecKeyRef publicKey
= NULL
;
267 if (myPeerInfo
&& peerInfo
) {
268 privateKey
= SOSFullPeerInfoCopyDeviceKey(myPeerInfo
, &localError
);
269 require_quiet(privateKey
, errOut
);
271 myRef
= SecOTRFullIdentityCreateFromSecKeyRef(allocator
, privateKey
, &localError
);
272 require_quiet(myRef
, errOut
);
274 CFReleaseNull(privateKey
);
276 publicKey
= SOSPeerInfoCopyPubKey(peerInfo
);
278 peerRef
= SecOTRPublicIdentityCreateFromSecKeyRef(allocator
, publicKey
, &localError
);
279 require_quiet(peerRef
, errOut
);
281 coder
->sessRef
= SecOTRSessionCreateFromID(allocator
, myRef
, peerRef
);
283 require(coder
->sessRef
, errOut
);
285 coder
->waitingForDataPacket
= false;
286 coder
->pendingResponse
= NULL
;
288 CFReleaseNull(publicKey
);
289 CFReleaseNull(privateKey
);
290 CFReleaseNull(myRef
);
291 CFReleaseNull(peerRef
);
293 secnotice("coder", "NULL Coder requested, no transport security");
296 SOSCoderStart(coder
, NULL
);
301 secerror("Coder create failed: %@\n", localError
? localError
: (CFTypeRef
)CFSTR("No local error in SOSCoderCreate"));
302 secerror("Coder create failed: %@\n", error
? *error
: (CFTypeRef
)CFSTR("WTF NULL?"));
303 CFReleaseNull(myRef
);
304 CFReleaseNull(peerRef
);
305 CFReleaseNull(publicKey
);
306 CFReleaseNull(privateKey
);
312 void SOSCoderDispose(SOSCoderRef coder
)
315 CFReleaseNull(coder
->sessRef
);
316 CFReleaseNull(coder
->pendingResponse
);
317 CFReleaseNull(coder
->peer_id
);
323 void SOSCoderReset(SOSCoderRef coder
)
325 SecOTRSessionReset(coder
->sessRef
);
326 coder
->waitingForDataPacket
= false;
327 CFReleaseNull(coder
->pendingResponse
);
330 CFDataRef
SOSCoderCopyPendingResponse(SOSCoderRef coder
)
332 return CFRetainSafe(coder
->pendingResponse
);
335 void SOSCoderConsumeResponse(SOSCoderRef coder
)
337 CFReleaseNull(coder
->pendingResponse
);
340 static bool SOSOTRSAppendStartPacket(SecOTRSessionRef session
, CFMutableDataRef appendPacket
, CFErrorRef
*error
) {
341 OSStatus otrStatus
= SecOTRSAppendStartPacket(session
, appendPacket
);
342 if (otrStatus
!= errSecSuccess
) {
343 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("append start packet returned: %" PRIdOSStatus
), otrStatus
);
345 return otrStatus
== errSecSuccess
;
348 // Start OTR negotiation if we haven't already done so.
350 SOSCoderStart(SOSCoderRef coder
, CFErrorRef
*error
) {
351 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
352 CFStringRef beginState
= NULL
;
353 SOSCoderStatus result
= kSOSCoderFailure
;
354 CFMutableDataRef startPacket
= NULL
;
356 require_action_quiet(coder
->sessRef
, coderFailure
, CFStringAppend(action
, CFSTR("*** no otr session ***")));
357 beginState
= CFCopyDescription(coder
->sessRef
);
358 require_action_quiet(!coder
->waitingForDataPacket
, negotiatingOut
, CFStringAppend(action
, CFSTR("waiting for peer to send first data packet")));
359 require_action_quiet(!SecOTRSGetIsReadyForMessages(coder
->sessRef
), coderFailure
, CFStringAppend(action
, CFSTR("otr session ready"));
360 result
= kSOSCoderDataReturned
);
361 require_action_quiet(SecOTRSGetIsIdle(coder
->sessRef
), negotiatingOut
, CFStringAppend(action
, CFSTR("otr negotiating already")));
362 require_action_quiet(startPacket
= CFDataCreateMutable(kCFAllocatorDefault
, 0), coderFailure
, SOSCreateError(kSOSErrorAllocationFailure
, CFSTR("alloc failed"), NULL
, error
));
363 require_quiet(SOSOTRSAppendStartPacket(coder
->sessRef
, startPacket
, error
), coderFailure
);
364 CFRetainAssign(coder
->pendingResponse
, startPacket
);
367 result
= kSOSCoderNegotiating
;
370 if (result
== kSOSCoderFailure
&& error
&& *error
)
371 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
372 secnotice("coder", "%@ %s %@ %@ returned %s", beginState
,
373 SecOTRPacketTypeString(startPacket
), action
, coder
->sessRef
, SOSCoderString(result
));
374 CFReleaseNull(startPacket
);
375 CFReleaseSafe(beginState
);
383 SOSCoderResendDH(SOSCoderRef coder
, CFErrorRef
*error
) {
384 if(coder
->sessRef
== NULL
) return kSOSCoderDataReturned
;
385 CFMutableDataRef startPacket
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
386 SOSCoderStatus result
= kSOSCoderFailure
;
387 require_noerr_quiet(SecOTRSAppendRestartPacket(coder
->sessRef
, startPacket
), exit
);
388 secnotice("coder", "Resending OTR Start %@", startPacket
);
389 CFRetainAssign(coder
->pendingResponse
, startPacket
);
390 result
= kSOSCoderNegotiating
;
392 CFReleaseNull(startPacket
);
397 static SOSCoderStatus
nullCoder(CFDataRef from
, CFMutableDataRef
*to
) {
398 *to
= CFDataCreateMutableCopy(NULL
, CFDataGetLength(from
), from
);
399 return kSOSCoderDataReturned
;
402 SOSCoderStatus
SOSCoderUnwrap(SOSCoderRef coder
, CFDataRef codedMessage
, CFMutableDataRef
*message
,
403 CFStringRef clientId
, CFErrorRef
*error
) {
404 if(codedMessage
== NULL
) return kSOSCoderDataReturned
;
405 if(coder
->sessRef
== NULL
) return nullCoder(codedMessage
, message
);
406 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
407 /* This should be the "normal" case. We just use OTR to unwrap the received message. */
408 SOSCoderStatus result
= kSOSCoderFailure
;
410 CFStringRef beginState
= CFCopyDescription(coder
->sessRef
);
411 enum SecOTRSMessageKind kind
= SecOTRSGetMessageKind(coder
->sessRef
, codedMessage
);
414 case kOTRNegotiationPacket
: {
415 /* If we're in here we haven't completed negotiating a session. Use SecOTRSProcessPacket() to go through
416 the negotiation steps and immediately send a reply back if necessary using the sendBlock. This
417 assumes the sendBlock is still available.
419 CFMutableDataRef response
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
420 OSStatus ppstatus
= errSecSuccess
;
422 switch (ppstatus
= SecOTRSProcessPacket(coder
->sessRef
, codedMessage
, response
)) {
424 if (CFDataGetLength(response
) > 1) {
425 CFStringAppendFormat(action
, NULL
, CFSTR("Sending OTR Response %s"), SecOTRPacketTypeString(response
));
426 CFRetainAssign(coder
->pendingResponse
, response
);
427 result
= kSOSCoderNegotiating
;
428 if (SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
429 CFStringAppend(action
, CFSTR(" begin waiting for data packet"));
430 coder
->waitingForDataPacket
= true;
432 } else if(!SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
433 CFStringAppend(action
, CFSTR("stuck?"));
434 result
= kSOSCoderNegotiating
;
436 CFStringAppend(action
, CFSTR("completed negotiation"));
437 result
= kSOSCoderNegotiationCompleted
;
438 coder
->waitingForDataPacket
= false;
442 CFStringAppend(action
, CFSTR("resending dh"));
443 result
= SOSCoderResendDH(coder
, error
);
446 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ Cannot negotiate session (%ld)"), clientId
, (long)ppstatus
);
447 result
= kSOSCoderFailure
;
451 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ Cannot allocate CFData"), clientId
);
452 result
= kSOSCoderFailure
;
455 CFReleaseNull(response
);
461 if(!SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
462 CFStringAppend(action
, CFSTR("not ready, resending DH packet"));
463 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed
, 1);
464 CFStringAppend(action
, CFSTR("not ready for data; resending dh"));
465 result
= SOSCoderResendDH(coder
, error
);
467 if (coder
->waitingForDataPacket
) {
468 CFStringAppend(action
, CFSTR("got data packet we were waiting for "));
469 coder
->waitingForDataPacket
= false;
471 CFMutableDataRef exposed
= CFDataCreateMutable(0, 0);
472 OSStatus otrResult
= SecOTRSVerifyAndExposeMessage(coder
->sessRef
, codedMessage
, exposed
);
473 CFStringAppend(action
, CFSTR("verify and expose message"));
475 if (otrResult
== errSecOTRTooOld
) {
476 CFStringAppend(action
, CFSTR(" too old"));
477 result
= kSOSCoderStaleEvent
;
479 SecError(otrResult
, error
, CFSTR("%@ Cannot expose message: %" PRIdOSStatus
), clientId
, otrResult
);
480 secerror("%@ Decode OTR Protected Packet: %@", clientId
, error
? *error
: NULL
);
481 result
= kSOSCoderFailure
;
484 CFStringAppend(action
, CFSTR("decoded OTR protected packet"));
487 result
= kSOSCoderDataReturned
;
489 CFReleaseNull(exposed
);
494 secerror("%@ Unknown packet type: %@", clientId
, codedMessage
);
495 SOSCreateError(kSOSErrorDecodeFailure
, CFSTR("Unknown packet type"), (error
!= NULL
) ? *error
: NULL
, error
);
496 result
= kSOSCoderFailure
;
501 if (result
== kSOSCoderFailure
&& error
&& *error
)
502 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
503 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId
, beginState
,
504 SecOTRPacketTypeString(codedMessage
), action
, coder
->sessRef
, SOSCoderString(result
));
505 CFReleaseSafe(beginState
);
512 SOSCoderStatus
SOSCoderWrap(SOSCoderRef coder
, CFDataRef message
, CFMutableDataRef
*codedMessage
, CFStringRef clientId
, CFErrorRef
*error
) {
513 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
514 SOSCoderStatus result
= kSOSCoderDataReturned
;
515 CFStringRef beginState
= NULL
;
516 CFMutableDataRef encoded
= NULL
;
517 OSStatus otrStatus
= 0;
519 require_action_quiet(coder
->sessRef
, errOut
,
520 CFStringAppend(action
, CFSTR("*** using null coder ***"));
521 result
= nullCoder(message
, codedMessage
));
522 beginState
= CFCopyDescription(coder
->sessRef
);
523 require_action_quiet(SecOTRSGetIsReadyForMessages(coder
->sessRef
), errOut
,
524 CFStringAppend(action
, CFSTR("not ready"));
525 result
= kSOSCoderNegotiating
);
526 require_action_quiet(!coder
->waitingForDataPacket
, errOut
,
527 CFStringAppend(action
, CFSTR("waiting for peer to send data packet first"));
528 result
= kSOSCoderNegotiating
);
529 require_action_quiet(encoded
= CFDataCreateMutable(kCFAllocatorDefault
, 0), errOut
,
530 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, NULL
, error
, NULL
, CFSTR("%@ alloc failed"), clientId
);
531 result
= kSOSCoderFailure
);
532 require_noerr_action_quiet(otrStatus
= SecOTRSSignAndProtectMessage(coder
->sessRef
, message
, encoded
), errOut
,
533 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ cannot protect message: %" PRIdOSStatus
), clientId
, otrStatus
);
534 CFReleaseNull(encoded
);
535 result
= kSOSCoderFailure
);
536 *codedMessage
= encoded
;
540 if (result
== kSOSCoderFailure
&& error
&& *error
)
541 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
542 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId
, beginState
,
543 SecOTRPacketTypeString(encoded
), action
, coder
->sessRef
, SOSCoderString(result
));
544 CFReleaseSafe(beginState
);
550 bool SOSCoderCanWrap(SOSCoderRef coder
) {
551 return coder
->sessRef
&& SecOTRSGetIsReadyForMessages(coder
->sessRef
) && !coder
->waitingForDataPacket
;