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/SecCoreCrypto.h>
43 #include <utilities/debugging.h>
45 #include <utilities/der_plist.h>
46 #include <utilities/der_plist_internal.h>
48 #include <corecrypto/ccder.h>
49 #include <utilities/iCloudKeychainTrace.h>
51 #include "AssertMacros.h"
53 struct __OpaqueSOSCoder
{
57 SecOTRSessionRef sessRef
;
58 bool waitingForDataPacket
;
59 CFDataRef pendingResponse
;
61 CFDataRef hashOfLastReceived
;
62 bool lastReceivedWasOld
;
65 #define lastReceived_di ccsha1_di
67 CFGiblisWithCompareFor(SOSCoder
)
69 static CFStringRef
SOSCoderCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
70 SOSCoderRef coder
= (SOSCoderRef
)cf
;
72 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<Coder %@ %@ %@ %s%s>"),
75 coder
->hashOfLastReceived
,
76 coder
->waitingForDataPacket
? "W" : "w",
77 coder
->lastReceivedWasOld
? "O" : "o"
85 static Boolean
SOSCoderCompare(CFTypeRef cfA
, CFTypeRef cfB
) {
86 SOSCoderRef coderA
= (SOSCoderRef
)cfA
, coderB
= (SOSCoderRef
)cfB
;
87 // Use mainly to see if peerB is actually this device (peerA)
88 return CFStringCompare(coderA
->peer_id
, coderB
->peer_id
, 0) == kCFCompareEqualTo
;
92 static const char *SOSCoderString(SOSCoderStatus coderStatus
) {
93 switch (coderStatus
) {
94 case kSOSCoderDataReturned
: return "DataReturned";
95 case kSOSCoderNegotiating
: return "Negotiating";
96 case kSOSCoderNegotiationCompleted
: return "NegotiationCompleted";
97 case kSOSCoderFailure
: return "Failure";
98 case kSOSCoderStaleEvent
: return "StaleEvent";
99 case kSOSCoderTooNew
: return "TooNew";
100 default: return "StatusUnknown";
104 CFStringRef
SOSCoderGetID(SOSCoderRef coder
) {
105 return coder
->peer_id
;
109 static void logRawCoderMessage(const uint8_t* der, uint8_t* der_end, bool encoding)
112 CFStringRef hexMessage = NULL;
113 if (der && der_end) {
114 CFIndex length = der_end - der;
115 CFDataRef message = CFDataCreate(kCFAllocatorDefault, der, length);
116 hexMessage = CFDataCopyHexString(message);
117 secnoticeq("coder", "%s RAW [%ld] %@", encoding ? "encode" : "decode", length, hexMessage);
118 CFReleaseSafe(message);
120 CFReleaseSafe(hexMessage);
125 static CFMutableDataRef
sessSerialized(SOSCoderRef coder
, CFErrorRef
*error
) {
126 CFMutableDataRef otr_state
= NULL
;
128 if(!coder
|| !coder
->sessRef
) {
129 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType
, NULL
, error
, 0, CFSTR("No session reference."));
133 if ((otr_state
= CFDataCreateMutable(NULL
, 0)) == NULL
) {
134 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, NULL
, error
, 0, CFSTR("Mutable Data allocation failed."));
138 if (errSecSuccess
!= SecOTRSAppendSerialization(coder
->sessRef
, otr_state
)) {
139 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, NULL
, error
, 0, CFSTR("Append Serialization failed."));
140 CFReleaseSafe(otr_state
);
148 static size_t der_sizeof_optional_data(CFDataRef data
) {
149 return data
? der_sizeof_data(data
, NULL
) : 0;
152 static uint8_t* der_encode_optional_data(CFDataRef data
, CFErrorRef
*error
, const uint8_t* der
, uint8_t* der_end
) {
153 return data
? der_encode_data(data
, error
, der
, der_end
) : der_end
;
158 static size_t SOSCoderGetDEREncodedSize(SOSCoderRef coder
, CFErrorRef
*error
) {
159 size_t encoded_size
= 0;
160 CFMutableDataRef otr_state
= sessSerialized(coder
, error
);
163 size_t data_size
= der_sizeof_data(otr_state
, error
);
164 size_t waiting_size
= ccder_sizeof_bool(coder
->waitingForDataPacket
, error
);
165 size_t pending_size
= der_sizeof_optional_data(coder
->pendingResponse
);
167 if ((data_size
!= 0) && (waiting_size
!= 0))
169 encoded_size
= ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, data_size
+ waiting_size
+ pending_size
);
171 CFReleaseSafe(otr_state
);
177 static uint8_t* SOSCoderEncodeToDER(SOSCoderRef coder
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
) {
178 if(!der_end
) return NULL
;
179 uint8_t* result
= NULL
;
180 CFMutableDataRef otr_state
= sessSerialized(coder
, error
);
183 result
= ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
184 der_encode_data(otr_state
, error
, der
,
185 ccder_encode_bool(coder
->waitingForDataPacket
, der
,
186 der_encode_optional_data(coder
->pendingResponse
, error
, der
, der_end
))));
187 CFReleaseSafe(otr_state
);
193 CFDataRef
SOSCoderCopyDER(SOSCoderRef coder
, CFErrorRef
* error
) {
194 CFMutableDataRef encoded
= NULL
;
195 size_t encoded_size
= SOSCoderGetDEREncodedSize(coder
, error
);
197 if (encoded_size
> 0) {
198 encoded
= CFDataCreateMutable(NULL
, encoded_size
);
200 CFDataSetLength(encoded
, encoded_size
);
201 uint8_t * der
= CFDataGetMutableBytePtr(encoded
);
202 uint8_t * der_end
= der
+ encoded_size
;
203 if (!SOSCoderEncodeToDER(coder
, error
, der
, der_end
)) {
204 CFReleaseNull(encoded
);
212 static SOSCoderRef
SOSCoderCreate_internal() {
213 SOSCoderRef p
= CFTypeAllocate(SOSCoder
, struct __OpaqueSOSCoder
, kCFAllocatorDefault
);
217 p
->pendingResponse
= NULL
;
218 p
->waitingForDataPacket
= false;
220 p
->hashOfLastReceived
= NULL
;
221 p
->lastReceivedWasOld
= false;
227 // 0 - Type not understood
228 // 1 - OCTET_STRING, just stored the data for OTR
229 // 2 - SEQUENCE with no version value
230 // 3 - SEQUENCE with version value we pull out of the CCDER_INTEGER
232 typedef enum coderExportFormatVersion
{
234 kCoderAsOTRDataOnly
= 1,
235 kCoderAsSequence
= 2,
236 kCoderAsVersionedSequence
= 3,
238 kCurrentCoderExportVersion
= kCoderAsVersionedSequence
239 } CoderExportFormatVersion
;
241 static uint64_t SOSCoderGetExportedVersion(const uint8_t *der
, const uint8_t *der_end
) {
243 uint64_t result
= kNotUnderstood
;
244 require(ccder_decode_tag(&tag
, der
, der_end
),xit
);
246 case CCDER_OCTET_STRING
: // TODO: this code is safe to delete?
247 result
= kCoderAsOTRDataOnly
;
250 case CCDER_CONSTRUCTED_SEQUENCE
:
252 const uint8_t *sequence_end
= NULL
;
253 der
= ccder_decode_sequence_tl(&sequence_end
, der
, der_end
);
254 ccder_tag firstSequenceTag
;
255 require(ccder_decode_tag(&firstSequenceTag
, der
, der_end
),xit
);
257 switch (firstSequenceTag
) {
258 case CCDER_OCTET_STRING
:
259 result
= kCoderAsSequence
;
262 der
= ccder_decode_uint64(NULL
, der
, sequence_end
);
264 result
= kNotUnderstood
;
266 result
= kCoderAsVersionedSequence
;
277 SOSCoderRef
SOSCoderCreateFromData(CFDataRef exportedData
, CFErrorRef
*error
) {
278 // TODO: fill in errors for all failure cases
279 //require_action_quiet(coder, xit, SOSCreateError(kSOSErrorSendFailure, CFSTR("No coder for peer"), NULL, error));
281 SOSCoderRef p
= SOSCoderCreate_internal();
283 const uint8_t *der
= CFDataGetBytePtr(exportedData
);
284 const uint8_t *der_end
= der
+ CFDataGetLength(exportedData
);
286 CFDataRef otr_data
= NULL
;
288 switch (SOSCoderGetExportedVersion(der
, der_end
)) {
289 case kCoderAsOTRDataOnly
:
290 der
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, der_end
);
291 p
->waitingForDataPacket
= false;
294 case kCoderAsSequence
:
296 const uint8_t *sequence_end
= NULL
;
297 der
= ccder_decode_sequence_tl(&sequence_end
, der
, der_end
);
299 require_action_quiet(sequence_end
== der_end
, fail
, SecCFDERCreateError(kSOSErrorDecodeFailure
, CFSTR("Extra data in SOS coder"), NULL
, error
));
301 der
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, sequence_end
);
302 der
= ccder_decode_bool(&p
->waitingForDataPacket
, der
, sequence_end
);
303 if (der
!= sequence_end
) { // optionally a pending response
304 der
= der_decode_data(kCFAllocatorDefault
, 0, &p
->pendingResponse
, error
, der
, sequence_end
);
309 case kCoderAsVersionedSequence
:
311 const uint8_t *sequence_end
= NULL
;
312 der
= ccder_decode_sequence_tl(&sequence_end
, der
, der_end
);
314 require_action_quiet(sequence_end
== der_end
, fail
, SecCFDERCreateError(kSOSErrorDecodeFailure
, CFSTR("Extra data in SOS coder"), NULL
, error
));
317 der
= ccder_decode_uint64(&version
, der
, sequence_end
);
318 if (version
!= kCoderAsVersionedSequence
) {
319 SOSErrorCreate(kSOSErrorDecodeFailure
, error
, NULL
, CFSTR("Unsupported Sequence Version: %lld"), version
);
323 der
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, sequence_end
);
324 der
= ccder_decode_bool(&p
->waitingForDataPacket
, der
, sequence_end
);
325 der
= ccder_decode_bool(&p
->lastReceivedWasOld
, der
, sequence_end
);
326 der
= der_decode_data(kCFAllocatorDefault
, 0, &p
->hashOfLastReceived
, error
, der
, sequence_end
);
327 if (der
!= sequence_end
) { // optionally a pending response
328 der
= der_decode_data(kCFAllocatorDefault
, 0, &p
->pendingResponse
, error
, der
, sequence_end
);
334 SOSErrorCreate(kSOSErrorDecodeFailure
, error
, NULL
, CFSTR("Unsupported SOS Coder DER"));
340 p
->sessRef
= SecOTRSessionCreateFromData(NULL
, otr_data
);
341 require(p
->sessRef
, fail
);
343 if (p
->hashOfLastReceived
== NULL
)
344 p
->hashOfLastReceived
= CFDataCreateMutableWithScratch(kCFAllocatorDefault
, lastReceived_di()->output_size
);
346 CFReleaseSafe(otr_data
);
351 CFReleaseSafe(otr_data
);
356 SOSCoderRef
SOSCoderCreate(SOSPeerInfoRef peerInfo
, SOSFullPeerInfoRef myPeerInfo
, CFBooleanRef useCompact
, CFErrorRef
*error
) {
357 CFAllocatorRef allocator
= CFGetAllocator(peerInfo
);
359 SOSCoderRef coder
= SOSCoderCreate_internal();
361 CFErrorRef localError
= NULL
;
363 SecOTRFullIdentityRef myRef
= NULL
;
364 SecOTRPublicIdentityRef peerRef
= NULL
;
365 SecKeyRef privateKey
= NULL
;
366 SecKeyRef publicKey
= NULL
;
368 if (myPeerInfo
&& peerInfo
) {
369 privateKey
= SOSFullPeerInfoCopyDeviceKey(myPeerInfo
, &localError
);
370 require_quiet(privateKey
, errOut
);
372 myRef
= SecOTRFullIdentityCreateFromSecKeyRef(allocator
, privateKey
, &localError
);
373 require_quiet(myRef
, errOut
);
375 CFReleaseNull(privateKey
);
377 publicKey
= SOSPeerInfoCopyPubKey(peerInfo
, &localError
);
378 require(publicKey
, errOut
);
380 peerRef
= SecOTRPublicIdentityCreateFromSecKeyRef(allocator
, publicKey
, &localError
);
381 require_quiet(peerRef
, errOut
);
383 if(useCompact
== kCFBooleanTrue
)
384 coder
->sessRef
= SecOTRSessionCreateFromIDAndFlags(allocator
, myRef
, peerRef
, kSecOTRUseAppleCustomMessageFormat
);
387 coder
->sessRef
= SecOTRSessionCreateFromID(allocator
, myRef
, peerRef
);
389 require(coder
->sessRef
, errOut
);
391 coder
->waitingForDataPacket
= false;
392 coder
->pendingResponse
= NULL
;
394 CFReleaseNull(publicKey
);
395 CFReleaseNull(privateKey
);
396 CFReleaseNull(myRef
);
397 CFReleaseNull(peerRef
);
399 secnotice("coder", "NULL Coder requested, no transport security");
402 coder
->hashOfLastReceived
= CFDataCreateMutableWithScratch(kCFAllocatorDefault
, lastReceived_di()->output_size
);
403 coder
->lastReceivedWasOld
= false;
405 SOSCoderStart(coder
, NULL
);
410 secerror("Coder create failed: %@\n", localError
? localError
: (CFTypeRef
)CFSTR("No local error in SOSCoderCreate"));
411 secerror("Coder create failed: %@\n", error
? *error
: (CFTypeRef
)CFSTR("WTF NULL?"));
412 CFReleaseNull(myRef
);
413 CFReleaseNull(peerRef
);
414 CFReleaseNull(publicKey
);
415 CFReleaseNull(privateKey
);
417 CFReleaseNull(coder
);
421 static void SOSCoderDestroy(CFTypeRef cf
)
423 SOSCoderRef coder
= (SOSCoderRef
) cf
;
425 CFReleaseNull(coder
->sessRef
);
426 CFReleaseNull(coder
->pendingResponse
);
427 CFReleaseNull(coder
->peer_id
);
428 CFReleaseNull(coder
->hashOfLastReceived
);
432 void SOSCoderReset(SOSCoderRef coder
)
434 SecOTRSessionReset(coder
->sessRef
);
435 coder
->waitingForDataPacket
= false;
436 CFReleaseNull(coder
->pendingResponse
);
438 coder
->lastReceivedWasOld
= false;
439 CFReleaseNull(coder
->hashOfLastReceived
);
440 coder
->hashOfLastReceived
= CFDataCreateMutableWithScratch(kCFAllocatorDefault
, lastReceived_di()->output_size
);
443 bool SOSCoderIsFor(SOSCoderRef coder
, SOSPeerInfoRef peerInfo
, SOSFullPeerInfoRef myPeerInfo
) {
444 SecKeyRef theirPublicKey
= NULL
;
445 SecKeyRef myPublicKey
= NULL
;
446 bool isForThisPair
= false;
447 CFErrorRef localError
= NULL
;
449 myPublicKey
= SOSPeerInfoCopyPubKey(SOSFullPeerInfoGetPeerInfo(myPeerInfo
), &localError
);
450 require(myPublicKey
, errOut
);
452 theirPublicKey
= SOSPeerInfoCopyPubKey(peerInfo
, &localError
);
453 require(theirPublicKey
, errOut
);
455 isForThisPair
= SecOTRSIsForKeys(coder
->sessRef
, myPublicKey
, theirPublicKey
);
459 secerror("SOSCoderIsFor failed: %@\n", localError
? localError
: (CFTypeRef
)CFSTR("No local error in SOSCoderCreate"));
462 CFReleaseNull(myPublicKey
);
463 CFReleaseNull(theirPublicKey
);
464 CFReleaseNull(localError
);
465 return isForThisPair
;
468 CFDataRef
SOSCoderCopyPendingResponse(SOSCoderRef coder
)
470 return coder
->pendingResponse
? CFDataCreateCopy(kCFAllocatorDefault
, coder
->pendingResponse
) : NULL
;
473 void SOSCoderConsumeResponse(SOSCoderRef coder
)
475 CFReleaseNull(coder
->pendingResponse
);
478 static bool SOSOTRSAppendStartPacket(SecOTRSessionRef session
, CFMutableDataRef appendPacket
, CFErrorRef
*error
) {
479 OSStatus otrStatus
= SecOTRSAppendStartPacket(session
, appendPacket
);
480 if (otrStatus
!= errSecSuccess
) {
481 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("append start packet returned: %" PRIdOSStatus
), otrStatus
);
483 return otrStatus
== errSecSuccess
;
486 // Start OTR negotiation if we haven't already done so.
488 SOSCoderStart(SOSCoderRef coder
, CFErrorRef
*error
) {
489 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
490 CFStringRef beginState
= NULL
;
491 SOSCoderStatus result
= kSOSCoderFailure
;
492 CFMutableDataRef startPacket
= NULL
;
494 require_action_quiet(coder
->sessRef
, coderFailure
, CFStringAppend(action
, CFSTR("*** no otr session ***")));
495 beginState
= CFCopyDescription(coder
->sessRef
);
496 require_action_quiet(!coder
->waitingForDataPacket
, negotiatingOut
, CFStringAppend(action
, CFSTR("waiting for peer to send first data packet")));
497 require_action_quiet(!SecOTRSGetIsReadyForMessages(coder
->sessRef
), coderFailure
, CFStringAppend(action
, CFSTR("otr session ready"));
498 result
= kSOSCoderDataReturned
);
499 require_action_quiet(SecOTRSGetIsIdle(coder
->sessRef
), negotiatingOut
, CFStringAppend(action
, CFSTR("otr negotiating already")));
500 require_action_quiet(startPacket
= CFDataCreateMutable(kCFAllocatorDefault
, 0), coderFailure
, SOSCreateError(kSOSErrorAllocationFailure
, CFSTR("alloc failed"), NULL
, error
));
501 require_quiet(SOSOTRSAppendStartPacket(coder
->sessRef
, startPacket
, error
), coderFailure
);
502 CFRetainAssign(coder
->pendingResponse
, startPacket
);
505 result
= kSOSCoderNegotiating
;
508 if (result
== kSOSCoderFailure
&& error
&& *error
)
509 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
510 secinfo("coder", "%@ %s %@ %@ returned %s", beginState
,
511 SecOTRPacketTypeString(startPacket
), action
, coder
->sessRef
, SOSCoderString(result
));
512 CFReleaseNull(startPacket
);
513 CFReleaseSafe(beginState
);
521 SOSCoderResendDH(SOSCoderRef coder
, CFErrorRef
*error
) {
522 if(coder
->sessRef
== NULL
) return kSOSCoderDataReturned
;
523 CFMutableDataRef startPacket
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
524 SOSCoderStatus result
= kSOSCoderFailure
;
525 require_noerr_quiet(SecOTRSAppendRestartPacket(coder
->sessRef
, startPacket
), exit
);
526 secnotice("coder", "Resending OTR Start %@", startPacket
);
527 CFRetainAssign(coder
->pendingResponse
, startPacket
);
528 result
= kSOSCoderNegotiating
;
530 CFReleaseNull(startPacket
);
535 static SOSCoderStatus
nullCoder(CFDataRef from
, CFMutableDataRef
*to
) {
536 *to
= CFDataCreateMutableCopy(NULL
, CFDataGetLength(from
), from
);
537 return kSOSCoderDataReturned
;
540 SOSCoderStatus
SOSCoderUnwrap(SOSCoderRef coder
, CFDataRef codedMessage
, CFMutableDataRef
*message
,
541 CFStringRef clientId
, CFErrorRef
*error
) {
542 if(codedMessage
== NULL
) return kSOSCoderDataReturned
;
543 if(coder
->sessRef
== NULL
) return nullCoder(codedMessage
, message
);
544 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
545 /* This should be the "normal" case. We just use OTR to unwrap the received message. */
546 SOSCoderStatus result
= kSOSCoderFailure
;
548 CFStringRef beginState
= CFCopyDescription(coder
->sessRef
);
549 enum SecOTRSMessageKind kind
= SecOTRSGetMessageKind(coder
->sessRef
, codedMessage
);
553 case kOTRNegotiationPacket
: {
554 /* If we're in here we haven't completed negotiating a session. Use SecOTRSProcessPacket() to go through
555 the negotiation steps and immediately send a reply back if necessary using the sendBlock. This
556 assumes the sendBlock is still available.
558 CFMutableDataRef response
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
559 OSStatus ppstatus
= errSecSuccess
;
561 switch (ppstatus
= SecOTRSProcessPacket(coder
->sessRef
, codedMessage
, response
)) {
563 if (CFDataGetLength(response
) > 1) {
564 CFStringAppendFormat(action
, NULL
, CFSTR("Sending OTR Response %s"), SecOTRPacketTypeString(response
));
565 CFRetainAssign(coder
->pendingResponse
, response
);
566 result
= kSOSCoderNegotiating
;
567 if (SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
568 CFStringAppend(action
, CFSTR(" begin waiting for data packet"));
569 coder
->waitingForDataPacket
= true;
571 } else if(!SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
572 CFStringAppend(action
, CFSTR("stuck?"));
573 result
= kSOSCoderNegotiating
;
575 CFStringAppend(action
, CFSTR("completed negotiation"));
576 result
= kSOSCoderNegotiationCompleted
;
577 coder
->waitingForDataPacket
= false;
581 CFStringAppend(action
, CFSTR("resending dh"));
582 result
= SOSCoderResendDH(coder
, error
);
585 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ Cannot negotiate session (%ld)"), clientId
, (long)ppstatus
);
586 result
= kSOSCoderFailure
;
590 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ Cannot allocate CFData"), clientId
);
591 result
= kSOSCoderFailure
;
594 CFReleaseNull(response
);
601 CFDataRef previousMessageHash
= coder
->hashOfLastReceived
;
602 coder
->hashOfLastReceived
= CFDataCreateWithHash(kCFAllocatorDefault
, lastReceived_di(), CFDataGetBytePtr(codedMessage
), CFDataGetLength(codedMessage
));
603 bool lastWasOld
= coder
->lastReceivedWasOld
;
604 coder
->lastReceivedWasOld
= false;
606 if(!SecOTRSGetIsReadyForMessages(coder
->sessRef
)) {
607 CFStringAppend(action
, CFSTR("not ready for data; resending DH packet"));
608 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed
, 1);
609 result
= SOSCoderResendDH(coder
, error
);
611 if (coder
->waitingForDataPacket
) {
612 CFStringAppend(action
, CFSTR("got data packet we were waiting for "));
613 coder
->waitingForDataPacket
= false;
615 CFMutableDataRef exposed
= CFDataCreateMutable(0, 0);
616 OSStatus otrResult
= SecOTRSVerifyAndExposeMessage(coder
->sessRef
, codedMessage
, exposed
);
617 CFStringAppend(action
, CFSTR("verify and expose message"));
620 CFStringAppend(action
, CFSTR("decoded OTR protected packet"));
621 CFTransferRetained(*message
, exposed
);
622 result
= kSOSCoderDataReturned
;
624 case errSecOTRTooOld
:
625 if (CFEqualSafe(previousMessageHash
, coder
->hashOfLastReceived
)) {
626 CFStringAppend(action
, CFSTR(" repeated"));
627 result
= kSOSCoderStaleEvent
;
629 coder
->lastReceivedWasOld
= true;
631 CFStringAppend(action
, CFSTR(" too old, repeated renegotiating"));
632 // Fail so we will renegotiate
633 result
= kSOSCoderFailure
;
635 CFStringAppend(action
, CFSTR(" too old, forcing message"));
636 // Force message send.
637 result
= kSOSCoderForceMessage
;
641 case errSecOTRIDTooNew
:
642 CFStringAppend(action
, CFSTR(" too new"));
643 result
= kSOSCoderTooNew
;
646 SecError(otrResult
, error
, CFSTR("%@ Cannot expose message: %" PRIdOSStatus
), clientId
, otrResult
);
647 secerror("%@ Decode OTR Protected Packet: %@", clientId
, error
? *error
: NULL
);
648 result
= kSOSCoderFailure
;
652 CFReleaseNull(exposed
);
654 CFReleaseNull(previousMessageHash
);
659 secerror("%@ Unknown packet type: %@", clientId
, codedMessage
);
660 SOSCreateError(kSOSErrorDecodeFailure
, CFSTR("Unknown packet type"), (error
!= NULL
) ? *error
: NULL
, error
);
661 result
= kSOSCoderFailure
;
666 if (result
== kSOSCoderFailure
&& error
&& *error
)
667 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
668 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId
, beginState
,
669 SecOTRPacketTypeString(codedMessage
), action
, coder
->sessRef
, SOSCoderString(result
));
670 CFReleaseSafe(beginState
);
677 SOSCoderStatus
SOSCoderWrap(SOSCoderRef coder
, CFDataRef message
, CFMutableDataRef
*codedMessage
, CFStringRef clientId
, CFErrorRef
*error
) {
678 CFMutableStringRef action
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
679 SOSCoderStatus result
= kSOSCoderDataReturned
;
680 CFStringRef beginState
= NULL
;
681 CFMutableDataRef encoded
= NULL
;
682 OSStatus otrStatus
= 0;
684 require_action_quiet(coder
->sessRef
, errOut
,
685 CFStringAppend(action
, CFSTR("*** using null coder ***"));
686 result
= nullCoder(message
, codedMessage
));
687 beginState
= CFCopyDescription(coder
->sessRef
);
688 require_action_quiet(SecOTRSGetIsReadyForMessages(coder
->sessRef
), errOut
,
689 CFStringAppend(action
, CFSTR("not ready"));
690 result
= kSOSCoderNegotiating
);
691 require_action_quiet(!coder
->waitingForDataPacket
, errOut
,
692 CFStringAppend(action
, CFSTR("waiting for peer to send data packet first"));
693 result
= kSOSCoderNegotiating
);
694 require_action_quiet(encoded
= CFDataCreateMutable(kCFAllocatorDefault
, 0), errOut
,
695 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, NULL
, error
, NULL
, CFSTR("%@ alloc failed"), clientId
);
696 result
= kSOSCoderFailure
);
697 require_noerr_action_quiet(otrStatus
= SecOTRSSignAndProtectMessage(coder
->sessRef
, message
, encoded
), errOut
,
698 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error
!= NULL
) ? *error
: NULL
, error
, NULL
, CFSTR("%@ cannot protect message: %" PRIdOSStatus
), clientId
, otrStatus
);
699 CFReleaseNull(encoded
);
700 result
= kSOSCoderFailure
);
701 *codedMessage
= encoded
;
705 if (result
== kSOSCoderFailure
&& error
&& *error
)
706 CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
);
707 secinfo("coder", "%@ %@ %s %@ %@ returned %s", clientId
, beginState
,
708 SecOTRPacketTypeString(encoded
), action
, coder
->sessRef
, SOSCoderString(result
));
709 CFReleaseSafe(beginState
);
715 bool SOSCoderCanWrap(SOSCoderRef coder
) {
716 return coder
->sessRef
&& SecOTRSGetIsReadyForMessages(coder
->sessRef
) && !coder
->waitingForDataPacket
;