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/SecOTRSessionPriv.h> 
  33 #include <Security/SecureObjectSync/SOSInternal.h> 
  34 #include <Security/SecureObjectSync/SOSFullPeerInfo.h> 
  35 #include <Security/SecureObjectSync/SOSPeerInfo.h> 
  36 #include <Security/SecureObjectSync/SOSPeer.h> 
  37 #include <Security/SecureObjectSync/SOSCoder.h> 
  39 #include <utilities/SecCFRelease.h> 
  40 #include <utilities/SecCFWrappers.h> 
  41 #include <utilities/SecIOFormat.h> 
  42 #include <utilities/SecCFError.h> 
  43 #include <utilities/SecCoreCrypto.h> 
  44 #include <utilities/debugging.h> 
  46 #include <utilities/der_plist.h> 
  47 #include <utilities/der_plist_internal.h> 
  49 #include <corecrypto/ccder.h> 
  50 #include <utilities/iCloudKeychainTrace.h> 
  52 #include "AssertMacros.h" 
  54 struct __OpaqueSOSCoder 
{ 
  58     SecOTRSessionRef sessRef
; 
  59     bool waitingForDataPacket
; 
  60     CFDataRef pendingResponse
; 
  62     CFDataRef hashOfLastReceived
; 
  63     bool      lastReceivedWasOld
; 
  66 #define lastReceived_di ccsha1_di 
  68 CFGiblisWithCompareFor(SOSCoder
) 
  70 static CFStringRef 
SOSCoderCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) { 
  71     SOSCoderRef coder 
= (SOSCoderRef
)cf
; 
  73         __block CFStringRef desc 
= NULL
; 
  74         CFDataPerformWithHexString(coder
->hashOfLastReceived
, ^(CFStringRef dataString
) { 
  75             desc 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<Coder %@ %@ %s%s>"), 
  78                                             coder
->waitingForDataPacket 
? "W" : "w", 
  79                                             coder
->lastReceivedWasOld 
? "O" : "o" 
  89 static Boolean 
SOSCoderCompare(CFTypeRef cfA
, CFTypeRef cfB
) { 
  90     SOSCoderRef coderA 
= (SOSCoderRef
)cfA
, coderB 
= (SOSCoderRef
)cfB
; 
  91     // Use mainly to see if peerB is actually this device (peerA) 
  92     return CFStringCompare(coderA
->peer_id
, coderB
->peer_id
, 0) == kCFCompareEqualTo
; 
  96 static const char *SOSCoderString(SOSCoderStatus coderStatus
) { 
  97     switch (coderStatus
) { 
  98         case kSOSCoderDataReturned
: return "DataReturned"; 
  99         case kSOSCoderNegotiating
: return "Negotiating"; 
 100         case kSOSCoderNegotiationCompleted
: return "NegotiationCompleted"; 
 101         case kSOSCoderFailure
: return "Failure"; 
 102         case kSOSCoderStaleEvent
: return "StaleEvent"; 
 103         case kSOSCoderTooNew
: return "TooNew"; 
 104         default: return "StatusUnknown"; 
 108 static CFMutableDataRef 
sessSerializedCreate(SOSCoderRef coder
, CFErrorRef 
*error
) { 
 109     CFMutableDataRef otr_state 
= NULL
; 
 111     if(!coder 
|| !coder
->sessRef
) { 
 112         SOSCreateErrorWithFormat(kSOSErrorUnexpectedType
, NULL
, error
, 0, CFSTR("No session reference.")); 
 116     if ((otr_state 
= CFDataCreateMutable(NULL
, 0)) == NULL
) { 
 117         SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, NULL
, error
, 0, CFSTR("Mutable Data allocation failed.")); 
 121     if (errSecSuccess 
!= SecOTRSAppendSerialization(coder
->sessRef
, otr_state
)) { 
 122         SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, NULL
, error
, 0, CFSTR("Append Serialization failed.")); 
 123         CFReleaseSafe(otr_state
); 
 131 static size_t der_sizeof_optional_data(CFDataRef data
) { 
 132     return data 
? der_sizeof_data(data
, NULL
) : 0; 
 135 static uint8_t* der_encode_optional_data(CFDataRef data
, CFErrorRef 
*error
, const uint8_t* der
, uint8_t* der_end
) { 
 136     return data 
? der_encode_data(data
, error
, der
, der_end
) : der_end
; 
 141 static size_t SOSCoderGetDEREncodedSize(SOSCoderRef coder
, CFErrorRef 
*error
) { 
 142     size_t encoded_size 
= 0; 
 143     CFMutableDataRef otr_state 
= sessSerializedCreate(coder
, error
); 
 146         size_t data_size 
= der_sizeof_data(otr_state
, error
); 
 147         size_t waiting_size 
= ccder_sizeof_bool(coder
->waitingForDataPacket
, error
); 
 148         size_t pending_size 
= der_sizeof_optional_data(coder
->pendingResponse
); 
 150         if ((data_size 
!= 0) && (waiting_size 
!= 0)) 
 152             encoded_size 
= ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, data_size 
+ waiting_size 
+ pending_size
); 
 154         CFReleaseSafe(otr_state
); 
 160 static uint8_t* SOSCoderEncodeToDER(SOSCoderRef coder
, CFErrorRef
* error
, const uint8_t* der
, uint8_t* der_end
) { 
 161     if(!der_end
) return NULL
; 
 162     uint8_t* result 
= NULL
; 
 163     CFMutableDataRef otr_state 
= sessSerializedCreate(coder
, error
); 
 166         result 
= ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
, 
 167                                              der_encode_data(otr_state
, error
, der
, 
 168                                              ccder_encode_bool(coder
->waitingForDataPacket
, der
, 
 169                                              der_encode_optional_data(coder
->pendingResponse
, error
, der
, der_end
)))); 
 170         CFReleaseSafe(otr_state
); 
 175 bool SOSCoderIsCoderInAwaitingState(SOSCoderRef coder
){ 
 176     return SecOTRSessionIsSessionInAwaitingState(coder
->sessRef
); 
 179 CFDataRef 
SOSCoderCopyDER(SOSCoderRef coder
, CFErrorRef
* error
) { 
 180     CFMutableDataRef encoded 
= NULL
; 
 181     size_t encoded_size 
= SOSCoderGetDEREncodedSize(coder
, error
); 
 183     if (encoded_size 
> 0) { 
 184         encoded 
= CFDataCreateMutable(NULL
, encoded_size
); 
 186             CFDataSetLength(encoded
, encoded_size
); 
 187             uint8_t * der 
= CFDataGetMutableBytePtr(encoded
); 
 188             uint8_t * der_end 
= der 
+ encoded_size
; 
 189             if (!SOSCoderEncodeToDER(coder
, error
, der
, der_end
)) { 
 190                 CFReleaseNull(encoded
); 
 198 static SOSCoderRef 
SOSCoderCreate_internal() { 
 199     SOSCoderRef p 
= CFTypeAllocate(SOSCoder
, struct __OpaqueSOSCoder
, kCFAllocatorDefault
); 
 203     p
->pendingResponse 
= NULL
; 
 204     p
->waitingForDataPacket 
= false; 
 206     p
->hashOfLastReceived 
= NULL
; 
 207     p
->lastReceivedWasOld 
= false; 
 213 // 0 - Type not understood 
 214 // 1 - OCTET_STRING, just stored the data for OTR 
 215 // 2 - SEQUENCE with no version value 
 216 // 3 - SEQUENCE with version value we pull out of the CCDER_INTEGER 
 218 typedef enum coderExportFormatVersion 
{ 
 220     kCoderAsOTRDataOnly 
= 1, 
 221     kCoderAsSequence 
= 2, 
 222     kCoderAsVersionedSequence 
= 3, 
 224     kCurrentCoderExportVersion 
= kCoderAsVersionedSequence
 
 225 } CoderExportFormatVersion
; 
 227 static uint64_t SOSCoderGetExportedVersion(const uint8_t *der
, const uint8_t *der_end
) { 
 229     uint64_t result 
= kNotUnderstood
; 
 230     require(ccder_decode_tag(&tag
, der
, der_end
),xit
); 
 232         case CCDER_OCTET_STRING
: // TODO: this code is safe to delete? 
 233             result 
= kCoderAsOTRDataOnly
; 
 236         case CCDER_CONSTRUCTED_SEQUENCE
: 
 238             const uint8_t *sequence_end 
= NULL
; 
 239             der 
= ccder_decode_sequence_tl(&sequence_end
, der
, der_end
); 
 240             ccder_tag firstSequenceTag
; 
 241             require(ccder_decode_tag(&firstSequenceTag
, der
, der_end
),xit
); 
 243             switch (firstSequenceTag
) { 
 244                 case CCDER_OCTET_STRING
: 
 245                     result 
= kCoderAsSequence
; 
 248                     der 
= ccder_decode_uint64(NULL
, der
, sequence_end
); 
 250                         result 
= kNotUnderstood
; 
 252                         result 
= kCoderAsVersionedSequence
; 
 263 SOSCoderRef 
SOSCoderCreateFromData(CFDataRef exportedData
, CFErrorRef 
*error
) { 
 264     // TODO: fill in errors for all failure cases 
 265     //require_action_quiet(coder, xit, SOSCreateError(kSOSErrorSendFailure, CFSTR("No coder for peer"), NULL, error)); 
 267     SOSCoderRef p 
= SOSCoderCreate_internal(); 
 269     const uint8_t *der 
= CFDataGetBytePtr(exportedData
); 
 270     const uint8_t *der_end 
= der 
+ CFDataGetLength(exportedData
); 
 272     CFDataRef otr_data 
= NULL
; 
 274     switch (SOSCoderGetExportedVersion(der
, der_end
)) { 
 275         case kCoderAsOTRDataOnly
: 
 276             der 
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, der_end
); 
 277             p
->waitingForDataPacket 
= false; 
 280         case kCoderAsSequence
: 
 282             const uint8_t *sequence_end 
= NULL
; 
 283             der 
= ccder_decode_sequence_tl(&sequence_end
, der
, der_end
); 
 285             require_action_quiet(sequence_end 
== der_end
, fail
, SecCFDERCreateError(kSOSErrorDecodeFailure
, CFSTR("Extra data in SOS coder"), NULL
, error
)); 
 287             der 
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, sequence_end
); 
 288             der 
= ccder_decode_bool(&p
->waitingForDataPacket
, der
, sequence_end
); 
 289             if (der 
!= sequence_end
) { // optionally a pending response 
 290                 der 
= der_decode_data(kCFAllocatorDefault
, 0, &p
->pendingResponse
, error
, der
, sequence_end
); 
 295         case kCoderAsVersionedSequence
: 
 297             const uint8_t *sequence_end 
= NULL
; 
 298             der 
= ccder_decode_sequence_tl(&sequence_end
, der
, der_end
); 
 300             require_action_quiet(sequence_end 
== der_end
, fail
, SecCFDERCreateError(kSOSErrorDecodeFailure
, CFSTR("Extra data in SOS coder"), NULL
, error
)); 
 303             der 
= ccder_decode_uint64(&version
, der
, sequence_end
); 
 304             if (version 
!= kCoderAsVersionedSequence
) { 
 305                 SOSErrorCreate(kSOSErrorDecodeFailure
, error
, NULL
, CFSTR("Unsupported Sequence Version: %lld"), version
); 
 309             der 
= der_decode_data(kCFAllocatorDefault
, 0, &otr_data
, error
, der
, sequence_end
); 
 310             der 
= ccder_decode_bool(&p
->waitingForDataPacket
, der
, sequence_end
); 
 311             der 
= ccder_decode_bool(&p
->lastReceivedWasOld
, der
, sequence_end
); 
 312             der 
= der_decode_data(kCFAllocatorDefault
, 0, &p
->hashOfLastReceived
, error
, der
, sequence_end
); 
 313             if (der 
!= sequence_end
) { // optionally a pending response 
 314                 der 
= der_decode_data(kCFAllocatorDefault
, 0, &p
->pendingResponse
, error
, der
, sequence_end
); 
 320             SOSErrorCreate(kSOSErrorDecodeFailure
, error
, NULL
, CFSTR("Unsupported SOS Coder DER")); 
 326     p
->sessRef 
= SecOTRSessionCreateFromData(NULL
, otr_data
); 
 327     require(p
->sessRef
, fail
); 
 329     if (p
->hashOfLastReceived 
== NULL
) 
 330         p
->hashOfLastReceived 
= CFDataCreateMutableWithScratch(kCFAllocatorDefault
, lastReceived_di()->output_size
); 
 332     CFReleaseSafe(otr_data
); 
 337     CFReleaseSafe(otr_data
); 
 342 SOSCoderRef 
SOSCoderCreate(SOSPeerInfoRef peerInfo
, SOSFullPeerInfoRef myPeerInfo
, CFBooleanRef useCompact
, CFErrorRef 
*error
) { 
 343     CFAllocatorRef allocator 
= CFGetAllocator(peerInfo
); 
 345     SOSCoderRef coder 
= SOSCoderCreate_internal(); 
 347     CFErrorRef localError 
= NULL
; 
 349     SecOTRFullIdentityRef myRef 
= NULL
; 
 350     SecOTRPublicIdentityRef peerRef 
= NULL
; 
 351     SecKeyRef privateKey 
= NULL
; 
 352     SecKeyRef publicKey 
= NULL
; 
 354     if (myPeerInfo 
&& peerInfo
) { 
 355         privateKey 
= SOSFullPeerInfoCopyDeviceKey(myPeerInfo
, &localError
); 
 356         require_quiet(privateKey
, errOut
); 
 358         myRef 
= SecOTRFullIdentityCreateFromSecKeyRef(allocator
, privateKey
, &localError
); 
 359         require_quiet(myRef
, errOut
); 
 361         CFReleaseNull(privateKey
); 
 363         publicKey 
= SOSPeerInfoCopyPubKey(peerInfo
, &localError
); 
 364         require(publicKey
, errOut
); 
 366         peerRef 
= SecOTRPublicIdentityCreateFromSecKeyRef(allocator
, publicKey
, &localError
); 
 367         require_quiet(peerRef
, errOut
); 
 369         if(useCompact 
== kCFBooleanTrue
) 
 370             coder
->sessRef 
= SecOTRSessionCreateFromIDAndFlags(allocator
, myRef
, peerRef
, kSecOTRUseAppleCustomMessageFormat
); 
 373             coder
->sessRef 
= SecOTRSessionCreateFromID(allocator
, myRef
, peerRef
); 
 375         require(coder
->sessRef
, errOut
); 
 377         coder
->waitingForDataPacket 
= false; 
 378         coder
->pendingResponse 
= NULL
; 
 380         CFReleaseNull(publicKey
); 
 381         CFReleaseNull(privateKey
); 
 382         CFReleaseNull(myRef
); 
 383         CFReleaseNull(peerRef
); 
 385         secnotice("coder", "NULL Coder requested, no transport security"); 
 388     coder
->hashOfLastReceived 
= CFDataCreateMutableWithScratch(kCFAllocatorDefault
, lastReceived_di()->output_size
); 
 389     coder
->lastReceivedWasOld 
= false; 
 391     SOSCoderStart(coder
, NULL
); 
 396     secerror("Coder create failed: %@\n", localError 
? localError 
: (CFTypeRef
)CFSTR("No local error in SOSCoderCreate")); 
 397     secerror("Coder create failed: %@\n", error 
? *error 
: (CFTypeRef
)CFSTR("WTF NULL?")); 
 398     CFReleaseNull(myRef
); 
 399     CFReleaseNull(peerRef
); 
 400     CFReleaseNull(publicKey
); 
 401     CFReleaseNull(privateKey
); 
 403     CFReleaseNull(coder
); 
 407 static void SOSCoderDestroy(CFTypeRef cf
) 
 409     SOSCoderRef coder 
= (SOSCoderRef
) cf
; 
 411         CFReleaseNull(coder
->sessRef
); 
 412         CFReleaseNull(coder
->pendingResponse
); 
 413         CFReleaseNull(coder
->hashOfLastReceived
); 
 417 void SOSCoderReset(SOSCoderRef coder
) 
 419     SecOTRSessionReset(coder
->sessRef
); 
 420     coder
->waitingForDataPacket 
= false; 
 421     CFReleaseNull(coder
->pendingResponse
); 
 423     coder
->lastReceivedWasOld 
= false; 
 424     CFReleaseNull(coder
->hashOfLastReceived
); 
 425     coder
->hashOfLastReceived 
= CFDataCreateMutableWithScratch(kCFAllocatorDefault
, lastReceived_di()->output_size
); 
 428 bool SOSCoderIsFor(SOSCoderRef coder
, SOSPeerInfoRef peerInfo
, SOSFullPeerInfoRef myPeerInfo
) { 
 429     SecKeyRef theirPublicKey 
= NULL
; 
 430     SecKeyRef myPublicKey 
= NULL
; 
 431     bool isForThisPair 
= false; 
 432     CFErrorRef localError 
= NULL
; 
 434     myPublicKey 
= SOSPeerInfoCopyPubKey(SOSFullPeerInfoGetPeerInfo(myPeerInfo
), &localError
); 
 435     require(myPublicKey
, errOut
); 
 437     theirPublicKey 
= SOSPeerInfoCopyPubKey(peerInfo
, &localError
); 
 438     require(theirPublicKey
, errOut
); 
 440     isForThisPair 
= SecOTRSIsForKeys(coder
->sessRef
, myPublicKey
, theirPublicKey
); 
 444         secerror("SOSCoderIsFor failed: %@\n", localError 
? localError 
: (CFTypeRef
)CFSTR("No local error in SOSCoderCreate")); 
 447     CFReleaseNull(myPublicKey
); 
 448     CFReleaseNull(theirPublicKey
); 
 449     CFReleaseNull(localError
); 
 450     return isForThisPair
; 
 453 CFDataRef 
SOSCoderCopyPendingResponse(SOSCoderRef coder
) 
 455     return coder
->pendingResponse 
? CFDataCreateCopy(kCFAllocatorDefault
, coder
->pendingResponse
) : NULL
; 
 458 void SOSCoderConsumeResponse(SOSCoderRef coder
) 
 460     CFReleaseNull(coder
->pendingResponse
); 
 463 static bool SOSOTRSAppendStartPacket(SecOTRSessionRef session
, CFMutableDataRef appendPacket
, CFErrorRef 
*error
) { 
 464     OSStatus otrStatus 
= SecOTRSAppendStartPacket(session
, appendPacket
); 
 465     if (otrStatus 
!= errSecSuccess
) { 
 466         SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error 
!= NULL
) ? *error 
: NULL
, error
, NULL
, CFSTR("append start packet returned: %" PRIdOSStatus
), otrStatus
); 
 468     return otrStatus 
== errSecSuccess
; 
 471 // Start OTR negotiation if we haven't already done so. 
 473 SOSCoderStart(SOSCoderRef coder
, CFErrorRef 
*error
) { 
 474     CFMutableStringRef action 
= CFStringCreateMutable(kCFAllocatorDefault
, 0); 
 475     CFStringRef beginState 
= NULL
; 
 476     SOSCoderStatus result 
= kSOSCoderFailure
; 
 477     CFMutableDataRef startPacket 
= NULL
; 
 479     require_action_quiet(coder
->sessRef
, coderFailure
, CFStringAppend(action
, CFSTR("*** no otr session ***"))); 
 480     beginState 
= CFCopyDescription(coder
->sessRef
); 
 481     require_action_quiet(!coder
->waitingForDataPacket
, negotiatingOut
, CFStringAppend(action
, CFSTR("waiting for peer to send first data packet"))); 
 482     require_action_quiet(!SecOTRSGetIsReadyForMessages(coder
->sessRef
), coderFailure
, CFStringAppend(action
, CFSTR("otr session ready")); 
 483                          result 
= kSOSCoderDataReturned
); 
 484     require_action_quiet(SecOTRSGetIsIdle(coder
->sessRef
), negotiatingOut
, CFStringAppend(action
, CFSTR("otr negotiating already"))); 
 485     require_action_quiet(startPacket 
= CFDataCreateMutable(kCFAllocatorDefault
, 0), coderFailure
, SOSCreateError(kSOSErrorAllocationFailure
, CFSTR("alloc failed"), NULL
, error
)); 
 486     require_quiet(SOSOTRSAppendStartPacket(coder
->sessRef
, startPacket
, error
), coderFailure
); 
 487     CFRetainAssign(coder
->pendingResponse
, startPacket
); 
 490     result 
= kSOSCoderNegotiating
; 
 493     if (result 
== kSOSCoderFailure 
&& error 
&& *error
) 
 494         CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
); 
 495     secinfo("coder", "%@ %s %@ %@ returned %s", beginState
, 
 496               SecOTRPacketTypeString(startPacket
), action
, coder
->sessRef
, SOSCoderString(result
)); 
 497     CFReleaseNull(startPacket
); 
 498     CFReleaseSafe(beginState
); 
 506 SOSCoderResendDH(SOSCoderRef coder
, CFErrorRef 
*error
) { 
 507     if(coder
->sessRef 
== NULL
) return kSOSCoderDataReturned
; 
 508     CFMutableDataRef startPacket 
= CFDataCreateMutable(kCFAllocatorDefault
, 0); 
 509     SOSCoderStatus result 
= kSOSCoderFailure
; 
 510     require_noerr_quiet(SecOTRSAppendRestartPacket(coder
->sessRef
, startPacket
), exit
); 
 511     secnotice("coder", "Resending OTR Start %@", startPacket
); 
 512     CFRetainAssign(coder
->pendingResponse
, startPacket
); 
 513     result 
= kSOSCoderNegotiating
; 
 515     CFReleaseNull(startPacket
); 
 520 static SOSCoderStatus 
nullCoder(CFDataRef from
, CFMutableDataRef 
*to
) { 
 521     *to 
= CFDataCreateMutableCopy(NULL
, CFDataGetLength(from
), from
); 
 522     return kSOSCoderDataReturned
; 
 525 SOSCoderStatus 
SOSCoderUnwrap(SOSCoderRef coder
, CFDataRef codedMessage
, CFMutableDataRef 
*message
, 
 526                               CFStringRef clientId
, CFErrorRef 
*error
) { 
 527     if(codedMessage 
== NULL
) return kSOSCoderDataReturned
; 
 528     if(coder
->sessRef 
== NULL
) return nullCoder(codedMessage
, message
); 
 529     CFMutableStringRef action 
= CFStringCreateMutable(kCFAllocatorDefault
, 0); 
 530     /* This should be the "normal" case.  We just use OTR to unwrap the received message. */ 
 531     SOSCoderStatus result 
= kSOSCoderFailure
; 
 533     CFStringRef beginState 
= CFCopyDescription(coder
->sessRef
); 
 534     enum SecOTRSMessageKind kind 
= SecOTRSGetMessageKind(coder
->sessRef
, codedMessage
); 
 538         case kOTRNegotiationPacket
: { 
 539             /* If we're in here we haven't completed negotiating a session.  Use SecOTRSProcessPacket() to go through 
 540              the negotiation steps and immediately send a reply back if necessary using the sendBlock.  This 
 541              assumes the sendBlock is still available. 
 543             CFMutableDataRef response 
= CFDataCreateMutable(kCFAllocatorDefault
, 0); 
 544             OSStatus ppstatus 
= errSecSuccess
; 
 546                 switch (ppstatus 
= SecOTRSProcessPacket(coder
->sessRef
, codedMessage
, response
)) { 
 548                         if (CFDataGetLength(response
) > 1) { 
 549                             CFStringAppendFormat(action
, NULL
, CFSTR("Sending OTR Response %s"), SecOTRPacketTypeString(response
)); 
 550                             CFRetainAssign(coder
->pendingResponse
, response
); 
 551                             result 
= kSOSCoderNegotiating
; 
 552                             if (SecOTRSGetIsReadyForMessages(coder
->sessRef
)) { 
 553                                 CFStringAppend(action
, CFSTR(" begin waiting for data packet")); 
 554                                 coder
->waitingForDataPacket 
= true; 
 556                         } else if(!SecOTRSGetIsReadyForMessages(coder
->sessRef
)) { 
 557                             CFStringAppend(action
, CFSTR("stuck?")); 
 558                             result 
= kSOSCoderNegotiating
; 
 560                             CFStringAppend(action
, CFSTR("completed negotiation")); 
 561                             result 
= kSOSCoderNegotiationCompleted
; 
 562                             coder
->waitingForDataPacket 
= false; 
 566                         CFStringAppend(action
, CFSTR("resending dh")); 
 567                         result 
= SOSCoderResendDH(coder
, error
); 
 570                         SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error 
!= NULL
) ? *error 
: NULL
, error
, NULL
, CFSTR("%@ Cannot negotiate session (%ld)"), clientId
, (long)ppstatus
); 
 571                         result 
= kSOSCoderFailure
; 
 575                 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, (error 
!= NULL
) ? *error 
: NULL
, error
, NULL
, CFSTR("%@ Cannot allocate CFData"), clientId
); 
 576                 result 
= kSOSCoderFailure
; 
 579             CFReleaseNull(response
); 
 586             CFDataRef previousMessageHash 
= coder
->hashOfLastReceived
; 
 587             coder
->hashOfLastReceived 
= CFDataCreateWithHash(kCFAllocatorDefault
, lastReceived_di(), CFDataGetBytePtr(codedMessage
), CFDataGetLength(codedMessage
)); 
 588             bool lastWasOld 
= coder
->lastReceivedWasOld
; 
 589             coder
->lastReceivedWasOld 
= false; 
 591             if(!SecOTRSGetIsReadyForMessages(coder
->sessRef
)) { 
 592                 CFStringAppend(action
, CFSTR("not ready for data; resending DH packet")); 
 593                                 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed
, 1); 
 594                 result 
= SOSCoderResendDH(coder
, error
); 
 596                 if (coder
->waitingForDataPacket
) { 
 597                     CFStringAppend(action
, CFSTR("got data packet we were waiting for ")); 
 598                     coder
->waitingForDataPacket 
= false; 
 600                 CFMutableDataRef exposed 
= CFDataCreateMutable(0, 0); 
 601                 OSStatus otrResult 
= SecOTRSVerifyAndExposeMessage(coder
->sessRef
, codedMessage
, exposed
); 
 602                 CFStringAppend(action
, CFSTR("verify and expose message")); 
 605                         CFStringAppend(action
, CFSTR("decoded OTR protected packet")); 
 606                         CFTransferRetained(*message
, exposed
); 
 607                         result 
= kSOSCoderDataReturned
; 
 609                     case errSecOTRTooOld
: 
 610                         if (CFEqualSafe(previousMessageHash
, coder
->hashOfLastReceived
)) { 
 611                             CFStringAppend(action
, CFSTR(" repeated")); 
 612                             result 
= kSOSCoderStaleEvent
; 
 614                             coder
->lastReceivedWasOld 
= true; 
 616                                 CFStringAppend(action
, CFSTR(" too old, repeated renegotiating")); 
 617                                 // Fail so we will renegotiate 
 618                                 result 
= kSOSCoderFailure
; 
 620                                 CFStringAppend(action
, CFSTR(" too old, forcing message")); 
 621                                 // Force message send. 
 622                                 result 
= kSOSCoderForceMessage
; 
 626                     case errSecOTRIDTooNew
: 
 627                         CFStringAppend(action
, CFSTR(" too new")); 
 628                         result 
= kSOSCoderTooNew
; 
 631                         SecError(otrResult
, error
, CFSTR("%@ Cannot expose message: %" PRIdOSStatus
), clientId
, otrResult
); 
 632                         secerror("%@ Decode OTR Protected Packet: %@", clientId
, error 
? *error 
: NULL
); 
 633                         result 
= kSOSCoderFailure
; 
 637                 CFReleaseNull(exposed
); 
 639             CFReleaseNull(previousMessageHash
); 
 644             secerror("%@ Unknown packet type: %@", clientId
, codedMessage
); 
 645             SOSCreateError(kSOSErrorDecodeFailure
, CFSTR("Unknown packet type"), (error 
!= NULL
) ? *error 
: NULL
, error
); 
 646             result 
= kSOSCoderFailure
; 
 651     if (result 
== kSOSCoderFailure 
&& error 
&& *error
) 
 652         CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
); 
 653     secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId
, beginState
, 
 654               SecOTRPacketTypeString(codedMessage
), action
, coder
->sessRef
, SOSCoderString(result
)); 
 655     CFReleaseSafe(beginState
); 
 662 SOSCoderStatus 
SOSCoderWrap(SOSCoderRef coder
, CFDataRef message
, CFMutableDataRef 
*codedMessage
, CFStringRef clientId
, CFErrorRef 
*error
) { 
 663     CFMutableStringRef action 
= CFStringCreateMutable(kCFAllocatorDefault
, 0); 
 664     SOSCoderStatus result 
= kSOSCoderDataReturned
; 
 665     CFStringRef beginState 
= NULL
; 
 666     CFMutableDataRef encoded 
= NULL
; 
 667     OSStatus otrStatus 
= 0; 
 669     require_action_quiet(coder
->sessRef
, errOut
, 
 670                          CFStringAppend(action
, CFSTR("*** using null coder ***")); 
 671                          result 
= nullCoder(message
, codedMessage
)); 
 672     beginState 
= CFCopyDescription(coder
->sessRef
); 
 673     require_action_quiet(SecOTRSGetIsReadyForMessages(coder
->sessRef
), errOut
, 
 674                          CFStringAppend(action
, CFSTR("not ready")); 
 675                          result 
= kSOSCoderNegotiating
); 
 676     require_action_quiet(!coder
->waitingForDataPacket
, errOut
, 
 677                          CFStringAppend(action
, CFSTR("waiting for peer to send data packet first")); 
 678                          result 
= kSOSCoderNegotiating
); 
 679     require_action_quiet(encoded 
= CFDataCreateMutable(kCFAllocatorDefault
, 0), errOut
, 
 680                          SOSCreateErrorWithFormat(kSOSErrorAllocationFailure
, NULL
, error
, NULL
, CFSTR("%@ alloc failed"), clientId
); 
 681                          result 
= kSOSCoderFailure
); 
 682     require_noerr_action_quiet(otrStatus 
= SecOTRSSignAndProtectMessage(coder
->sessRef
, message
, encoded
), errOut
, 
 683                                SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, (error 
!= NULL
) ? *error 
: NULL
, error
, NULL
, CFSTR("%@ cannot protect message: %" PRIdOSStatus
), clientId
, otrStatus
); 
 684                                CFReleaseNull(encoded
); 
 685                                result 
= kSOSCoderFailure
); 
 686     *codedMessage 
= encoded
; 
 690     if (result 
== kSOSCoderFailure 
&& error 
&& *error
) 
 691         CFStringAppendFormat(action
, NULL
, CFSTR(" %@"), *error
); 
 692     secinfo("coder", "%@ %@ %s %@ %@ returned %s", clientId
, beginState
, 
 693               SecOTRPacketTypeString(encoded
), action
, coder
->sessRef
, SOSCoderString(result
)); 
 694     CFReleaseSafe(beginState
); 
 700 bool SOSCoderCanWrap(SOSCoderRef coder
) { 
 701     return coder
->sessRef 
&& SecOTRSGetIsReadyForMessages(coder
->sessRef
) && !coder
->waitingForDataPacket
;