5 // Created by Richard Murphy on 2/6/13.
10 #include <CoreFoundation/CFBase.h>
11 #include <CoreFoundation/CFError.h>
13 #include <Security/SecBasePriv.h>
14 #include <Security/SecOTR.h>
15 #include <Security/SecOTRSession.h>
16 #include <SecureObjectSync/SOSInternal.h>
17 #include <SecureObjectSync/SOSFullPeerInfo.h>
18 #include <SecureObjectSync/SOSPeerInfo.h>
19 #include <SecureObjectSync/SOSPeer.h>
20 #include <SecureObjectSync/SOSCoder.h>
22 #include <utilities/SecCFRelease.h>
23 #include <utilities/SecCFWrappers.h>
24 #include <utilities/SecIOFormat.h>
25 #include <utilities/SecCFError.h>
26 #include <utilities/debugging.h>
28 #include <utilities/der_plist.h>
29 #include <utilities/der_plist_internal.h>
31 #include <corecrypto/ccder.h>
32 #include <utilities/iCloudKeychainTrace.h>
34 #include "AssertMacros.h"
36 struct __OpaqueSOSCoder
{
38 SecOTRSessionRef sessRef
;
39 bool waitingForDataPacket
;
42 static const char *SOSPeerCoderString(SOSPeerCoderStatus coderStatus
) {
43 switch (coderStatus
) {
44 case kSOSPeerCoderDataReturned
: return "DataReturned";
45 case kSOSPeerCoderNegotiating
: return "Negotiating";
46 case kSOSPeerCoderNegotiationCompleted
: return "NegotiationCompleted";
47 case kSOSPeerCoderFailure
: return "Failure";
48 case kSOSPeerCoderStaleEvent
: return "StaleEvent";
49 default: return "StatusUnknown";
53 static size_t der_sizeof_bool(bool value
) {
54 return ccder_sizeof(CCDER_BOOLEAN
, 1);
57 static uint8_t* der_encode_bool(bool value
, const uint8_t *der
, uint8_t *der_end
) {
58 uint8_t valueByte
= value
;
59 return ccder_encode_tl(CCDER_BOOLEAN
, 1, der
,
60 ccder_encode_body(1, &valueByte
, der
, der_end
));
63 static const uint8_t* der_decode_bool(bool *value
, const uint8_t *der
, const uint8_t *der_end
) {
64 size_t payload_size
= 0;
66 der
= ccder_decode_tl(CCDER_BOOLEAN
, &payload_size
, der
, der_end
);
68 if (payload_size
!= 1) {
80 static CFMutableDataRef
sessSerialized(SOSCoderRef coder
, CFErrorRef
*error
) {
81 CFMutableDataRef otr_state
= NULL
;
83 if(!coder
|| !coder
->sessRef
) {
84 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType
, NULL
, error
, 0, CFSTR("No session reference."));
88 if ((otr_state
= CFDataCreateMutable(NULL
, 0)) == NULL
) {
89 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, NULL
, error
, 0, CFSTR("Mutable Data allocation failed."));
93 if (errSecSuccess
!= SecOTRSAppendSerialization(coder
->sessRef
, otr_state
)) {
94 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, NULL
, error
, 0, CFSTR("Append Serialization failed."));
95 CFReleaseSafe(otr_state
);
103 static size_t SOSCoderGetDEREncodedSize(SOSCoderRef coder
, CFErrorRef
*error
) {
104 size_t encoded_size
= 0;
105 CFMutableDataRef otr_state
= sessSerialized(coder
, error
);
108 size_t data_size
= der_sizeof_data(otr_state
, error
);
109 size_t waiting_size
= der_sizeof_bool(coder
->waitingForDataPacket
);
111 if ((data_size
!= 0) && (waiting_size
!= 0))
113 encoded_size
= ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, data_size
+ waiting_size
);
115 CFReleaseSafe(otr_state
);
120 static uint8_t* SOSCoderEncodeToDER(SOSCoderRef coder
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
) {
121 if(!der_end
) return NULL
;
122 uint8_t* result
= NULL
;
123 CFMutableDataRef otr_state
= sessSerialized(coder
, error
);
126 result
= ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
127 der_encode_data(otr_state
, error
, der
,
128 der_encode_bool(coder
->waitingForDataPacket
, der
, der_end
)));
129 CFReleaseSafe(otr_state
);
135 CFDataRef
SOSCoderCopyDER(SOSCoderRef coder
, CFErrorRef
* error
) {
136 CFMutableDataRef encoded
= NULL
;
137 size_t encoded_size
= SOSCoderGetDEREncodedSize(coder
, error
);
139 if (encoded_size
> 0) {
140 encoded
= CFDataCreateMutable(NULL
, encoded_size
);
142 CFDataSetLength(encoded
, encoded_size
);
143 uint8_t * der
= CFDataGetMutableBytePtr(encoded
);
144 uint8_t * der_end
= der
+ encoded_size
;
145 if (!SOSCoderEncodeToDER(coder
, error
, der
, der_end
)) {
146 CFReleaseNull(encoded
);
154 SOSCoderRef
SOSCoderCreateFromData(CFDataRef exportedData
, CFErrorRef
*error
) {
156 SOSCoderRef p
= calloc(1, sizeof(struct __OpaqueSOSCoder
));
158 const uint8_t *der
= CFDataGetBytePtr(exportedData
);
159 const uint8_t *der_end
= der
+ CFDataGetLength(exportedData
);
161 CFDataRef otr_data
= NULL
;
164 require(ccder_decode_tag(&tag
, der
, der_end
),fail
);
167 case CCDER_OCTET_STRING
:
169 der
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, der_end
);
170 p
->waitingForDataPacket
= false;
174 case CCDER_CONSTRUCTED_SEQUENCE
:
176 const uint8_t *sequence_end
= NULL
;
177 der
= ccder_decode_sequence_tl(&sequence_end
, der
, der_end
);
179 require_action_quiet(sequence_end
== der_end
, fail
, SecCFDERCreateError(kSOSErrorDecodeFailure
, CFSTR("Extra data in SOS coder"), NULL
, error
));
181 der
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, der_end
);
182 der
= der_decode_bool(&p
->waitingForDataPacket
, der
, sequence_end
);
187 SecCFDERCreateError(kSOSErrorDecodeFailure
, CFSTR("Unsupported SOS Coder DER"), NULL
, error
);
193 p
->sessRef
= SecOTRSessionCreateFromData(NULL
, otr_data
);
194 require(p
->sessRef
, fail
);
196 CFReleaseSafe(otr_data
);
201 CFReleaseSafe(otr_data
);
206 SOSCoderRef
SOSCoderCreate(SOSPeerInfoRef peerInfo
, SOSFullPeerInfoRef myPeerInfo
, CFErrorRef
*error
) {
207 CFAllocatorRef allocator
= CFGetAllocator(peerInfo
);
209 SOSCoderRef p
= calloc(1, sizeof(struct __OpaqueSOSCoder
));
211 SecOTRFullIdentityRef myRef
= NULL
;
212 SecOTRPublicIdentityRef peerRef
= NULL
;
213 SecKeyRef privateKey
= NULL
;
214 SecKeyRef publicKey
= NULL
;
216 if (myPeerInfo
&& peerInfo
) {
217 privateKey
= SOSFullPeerInfoCopyDeviceKey(myPeerInfo
, error
);
218 require_quiet(privateKey
, errOut
);
220 myRef
= SecOTRFullIdentityCreateFromSecKeyRef(allocator
, privateKey
, error
);
221 require_quiet(myRef
, errOut
);
223 CFReleaseNull(privateKey
);
225 publicKey
= SOSPeerInfoCopyPubKey(peerInfo
);
227 peerRef
= SecOTRPublicIdentityCreateFromSecKeyRef(allocator
, publicKey
, error
);
228 require_quiet(peerRef
, errOut
);
230 p
->sessRef
= SecOTRSessionCreateFromID(allocator
, myRef
, peerRef
);
232 require(p
->sessRef
, errOut
);
234 p
->waitingForDataPacket
= false;
236 CFReleaseNull(publicKey
);
237 CFReleaseNull(privateKey
);
238 CFReleaseNull(myRef
);
239 CFReleaseNull(peerRef
);
241 secnotice("coder", "NULL Coder requested, no transport security");
247 secerror("Coder create failed: %@\n", *error
);
248 CFReleaseNull(myRef
);
249 CFReleaseNull(peerRef
);
250 CFReleaseNull(publicKey
);
251 CFReleaseNull(privateKey
);
257 void SOSCoderDispose(SOSCoderRef coder
)
259 CFReleaseNull(coder
->sessRef
);
264 void SOSCoderReset(SOSCoderRef coder
)
266 SecOTRSessionReset(coder
->sessRef
);
267 coder
->waitingForDataPacket
= false;
270 static bool SOSOTRSAppendStartPacket(SecOTRSessionRef session
, CFMutableDataRef appendPacket
, CFErrorRef
*error
) {
271 OSStatus otrStatus
= SecOTRSAppendStartPacket(session
, appendPacket
);
272 if (otrStatus
!= errSecSuccess
) {
273 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("append start packet returned: %" PRIdOSStatus
), otrStatus
);
275 return otrStatus
== errSecSuccess
;
278 // Start OTR negotiation if we haven't already done so.
280 SOSCoderStart(SOSCoderRef coder
, SOSPeerSendBlock sendBlock
, CFStringRef clientId
, CFErrorRef
*error
) {
281 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
282 CFStringRef beginState
= NULL
;
283 SOSPeerCoderStatus result
= kSOSPeerCoderFailure
;
284 CFMutableDataRef startPacket
= NULL
;
286 require_action_quiet(coder
->sessRef
, coderFailure
, CFStringAppend(action
, CFSTR("*** no otr session ***")));
287 beginState
= CFCopyDescription(coder
->sessRef
);
288 require_action_quiet(!coder
->waitingForDataPacket
, negotiatingOut
, CFStringAppend(action
, CFSTR("waiting for peer to send first data packet")));
289 require_action_quiet(!SecOTRSGetIsReadyForMessages(coder
->sessRef
), coderFailure
, CFStringAppend(action
, CFSTR("otr session ready"));
290 result
= kSOSPeerCoderDataReturned
);
291 require_action_quiet(SecOTRSGetIsIdle(coder
->sessRef
), negotiatingOut
, CFStringAppend(action
, CFSTR("otr negotiating already")));
292 require_action_quiet(startPacket
= CFDataCreateMutable(kCFAllocatorDefault
, 0), coderFailure
, SOSCreateError(kSOSErrorAllocationFailure
, CFSTR("alloc failed"), NULL
, error
));
293 require_quiet(SOSOTRSAppendStartPacket(coder
->sessRef
, startPacket
, error
), coderFailure
);
294 require_quiet(sendBlock(startPacket
, error
), coderFailure
);
297 result
= kSOSPeerCoderNegotiating
;
300 if (result
== kSOSPeerCoderFailure
&& error
&& *error
)
301 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
302 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId
, beginState
,
303 SecOTRPacketTypeString(startPacket
), action
, coder
->sessRef
, SOSPeerCoderString(result
));
304 CFReleaseNull(startPacket
);
305 CFReleaseSafe(beginState
);
313 SOSCoderResendDH(SOSCoderRef coder
, SOSPeerSendBlock sendBlock
, CFErrorRef
*error
) {
314 if(coder
->sessRef
== NULL
) return kSOSPeerCoderDataReturned
;
316 CFMutableDataRef startPacket
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
317 if (SecOTRSAppendRestartPacket(coder
->sessRef
, startPacket
)) {
318 return kSOSPeerCoderFailure
;
321 secnotice("coder", "Resending OTR Start %@", startPacket
);
322 SOSPeerCoderStatus result
= sendBlock(startPacket
, error
) ? kSOSPeerCoderNegotiating
: kSOSPeerCoderFailure
;
323 CFReleaseNull(startPacket
);
328 static SOSPeerCoderStatus
nullCoder(CFDataRef from
, CFMutableDataRef
*to
) {
329 *to
= CFDataCreateMutableCopy(NULL
, CFDataGetLength(from
), from
);
330 return kSOSPeerCoderDataReturned
;
333 SOSPeerCoderStatus
SOSCoderUnwrap(SOSCoderRef coder
, SOSPeerSendBlock send_block
,
334 CFDataRef codedMessage
, CFMutableDataRef
*message
,
335 CFStringRef clientId
,
337 if(codedMessage
== NULL
) return kSOSPeerCoderDataReturned
;
338 if(coder
->sessRef
== NULL
) return nullCoder(codedMessage
, message
);
339 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
340 /* This should be the "normal" case. We just use OTR to unwrap the received message. */
341 SOSPeerCoderStatus result
= kSOSPeerCoderFailure
;
343 CFStringRef beginState
= CFCopyDescription(coder
->sessRef
);
344 enum SecOTRSMessageKind kind
= SecOTRSGetMessageKind(coder
->sessRef
, codedMessage
);
347 case kOTRNegotiationPacket
: {
349 /* If we're in here we haven't completed negotiating a session. Use SecOTRSProcessPacket() to go through
350 the negotiation steps and immediately send a reply back if necessary using the sendBlock. This
351 assumes the sendBlock is still available.
353 CFMutableDataRef response
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
354 OSStatus ppstatus
= errSecSuccess
;
356 switch (ppstatus
= SecOTRSProcessPacket(coder
->sessRef
, codedMessage
, response
)) {
358 if (CFDataGetLength(response
) > 1) {
359 CFStringAppendFormat(action
, NULL
, CFSTR("Sending OTR Response %s"), SecOTRPacketTypeString(response
));
360 if (send_block(response
, error
)) {
361 result
= kSOSPeerCoderNegotiating
;
362 if (SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
363 CFStringAppend(action
, CFSTR(" begin waiting for data packet"));
364 coder
->waitingForDataPacket
= true;
367 secerror("%@ Coder send Error %@", clientId
, (CFTypeRef
)error
);
368 result
= kSOSPeerCoderFailure
;
370 } else if(!SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
371 CFStringAppend(action
, CFSTR("stuck?"));
372 result
= kSOSPeerCoderNegotiating
;
374 CFStringAppend(action
, CFSTR("completed negotiation"));
375 result
= kSOSPeerCoderNegotiationCompleted
;
376 coder
->waitingForDataPacket
= false;
380 CFStringAppend(action
, CFSTR("resending dh"));
381 result
= SOSCoderResendDH(coder
, send_block
, error
);
384 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ Cannot negotiate session (%ld)"), clientId
, (long)ppstatus
);
385 result
= kSOSPeerCoderFailure
;
389 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ Cannot allocate CFData"), clientId
);
390 result
= kSOSPeerCoderFailure
;
393 CFReleaseNull(response
);
395 secerror("%@ Can't send, no send_block!!", clientId
);
396 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ Cannot negotiate session"), clientId
);
397 result
= kSOSPeerCoderFailure
;
404 if(!SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
405 CFStringAppend(action
, CFSTR("not ready, resending DH packet"));
406 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed
, 1);
407 CFStringAppend(action
, CFSTR("not ready for data; resending dh"));
408 result
= SOSCoderResendDH(coder
, send_block
, error
);
410 if (coder
->waitingForDataPacket
) {
411 CFStringAppend(action
, CFSTR("got data packet we were waiting for "));
412 coder
->waitingForDataPacket
= false;
414 CFMutableDataRef exposed
= CFDataCreateMutable(0, 0);
415 OSStatus otrResult
= SecOTRSVerifyAndExposeMessage(coder
->sessRef
, codedMessage
, exposed
);
416 CFStringAppend(action
, CFSTR("verify and expose message"));
418 if (otrResult
== errSecOTRTooOld
) {
419 CFStringAppend(action
, CFSTR(" too old"));
420 result
= kSOSPeerCoderStaleEvent
;
422 SecError(otrResult
, error
, CFSTR("%@ Cannot expose message: %" PRIdOSStatus
), clientId
, otrResult
);
423 secerror("%@ Decode OTR Protected Packet: %@", clientId
, error
? *error
: NULL
);
424 result
= kSOSPeerCoderFailure
;
427 CFStringAppend(action
, CFSTR("decoded OTR protected packet"));
430 result
= kSOSPeerCoderDataReturned
;
432 CFReleaseNull(exposed
);
437 secerror("%@ Unknown packet type: %@", clientId
, codedMessage
);
438 SOSCreateError(kSOSErrorDecodeFailure
, CFSTR("Unknown packet type"), (error
!= NULL
) ? *error
: NULL
, error
);
439 result
= kSOSPeerCoderFailure
;
444 if (result
== kSOSPeerCoderFailure
&& error
&& *error
)
445 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
446 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId
, beginState
,
447 SecOTRPacketTypeString(codedMessage
), action
, coder
->sessRef
, SOSPeerCoderString(result
));
448 CFReleaseSafe(beginState
);
455 SOSPeerCoderStatus
SOSCoderWrap(SOSCoderRef coder
, CFDataRef message
, CFMutableDataRef
*codedMessage
, CFStringRef clientId
, CFErrorRef
*error
) {
456 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
457 SOSPeerCoderStatus result
= kSOSPeerCoderDataReturned
;
458 CFStringRef beginState
= NULL
;
459 CFMutableDataRef encoded
= NULL
;
460 OSStatus otrStatus
= 0;
462 require_action_quiet(coder
->sessRef
, errOut
,
463 CFStringAppend(action
, CFSTR("*** using null coder ***"));
464 result
= nullCoder(message
, codedMessage
));
465 beginState
= CFCopyDescription(coder
->sessRef
);
466 require_action_quiet(SecOTRSGetIsReadyForMessages(coder
->sessRef
), errOut
,
467 CFStringAppend(action
, CFSTR("not ready"));
468 result
= kSOSPeerCoderNegotiating
);
469 require_action_quiet(!coder
->waitingForDataPacket
, errOut
,
470 CFStringAppend(action
, CFSTR("waiting for peer to send data packet first"));
471 result
= kSOSPeerCoderNegotiating
);
472 require_action_quiet(encoded
= CFDataCreateMutable(kCFAllocatorDefault
, 0), errOut
,
473 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, NULL
, error
, NULL
, CFSTR("%@ alloc failed"), clientId
);
474 result
= kSOSPeerCoderFailure
);
475 require_noerr_action_quiet(otrStatus
= SecOTRSSignAndProtectMessage(coder
->sessRef
, message
, encoded
), errOut
,
476 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ cannot protect message: %" PRIdOSStatus
), clientId
, otrStatus
);
477 CFReleaseNull(encoded
);
478 result
= kSOSPeerCoderFailure
);
479 *codedMessage
= encoded
;
483 if (result
== kSOSPeerCoderFailure
&& error
&& *error
)
484 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
485 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId
, beginState
,
486 SecOTRPacketTypeString(encoded
), action
, coder
->sessRef
, SOSPeerCoderString(result
));
487 CFReleaseSafe(beginState
);
493 bool SOSCoderCanWrap(SOSCoderRef coder
) {
494 return coder
->sessRef
&& SecOTRSGetIsReadyForMessages(coder
->sessRef
) && !coder
->waitingForDataPacket
;