2 * Copyright (c) 2011-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 <sys/types.h>
27 #include <CoreFoundation/CFDate.h>
29 #include "SecOTRSession.h"
31 #include "SecOTRMath.h"
32 #include "SecOTRDHKey.h"
33 #include "SecOTRSessionPriv.h"
34 #include "SecOTRPackets.h"
35 #include "SecOTRPacketData.h"
36 #include "SecOTRIdentityPriv.h"
38 #include <utilities/SecCFWrappers.h>
40 #include <CoreFoundation/CFRuntime.h>
41 #include <CoreFoundation/CFString.h>
43 #include <Security/SecBasePriv.h>
44 #include <Security/SecRandom.h>
45 #include <Security/SecBase64.h>
46 #include <Security/SecKeyPriv.h>
48 #include <Security/SecureObjectSync/SOSPeerInfo.h>
49 #include <Security/SecureObjectSync/SOSCircle.h>
50 #include <Security/SecureObjectSync/SOSCloudCircle.h>
51 #include <Security/SecureObjectSync/SOSInternal.h>
52 #include <Security/SecureObjectSync/SOSUserKeygen.h>
54 #include <AssertMacros.h>
56 #include <corecrypto/cchmac.h>
57 #include <corecrypto/ccsha2.h>
58 #include <corecrypto/ccsha1.h>
64 #include <os/activity.h>
66 #include <utilities/array_size.h>
68 #include <ipc/securityd_client.h>
69 #include <Security/SecuritydXPC.h>
71 CFGiblisFor(SecOTRSession
);
73 static uint64_t setup_defaults_settings(){
75 Boolean keyExistsAndHasValue
= false;
77 seconds
= CFPreferencesGetAppIntegerValue(CFSTR("OTR"), CFSTR("com.apple.security"), &keyExistsAndHasValue
);
78 secdebug("OTR", "Retrieving OTR default settings was success? %d value retrieved: %llu", keyExistsAndHasValue
, seconds
);
79 return keyExistsAndHasValue
? seconds
: (kSecondsPerMinute
* 15); //15 minutes by default
82 static uint64_t SecOTRGetDefaultsWriteSeconds(void) {
83 static dispatch_once_t sdOnceToken
;
84 static uint64_t seconds
;
86 dispatch_once(&sdOnceToken
, ^{
87 seconds
= setup_defaults_settings();
93 static void SecOTRSEnableTimeToRoll(SecOTRSessionRef session
){
94 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
95 CFAbsoluteTime nextTimeToRoll
= now
+ session
->_stallSeconds
;
97 if(session
->_timeToRoll
== 0 || session
->_timeToRoll
> nextTimeToRoll
){
98 session
->_timeToRoll
= nextTimeToRoll
;
102 static void SecOTRSExpireCachedKeysForFullKey(SecOTRSessionRef session
, SecOTRFullDHKeyRef myKey
)
104 for(int i
= 0; i
< kOTRKeyCacheSize
; ++i
)
106 if (0 == timingsafe_bcmp(session
->_keyCache
[i
]._fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
)) {
107 CFDataAppendBytes(session
->_macKeysToExpose
, session
->_keyCache
[i
]._receiveMacKey
, sizeof(session
->_keyCache
[i
]._receiveMacKey
));
108 bzero(&session
->_keyCache
[i
], sizeof(session
->_keyCache
[i
]));
113 static void SecOTRSExpireCachedKeysForPublicKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef theirKey
)
115 for(int i
= 0; i
< kOTRKeyCacheSize
; ++i
)
117 if (0 == timingsafe_bcmp(session
->_keyCache
[i
]._publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
)) {
118 CFDataAppendBytes(session
->_macKeysToExpose
, session
->_keyCache
[i
]._receiveMacKey
, sizeof(session
->_keyCache
[i
]._receiveMacKey
));
120 bzero(&session
->_keyCache
[i
], sizeof(session
->_keyCache
[i
]));
125 static void SecOTRGenerateNewProposedKey(SecOTRSessionRef session
)
127 SecOTRSExpireCachedKeysForFullKey(session
, session
->_myKey
);
129 // Swap the keys so we know the current key.
131 SecOTRFullDHKeyRef oldKey
= session
->_myKey
;
132 session
->_myKey
= session
->_myNextKey
;
133 session
->_myNextKey
= oldKey
;
136 // Derive a new next key by regenerating over the old key.
137 SecFDHKNewKey(session
->_myNextKey
);
139 session
->_keyID
+= 1;
143 static void SecOTRSHandleProposalAcknowledge(SecOTRSessionRef session
){
144 if(session
->_missedAck
){
145 SecOTRGenerateNewProposedKey(session
);
146 session
->_missedAck
= false;
149 session
->_receivedAck
= true;
150 SecOTRSEnableTimeToRoll(session
);
154 static void SecOTRSRollIfTime(SecOTRSessionRef session
){
156 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
157 CFAbsoluteTime longestTimeToRoll
= now
+ session
->_stallSeconds
;
159 //in case time to roll becomes too large we're going to roll now!
160 if(session
->_timeToRoll
< now
|| session
->_timeToRoll
> longestTimeToRoll
){
161 SOSOTRSRoll(session
);
162 session
->_timeToRoll
= 0;
167 static OTRMessageType
SecOTRSGetMessageType(CFDataRef message
)
169 OTRMessageType type
= kInvalidMessage
;
171 CFDataRef decodedBytes
= SecOTRCopyIncomingBytes(message
);
173 const uint8_t *bytes
= CFDataGetBytePtr(decodedBytes
);
174 size_t size
= CFDataGetLength(decodedBytes
);
176 if (noErr
!= ReadHeader(&bytes
, &size
, &type
)) {
177 uint8_t firstByte
= *CFDataGetBytePtr(decodedBytes
);
179 case kOddCompactDataMessage
:
180 case kEvenCompactDataMessage
:
181 case kOddCompactDataMessageWithHashes
:
182 case kEvenCompactDataMessageWithHashes
:
191 CFReleaseNull(decodedBytes
);
198 static CFStringRef
SecOTRCacheElementCopyDescription(SecOTRCacheElement
*keyCache
){
199 __block CFStringRef description
= NULL
;
200 BufferPerformWithHexString(keyCache
->_fullKeyHash
, sizeof(keyCache
->_fullKeyHash
), ^(CFStringRef fullKeyHashString
) {
201 BufferPerformWithHexString(keyCache
->_publicKeyHash
,sizeof(keyCache
->_publicKeyHash
), ^(CFStringRef publicKeyHashString
) {
202 description
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("fkh: [%@], pkh: [%@], c: %llu tc: %llu"), fullKeyHashString
, publicKeyHashString
, keyCache
->_counter
, keyCache
->_theirCounter
);
209 const char *SecOTRPacketTypeString(CFDataRef message
)
211 if (!message
) return "NoMessage";
212 switch (SecOTRSGetMessageType(message
)) {
213 case kDHMessage
: return "DHMessage (0x02)";
214 case kDataMessage
: return "DataMessage (0x03)";
215 case kDHKeyMessage
: return "DHKeyMessage (0x0A)";
216 case kRevealSignatureMessage
: return "RevealSignatureMessage (0x11)";
217 case kSignatureMessage
: return "SignatureMessage (0x12)";
218 case kEvenCompactDataMessage
: return "kEvenCompactDatamessage (0x20)";
219 case kOddCompactDataMessage
: return "kOddCompactDataMessage (0x21)";
220 case kEvenCompactDataMessageWithHashes
: return "kEvenCompactDatamessage (0x30)";
221 case kOddCompactDataMessageWithHashes
: return "kOddCompactDataMessage (0x31)";
222 case kInvalidMessage
: return "InvalidMessage (0xFF)";
223 default: return "UnknownMessage";
227 static const char *SecOTRAuthStateString(SecOTRAuthState authState
)
230 case kIdle
: return "Idle";
231 case kAwaitingDHKey
: return "AwaitingDHKey";
232 case kAwaitingRevealSignature
: return "AwaitingRevealSignature";
233 case kAwaitingSignature
: return "AwaitingSignature";
234 case kDone
: return "Done";
235 default: return "InvalidState";
239 static CF_RETURNS_RETAINED CFStringRef
SecOTRSessionCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
240 SecOTRSessionRef session
= (SecOTRSessionRef
)cf
;
242 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
244 return CFStringCreateWithFormat(kCFAllocatorDefault
,NULL
,CFSTR("<%s %s %s %s%s%s%s %d:%d %s%s %llu %s%s%s%s>"),
245 SecOTRAuthStateString(session
->_state
),
246 session
->_compactAppleMessages
? "C" :"c",
247 session
->_includeHashes
? "I" : "i",
248 session
->_me
? "F" : "f",
249 session
->_them
? "P" : "p",
250 session
->_receivedDHMessage
? "D" : "d",
251 session
->_receivedDHKeyMessage
? "K" : "k",
253 session
->_theirKeyID
,
254 session
->_theirPreviousKey
? "P" : "p",
255 session
->_theirKey
? "T" : "t",
256 session
->_stallSeconds
,
257 session
->_missedAck
? "M" : "m",
258 session
->_receivedAck
? "R" : "r",
259 session
->_stallingTheirRoll
? "S" : "s",
260 (session
->_timeToRoll
> now
&& session
->_timeToRoll
!= 0) ? "E" : "e");
263 static void SecOTRSessionDestroy(CFTypeRef cf
) {
264 SecOTRSessionRef session
= (SecOTRSessionRef
)cf
;
266 CFReleaseNull(session
->_receivedDHMessage
);
267 CFReleaseNull(session
->_receivedDHKeyMessage
);
269 CFReleaseNull(session
->_me
);
270 CFReleaseNull(session
->_myKey
);
271 CFReleaseNull(session
->_myNextKey
);
273 CFReleaseNull(session
->_them
);
274 CFReleaseNull(session
->_theirKey
);
275 CFReleaseNull(session
->_theirPreviousKey
);
277 CFReleaseNull(session
->_macKeysToExpose
);
279 dispatch_release(session
->_queue
);
282 static void SecOTRSessionResetInternal(SecOTRSessionRef session
)
284 session
->_state
= kIdle
;
286 CFReleaseNull(session
->_receivedDHMessage
);
287 CFReleaseNull(session
->_receivedDHKeyMessage
);
290 CFReleaseNull(session
->_myKey
);
291 CFReleaseNull(session
->_myNextKey
);
292 //session->_myNextKey = SecOTRFullDHKCreate(kCFAllocatorDefault);
293 session
->_theirKeyID
= 0;
294 CFReleaseNull(session
->_theirKey
);
295 CFReleaseNull(session
->_theirPreviousKey
);
296 CFReleaseNull(session
->_macKeysToExpose
);
297 session
->_macKeysToExpose
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
299 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
302 int SecOTRSGetKeyID(SecOTRSessionRef session
){
303 return session
->_keyID
;
306 int SecOTRSGetTheirKeyID(SecOTRSessionRef session
){
307 return session
->_theirKeyID
;
310 void SecOTRSessionReset(SecOTRSessionRef session
)
312 dispatch_sync_f(session
->_queue
, session
, (dispatch_function_t
) SecOTRSessionResetInternal
);
316 static void SecOTRPIPerformWithSerializationString(SecOTRPublicIdentityRef id
, void (^action
)(CFStringRef string
)) {
317 CFMutableDataRef idData
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
318 SecOTRPIAppendSerialization(id
, idData
, NULL
);
319 CFDataPerformWithHexString(idData
, action
);
320 CFReleaseNull(idData
);
323 SecOTRSessionRef
SecOTRSessionCreateFromID(CFAllocatorRef allocator
,
324 SecOTRFullIdentityRef myID
,
325 SecOTRPublicIdentityRef theirID
)
327 SecOTRSessionRef newID
= CFTypeAllocate(SecOTRSession
, struct _SecOTRSession
, allocator
);
329 (void)SecOTRGetDefaultsWriteSeconds();
330 newID
->_queue
= dispatch_queue_create("OTRSession", DISPATCH_QUEUE_SERIAL
);
332 newID
->_me
= CFRetainSafe(myID
);
333 newID
->_them
= CFRetainSafe(theirID
);
334 newID
->_receivedDHMessage
= NULL
;
335 newID
->_receivedDHKeyMessage
= NULL
;
336 newID
->_myKey
= NULL
;
337 newID
->_myNextKey
= NULL
;
338 newID
->_theirKey
= NULL
;
339 newID
->_theirPreviousKey
= NULL
;
340 newID
->_macKeysToExpose
= NULL
;
341 newID
->_textOutput
= false;
342 newID
->_compactAppleMessages
= false;
343 newID
->_includeHashes
= false;
345 newID
->_timeToRoll
= 0;
346 newID
->_stallingTheirRoll
= false;
347 newID
->_stallSeconds
= 0;
348 newID
->_missedAck
= true;
349 newID
->_receivedAck
= false;
351 SecOTRSessionResetInternal(newID
);
354 SecOTRPublicIdentityRef myPublicID
= SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault
, newID
->_me
, NULL
);
355 SecOTRPIPerformWithSerializationString(myPublicID
, ^(CFStringRef myIDString
) {
356 SecOTRPIPerformWithSerializationString(newID
->_them
, ^(CFStringRef theirIDString
) {
357 secnotice("otr", "%@ Creating with M: %@, T: %@", newID
, myIDString
, theirIDString
);
360 CFReleaseNull(myPublicID
);
366 SecOTRSessionRef
SecOTRSessionCreateFromIDAndFlags(CFAllocatorRef allocator
,
367 SecOTRFullIdentityRef myID
,
368 SecOTRPublicIdentityRef theirID
,
372 uint64_t seconds
= SecOTRGetDefaultsWriteSeconds();
374 SecOTRSessionRef newID
= SecOTRSessionCreateFromID(allocator
, myID
, theirID
);
375 if (flags
& kSecOTRSendTextMessages
) {
376 newID
->_textOutput
= true;
378 if (flags
& kSecOTRUseAppleCustomMessageFormat
) {
379 newID
->_compactAppleMessages
= true;
381 if(flags
& kSecOTRIncludeHashesInMessages
)
383 newID
->_includeHashes
= true;
385 if(flags
& kSecOTRSlowRoll
)
387 newID
->_stallSeconds
= seconds
;
393 static uint64_t constant_zero
= 0;
395 static bool hashIsZero(uint8_t hash
[CCSHA1_OUTPUT_SIZE
])
398 for(size_t byte
= 0; isZero
&& byte
< CCSHA1_OUTPUT_SIZE
; ++byte
)
399 isZero
= (0 == hash
[byte
]);
404 static bool SOSOTRSCacheEntryIsEmpty(SecOTRCacheElement
*element
)
406 return hashIsZero(element
->_fullKeyHash
) && hashIsZero(element
->_publicKeyHash
);
411 static void WithCacheDescription(SecOTRSessionRef session
, void (^operation
)(CFStringRef cacheDescription
)) {
412 CFStringRef description
= NULL
;
414 CFStringRef keyCache0Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[0]);
415 CFStringRef keyCache1Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[1]);
416 CFStringRef keyCache2Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[2]);
417 CFStringRef keyCache3Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[3]);
419 description
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("{%@, %@, %@, %@}"), keyCache0Description
, keyCache1Description
, keyCache2Description
, keyCache3Description
);
421 operation(description
);
423 CFReleaseNull(keyCache0Description
);
424 CFReleaseNull(keyCache1Description
);
425 CFReleaseNull(keyCache2Description
);
426 CFReleaseNull(keyCache3Description
);
427 CFReleaseNull(description
);
432 static void SecOTRSFindKeysForMessage(SecOTRSessionRef session
,
433 SecOTRFullDHKeyRef myKey
,
434 SecOTRPublicDHKeyRef theirKey
,
436 uint8_t** messageKey
, uint8_t** macKey
, uint64_t **counter
)
438 SecOTRCacheElement
* emptyKeys
= NULL
;
439 SecOTRCacheElement
* cachedKeys
= NULL
;
441 int emptyPosition
= kOTRKeyCacheSize
;
444 if ((NULL
== myKey
) || (NULL
== theirKey
)) {
450 *counter
= &constant_zero
;
455 for(int i
= 0; i
< kOTRKeyCacheSize
; ++i
)
457 if (0 == timingsafe_bcmp(session
->_keyCache
[i
]._fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
)
458 && (0 == timingsafe_bcmp(session
->_keyCache
[i
]._publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
))) {
459 cachedKeys
= &session
->_keyCache
[i
];
461 secdebug("OTR","session@[%p] found key match: mk: %@, tk: %@", session
, myKey
, theirKey
);
466 if (emptyKeys
== NULL
&& SOSOTRSCacheEntryIsEmpty(&(session
->_keyCache
[i
]))) {
471 emptyKeys
= &session
->_keyCache
[i
];
475 if (cachedKeys
== NULL
) {
476 if (emptyKeys
== NULL
) {
478 WithCacheDescription(session
, ^(CFStringRef cacheDescription
) {
479 secdebug("OTR","session@[%p] Cache miss, spooky for mk: %@, tk: %@ cache: %@", session
, myKey
, theirKey
, cacheDescription
);
484 emptyKeys
= &session
->_keyCache
[0];
489 // Fill in the entry.
490 memcpy(emptyKeys
->_fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
);
491 memcpy(emptyKeys
->_publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
);
493 emptyKeys
->_counter
= 0;
494 emptyKeys
->_theirCounter
= 0;
496 SecOTRDHKGenerateOTRKeys(myKey
, theirKey
,
497 emptyKeys
->_sendEncryptionKey
, emptyKeys
->_sendMacKey
,
498 emptyKeys
->_receiveEncryptionKey
, emptyKeys
->_receiveMacKey
);
500 cachedKeys
= emptyKeys
;
502 WithCacheDescription(session
, ^(CFStringRef cacheDescription
) {
503 secdebug("OTR","mk %@, th: %@ session@[%p] new key cache state added key@[%d]: %@", myKey
, theirKey
, session
, emptyPosition
, cacheDescription
);
510 *messageKey
= sending
? cachedKeys
->_sendEncryptionKey
: cachedKeys
->_receiveEncryptionKey
;
512 *macKey
= sending
? cachedKeys
->_sendMacKey
: cachedKeys
->_receiveMacKey
;
514 *counter
= sending
? &cachedKeys
->_counter
: &cachedKeys
->_theirCounter
;
517 SecOTRSessionRef
SecOTRSessionCreateFromData(CFAllocatorRef allocator
, CFDataRef data
)
522 SecOTRSessionRef result
= NULL
;
523 SecOTRSessionRef session
= CFTypeAllocate(SecOTRSession
, struct _SecOTRSession
, allocator
);
525 uint8_t numberOfKeys
;
528 const uint8_t *bytes
= CFDataGetBytePtr(data
);
529 size_t size
= (size_t)CFDataGetLength(data
);
531 (void)SecOTRGetDefaultsWriteSeconds();
533 session
->_queue
= dispatch_queue_create("OTRSession", DISPATCH_QUEUE_SERIAL
);
536 session
->_them
= NULL
;
537 session
->_myKey
= NULL
;
538 session
->_myNextKey
= NULL
;
539 session
->_theirKey
= NULL
;
540 session
->_theirPreviousKey
= NULL
;
541 session
->_receivedDHMessage
= NULL
;
542 session
->_receivedDHKeyMessage
= NULL
;
543 session
->_textOutput
= false;
544 session
->_compactAppleMessages
= false;
545 session
->_timeToRoll
= 0;
546 session
->_stallingTheirRoll
= false;
547 session
->_stallSeconds
= 0;
548 session
->_missedAck
= true;
549 session
->_receivedAck
= false;
551 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
554 require_noerr(ReadByte(&bytes
, &size
, &version
), fail
);
555 require(version
<= 6, fail
);
557 require_noerr(ReadLong(&bytes
, &size
, &session
->_state
), fail
);
558 session
->_me
= SecOTRFullIdentityCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
, NULL
);
559 require(session
->_me
!= NULL
, fail
);
560 session
->_them
= SecOTRPublicIdentityCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
, NULL
);
561 require(session
->_them
!= NULL
, fail
);
563 require(size
> sizeof(session
->_r
), fail
);
564 memcpy(session
->_r
, bytes
, sizeof(session
->_r
));
565 bytes
+= sizeof(session
->_r
);
566 size
-= sizeof(session
->_r
);
569 uint8_t hasMessage
= false;
570 ReadByte(&bytes
, &size
, &hasMessage
);
572 session
->_receivedDHMessage
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
577 uint8_t hasMessage
= false;
578 ReadByte(&bytes
, &size
, &hasMessage
);
580 session
->_receivedDHKeyMessage
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
586 require_noerr(ReadByte(&bytes
, &size
, &ready
), fail
);
587 if (ready
&& session
->_state
== kIdle
)
588 session
->_state
= kDone
;
592 require_noerr(ReadLong(&bytes
, &size
, &session
->_keyID
), fail
);
593 if (session
->_keyID
> 0) {
594 session
->_myKey
= SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
);
595 require(session
->_myKey
!= NULL
, fail
);
596 session
->_myNextKey
= SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
);
597 require(session
->_myNextKey
!= NULL
, fail
);
601 require_noerr(ReadByte(&bytes
, &size
, &numberOfKeys
), fail
);
603 require_noerr(ReadLong(&bytes
, &size
, &session
->_theirKeyID
), fail
);
605 if (session
->_theirKeyID
> 0) {
606 if (session
->_theirKeyID
> 1) {
607 session
->_theirPreviousKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
608 require(session
->_theirPreviousKey
!= NULL
, fail
);
610 session
->_theirKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
611 require(session
->_theirKey
!= NULL
, fail
);
615 if(numberOfKeys
>= 1){
616 if (numberOfKeys
>= 2) {
617 session
->_theirPreviousKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
618 require(session
->_theirPreviousKey
!= NULL
, fail
);
620 session
->_theirKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
621 require(session
->_theirKey
!= NULL
, fail
);
627 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
628 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
629 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
630 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
631 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
632 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
633 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
634 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
635 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
636 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
637 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
638 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
639 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
640 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
641 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
642 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
644 session
->_macKeysToExpose
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
645 require(session
->_macKeysToExpose
!= NULL
, fail
);
647 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_textOutput
), fail
);
650 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_compactAppleMessages
), fail
);
653 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_includeHashes
), fail
);
656 require_noerr(ReadLongLong(&bytes
, &size
, &session
->_stallSeconds
), fail
);
657 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_stallingTheirRoll
), fail
);
658 require_noerr(ReadLongLong(&bytes
, &size
, &timeToRoll
), fail
);
659 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_missedAck
), fail
);
660 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_receivedAck
), fail
);
661 session
->_timeToRoll
= timeToRoll
;
667 CFReleaseNull(session
);
672 OSStatus
SecOTRSAppendSerialization(SecOTRSessionRef session
, CFMutableDataRef serializeInto
)
674 __block OSStatus result
= errSecParam
;
676 require(session
, abort
);
677 require(serializeInto
, abort
);
679 CFIndex start
= CFDataGetLength(serializeInto
);
681 dispatch_sync(session
->_queue
, ^{
682 const uint8_t version
= 6;
683 uint8_t numberOfKeys
= 0;
684 CFDataAppendBytes(serializeInto
, &version
, sizeof(version
));
686 AppendLong(serializeInto
, session
->_state
);
688 result
= (SecOTRFIAppendSerialization(session
->_me
, serializeInto
, NULL
)) ? errSecSuccess
: errSecParam
;
690 if (result
== errSecSuccess
) {
691 result
= (SecOTRPIAppendSerialization(session
->_them
, serializeInto
, NULL
)) ? errSecSuccess
: errSecParam
;
694 if (result
== errSecSuccess
) {
695 CFDataAppendBytes(serializeInto
, session
->_r
, sizeof(session
->_r
));
697 if (session
->_receivedDHMessage
== NULL
) {
698 AppendByte(serializeInto
, 0);
700 AppendByte(serializeInto
, 1);
701 AppendCFDataAsDATA(serializeInto
, session
->_receivedDHMessage
);
704 if (session
->_receivedDHKeyMessage
== NULL
) {
705 AppendByte(serializeInto
, 0);
707 AppendByte(serializeInto
, 1);
708 AppendCFDataAsDATA(serializeInto
, session
->_receivedDHKeyMessage
);
711 AppendLong(serializeInto
, session
->_keyID
);
712 if (session
->_keyID
> 0) {
713 SecFDHKAppendSerialization(session
->_myKey
, serializeInto
);
714 SecFDHKAppendSerialization(session
->_myNextKey
, serializeInto
);
717 if(session
->_theirPreviousKey
!= NULL
)
719 if(session
->_theirKey
!= NULL
)
722 AppendByte(serializeInto
, numberOfKeys
);
724 AppendLong(serializeInto
, session
->_theirKeyID
);
726 if (session
->_theirPreviousKey
!= NULL
)
727 SecPDHKAppendSerialization(session
->_theirPreviousKey
, serializeInto
);
729 if (session
->_theirKey
!= NULL
)
730 SecPDHKAppendSerialization(session
->_theirKey
, serializeInto
);
734 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
735 AppendLongLong(serializeInto
, *counter
);
736 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
737 AppendLongLong(serializeInto
, *counter
);
738 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
739 AppendLongLong(serializeInto
, *counter
);
740 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
741 AppendLongLong(serializeInto
, *counter
);
742 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
743 AppendLongLong(serializeInto
, *counter
);
744 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
745 AppendLongLong(serializeInto
, *counter
);
746 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
747 AppendLongLong(serializeInto
, *counter
);
748 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
749 AppendLongLong(serializeInto
, *counter
);
751 AppendCFDataAsDATA(serializeInto
, session
->_macKeysToExpose
);
753 AppendByte(serializeInto
, session
->_textOutput
? 1 : 0);
754 AppendByte(serializeInto
, session
->_compactAppleMessages
? 1 : 0);
755 AppendByte(serializeInto
, session
->_includeHashes
? 1 : 0);
757 AppendLongLong(serializeInto
, session
->_stallSeconds
? session
->_stallSeconds
: constant_zero
);
759 AppendByte(serializeInto
, session
->_stallingTheirRoll
? 1 : 0);
760 AppendLongLong(serializeInto
, (uint64_t)session
->_timeToRoll
);
761 AppendByte(serializeInto
, session
->_missedAck
? 1 : 0);
762 AppendByte(serializeInto
, session
->_receivedAck
? 1 : 0);
767 if (result
!= errSecSuccess
)
768 CFDataSetLength(serializeInto
, start
);
775 bool SecOTRSIsForKeys(SecOTRSessionRef session
, SecKeyRef myPublic
, SecKeyRef theirPublic
)
777 __block
bool isForKeys
= false;
779 dispatch_sync(session
->_queue
, ^{
780 isForKeys
= SecOTRFICompareToPublicKey(session
->_me
, myPublic
) &&
781 SecOTRPICompareToPublicKey(session
->_them
, theirPublic
);
787 bool SecOTRSGetIsReadyForMessages(SecOTRSessionRef session
)
791 dispatch_sync(session
->_queue
, ^{ result
= session
->_state
== kDone
; });
796 bool SecOTRSGetIsIdle(SecOTRSessionRef session
)
800 dispatch_sync(session
->_queue
, ^{ result
= session
->_state
== kIdle
; });
805 static void SecOTRSPrecalculateForPair(SecOTRSessionRef session
,
806 SecOTRFullDHKeyRef myKey
,
807 SecOTRPublicDHKeyRef theirKey
)
809 if (myKey
== NULL
|| theirKey
== NULL
)
812 SecOTRSFindKeysForMessage(session
, myKey
, theirKey
, true, NULL
, NULL
, NULL
);
813 SecOTRSFindKeysForMessage(session
, myKey
, theirKey
, false, NULL
, NULL
, NULL
);
816 static void SecOTRSPrecalculateKeysInternal(SecOTRSessionRef session
)
818 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirKey
);
819 SecOTRSPrecalculateForPair(session
, session
->_myNextKey
, session
->_theirKey
);
820 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirPreviousKey
);
821 SecOTRSPrecalculateForPair(session
, session
->_myNextKey
, session
->_theirPreviousKey
);
824 static void SecOTRSPrecalculateNextKeysInternal(SecOTRSessionRef session
)
826 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirKey
);
829 void SecOTRSPrecalculateKeys(SecOTRSessionRef session
)
831 dispatch_sync_f(session
->_queue
, session
, (dispatch_function_t
) SecOTRSPrecalculateKeysInternal
);
834 enum SecOTRSMessageKind
SecOTRSGetMessageKind(SecOTRSessionRef session
, CFDataRef message
)
836 OTRMessageType type
= SecOTRSGetMessageType(message
);
838 enum SecOTRSMessageKind kind
;
842 case kEvenCompactDataMessage
:
843 case kOddCompactDataMessage
:
844 case kEvenCompactDataMessageWithHashes
:
845 case kOddCompactDataMessageWithHashes
:
846 kind
= kOTRDataPacket
;
850 case kRevealSignatureMessage
:
851 case kSignatureMessage
:
852 kind
= kOTRNegotiationPacket
;
854 case kInvalidMessage
:
856 kind
= kOTRUnknownPacket
;
863 static OSStatus
SecOTRSSignAndProtectRaw_locked(SecOTRSessionRef session
,
864 CFDataRef sourceMessage
, CFMutableDataRef destinationMessage
,
865 uint8_t* messageKey
, uint8_t* macKey
, uint64_t* counter
, uint32_t theirKeyID
, SecOTRPublicDHKeyRef theirKey
)
867 CFIndex start
= CFDataGetLength(destinationMessage
);
869 AppendHeader(destinationMessage
, kDataMessage
);
870 AppendByte(destinationMessage
, 0); // Flags, all zero
872 AppendLong(destinationMessage
, session
->_keyID
);
873 AppendLong(destinationMessage
, theirKeyID
);
874 SecFDHKAppendPublicSerialization(session
->_myNextKey
, destinationMessage
);
875 AppendLongLong(destinationMessage
, ++*counter
);
877 CFIndex sourceSize
= CFDataGetLength(sourceMessage
);
878 assert(((unsigned long)sourceSize
)<=UINT32_MAX
); /* this is correct as long as CFIndex is a signed long */
879 AppendLong(destinationMessage
, (uint32_t)sourceSize
);
880 uint8_t* encryptedDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, sourceSize
);
881 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
883 (size_t)sourceSize
, CFDataGetBytePtr(sourceMessage
),
884 encryptedDataPointer
);
886 CFIndex macedContentsSize
= CFDataGetLength(destinationMessage
) - start
;
887 CFIndex macSize
= CCSHA1_OUTPUT_SIZE
;
888 uint8_t* macDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, macSize
);
891 kOTRMessageMacKeyBytes
, macKey
,
892 macedContentsSize
, CFDataGetBytePtr(destinationMessage
) + start
,
895 CFDataAppend(destinationMessage
, session
->_macKeysToExpose
);
897 return errSecSuccess
;
900 const size_t kCompactMessageMACSize
= 16;
902 static OSStatus
SecOTRSSignAndProtectCompact_locked(SecOTRSessionRef session
,
903 CFDataRef sourceMessage
, CFMutableDataRef destinationMessage
,
904 uint8_t* messageKey
, uint8_t* macKey
, uint64_t* counter
, uint32_t theirKeyID
, SecOTRPublicDHKeyRef theirKey
)
906 CFIndex start
= CFDataGetLength(destinationMessage
);
907 bool sendHashes
= session
->_includeHashes
;
909 const uint8_t messageType
= sendHashes
? ((theirKeyID
& 0x1) ? kOddCompactDataMessageWithHashes
: kEvenCompactDataMessageWithHashes
)
910 : ((theirKeyID
& 0x1) ? kOddCompactDataMessage
: kEvenCompactDataMessage
);
912 AppendByte(destinationMessage
, messageType
);
914 SecFDHKAppendCompactPublicSerialization(session
->_myNextKey
, destinationMessage
);
915 AppendLongLongCompact(destinationMessage
, ++*counter
);
917 CFIndex sourceSize
= CFDataGetLength(sourceMessage
);
918 assert(((unsigned long)sourceSize
)<=UINT32_MAX
); /* this is correct as long as CFIndex is a signed long */
919 uint8_t* encryptedDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, sourceSize
);
920 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
922 (size_t)sourceSize
, CFDataGetBytePtr(sourceMessage
),
923 encryptedDataPointer
);
926 uint8_t *senderHashPtr
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, kSecDHKHashSize
);
928 memcpy(senderHashPtr
, SecFDHKGetHash(session
->_myKey
), kSecDHKHashSize
);
930 uint8_t *receiverHashPtr
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, kSecDHKHashSize
);
932 memcpy(receiverHashPtr
, SecPDHKGetHash(theirKey
), kSecDHKHashSize
);
936 CFIndex macedContentsSize
= CFDataGetLength(destinationMessage
) - start
;
937 CFIndex macSize
= CCSHA1_OUTPUT_SIZE
;
938 uint8_t mac
[macSize
];
940 kOTRMessageMacKeyBytes
, macKey
,
941 macedContentsSize
, CFDataGetBytePtr(destinationMessage
) + start
,
944 CFDataAppendBytes(destinationMessage
, mac
, kCompactMessageMACSize
);
946 return errSecSuccess
;
949 OSStatus
SecOTRSSignAndProtectMessage(SecOTRSessionRef session
,
950 CFDataRef sourceMessage
,
951 CFMutableDataRef protectedMessage
)
953 __block OSStatus result
= errSecParam
;
955 require(session
, abort
);
956 require(sourceMessage
, abort
);
957 require(protectedMessage
, abort
);
959 if(session
->_state
!= kDone
){
960 secdebug("OTR", "Cannot sign and protect messages, we are not done negotiating sesion[%p]", session
);
961 require_quiet( session
->_state
== kDone
, abort
);
964 dispatch_sync(session
->_queue
, ^{
965 if (session
->_myKey
== NULL
||
966 session
->_theirKey
== NULL
) {
973 uint32_t theirKeyID
= session
->_theirKeyID
;
975 SecOTRPublicDHKeyRef theirKeyToUse
= session
->_theirKey
;
977 SecOTRSRollIfTime(session
);
979 if(session
->_stallingTheirRoll
&& session
->_theirPreviousKey
){
980 theirKeyToUse
= session
->_theirPreviousKey
;
981 theirKeyID
= session
->_theirKeyID
- 1;
984 SecOTRSFindKeysForMessage(session
, session
->_myKey
, theirKeyToUse
,
986 &messageKey
, &macKey
, &counter
);
987 // The || !protectedMessage below is only here to shut the static analyzer up, the require(protectedMessage, abort) outside the block already ensures this new term is never true.
988 CFMutableDataRef destinationMessage
= session
->_textOutput
|| !protectedMessage
? CFDataCreateMutable(kCFAllocatorDefault
, 0) : CFRetainSafe(protectedMessage
);
990 result
= session
->_compactAppleMessages
? SecOTRSSignAndProtectCompact_locked(session
, sourceMessage
, destinationMessage
, messageKey
, macKey
, counter
, theirKeyID
, theirKeyToUse
)
991 : SecOTRSSignAndProtectRaw_locked(session
, sourceMessage
, destinationMessage
, messageKey
, macKey
, counter
, theirKeyID
, theirKeyToUse
);
993 if (result
== errSecSuccess
) {
994 if (session
->_textOutput
) {
995 SecOTRPrepareOutgoingBytes(destinationMessage
, protectedMessage
);
998 CFDataSetLength(session
->_macKeysToExpose
, 0);
1001 CFReleaseSafe(destinationMessage
);
1003 result
= errSecSuccess
;
1010 void SecOTRSKickTimeToRoll(SecOTRSessionRef session
){
1011 session
->_timeToRoll
= CFAbsoluteTimeGetCurrent();
1014 static void SecOTRAcceptNewRemoteKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef newKey
)
1016 if (session
->_theirPreviousKey
) {
1017 SecOTRSExpireCachedKeysForPublicKey(session
, session
->_theirPreviousKey
);
1020 CFReleaseNull(session
->_theirPreviousKey
);
1021 session
->_theirPreviousKey
= session
->_theirKey
;
1022 session
->_theirKey
= CFRetainSafe(newKey
);
1023 session
->_stallingTheirRoll
= true;
1025 session
->_theirKeyID
+= 1;
1027 SecOTRSEnableTimeToRoll(session
);
1030 OSStatus
SecOTRSetupInitialRemoteKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef CF_CONSUMED initialKey
) {
1032 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
1034 CFReleaseNull(session
->_theirPreviousKey
);
1035 CFAssignRetained(session
->_theirKey
, initialKey
);
1036 session
->_theirKeyID
= 1;
1038 return errSecSuccess
;
1042 /// MARK: SLOW ROLLING
1045 void SOSOTRSRoll(SecOTRSessionRef session
){
1047 session
->_stallingTheirRoll
= false;
1049 //receiving side roll
1050 if(session
->_receivedAck
){
1051 SecOTRGenerateNewProposedKey(session
);
1052 session
->_missedAck
= false;
1053 session
->_receivedAck
= false;
1056 session
->_missedAck
= true;
1060 static OSStatus
SecOTRVerifyAndExposeRaw_locked(SecOTRSessionRef session
,
1061 CFDataRef decodedBytes
,
1062 CFMutableDataRef exposedMessageContents
)
1064 OSStatus result
= errSecDecode
;
1066 SecOTRPublicDHKeyRef newKey
= NULL
;
1067 const uint8_t* bytes
;
1069 SecOTRFullDHKeyRef myKeyForMessage
= NULL
;
1070 SecOTRPublicDHKeyRef theirKeyForMessage
= NULL
;
1071 bytes
= CFDataGetBytePtr(decodedBytes
);
1072 size
= CFDataGetLength(decodedBytes
);
1074 const uint8_t* macDataStart
= bytes
;
1079 require_noerr_quiet(result
= ReadAndVerifyHeader(&bytes
, &size
, kDataMessage
), fail
);
1080 require_action_quiet(size
> 0, fail
, result
= errSecDecode
);
1082 require_noerr_quiet(result
= ReadAndVerifyByte(&bytes
, &size
, 0), fail
); // Flags, always zero
1084 require_noerr_quiet(result
= ReadLong(&bytes
, &size
, &theirID
), fail
);
1086 require_action_quiet(theirID
== session
->_theirKeyID
|| (theirID
== (session
->_theirKeyID
- 1) && session
->_theirPreviousKey
!= NULL
),
1088 result
= ((theirID
+ 1) < session
->_theirKeyID
) ? errSecOTRTooOld
: errSecOTRIDTooNew
);
1090 require_noerr_quiet(result
= ReadLong(&bytes
, &size
, &myID
), fail
);
1092 require_action_quiet(myID
== session
->_keyID
|| (myID
== session
->_keyID
+ 1 && session
->_myNextKey
!= NULL
),
1094 result
= (myID
< session
->_keyID
) ? errSecOTRTooOld
: errSecOTRIDTooNew
);
1097 // Choose appripriate keys for message:
1099 uint8_t *messageKey
;
1101 uint64_t *theirCounter
;
1103 myKeyForMessage
= (myID
== session
->_keyID
) ? session
->_myKey
: session
->_myNextKey
;
1104 theirKeyForMessage
= (theirID
== session
->_theirKeyID
) ? session
->_theirKey
: session
->_theirPreviousKey
;
1106 SecOTRSFindKeysForMessage(session
, myKeyForMessage
, theirKeyForMessage
, false,
1107 &messageKey
, &macKey
, &theirCounter
);
1109 size_t nextKeyMPISize
;
1110 const uint8_t* nextKeyMPIBytes
;
1111 require_noerr_quiet(result
= SizeAndSkipMPI(&bytes
, &size
, &nextKeyMPIBytes
, &nextKeyMPISize
), fail
);
1114 require_noerr_quiet(result
= ReadLongLong(&bytes
, &size
, &counter
), fail
);
1115 require_action_quiet(counter
> *theirCounter
, fail
, result
= errSecOTRTooOld
);
1118 const uint8_t* messageStart
;
1119 require_noerr_quiet(result
= SizeAndSkipDATA(&bytes
, &size
, &messageStart
, &messageSize
), fail
);
1121 size_t macDataSize
= (bytes
- macDataStart
) ? (size_t)(bytes
- macDataStart
) : 0;
1122 uint8_t mac
[CCSHA1_OUTPUT_SIZE
];
1123 require_action_quiet(sizeof(mac
) <= size
, fail
, result
= errSecDecode
);
1126 kOTRMessageMacKeyBytes
, macKey
,
1127 macDataSize
, macDataStart
,
1130 require_noerr_action_quiet(timingsafe_bcmp(mac
, bytes
, sizeof(mac
)), fail
, result
= errSecAuthFailed
);
1132 uint8_t* dataSpace
= CFDataIncreaseLengthAndGetMutableBytes(exposedMessageContents
, (CFIndex
)messageSize
);
1134 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
1136 messageSize
, messageStart
,
1139 // Everything is good, accept the meta data.
1140 *theirCounter
= counter
;
1142 newKey
= SecOTRPublicDHKCreateFromBytes(kCFAllocatorDefault
, &nextKeyMPIBytes
, &nextKeyMPISize
);
1145 bool acceptTheirNewKey
= newKey
!= NULL
&& theirID
== session
->_theirKeyID
;
1147 if (acceptTheirNewKey
) {
1148 SecOTRAcceptNewRemoteKey(session
, newKey
);
1151 if (myID
== (session
->_keyID
+ 1)) {
1152 SecOTRSHandleProposalAcknowledge(session
);
1155 SecOTRSRollIfTime(session
);
1157 SecOTRSPrecalculateNextKeysInternal(session
);
1160 if(result
!= errSecSuccess
){
1161 CFDataPerformWithHexString(decodedBytes
, ^(CFStringRef decodedBytesString
) {
1162 SecOTRPublicIdentityRef myPublicID
= SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault
, session
->_me
, NULL
);
1163 SecOTRPIPerformWithSerializationString(myPublicID
, ^(CFStringRef myIDString
) {
1164 SecOTRPIPerformWithSerializationString(session
->_them
, ^(CFStringRef theirIDString
) {
1165 secnotice("OTR","session[%p] failed to decrypt, session: %@, mk: %@, mpk: %@, tpk: %@, tk: %@, chose tktu: %@", session
, session
,
1166 session
->_myKey
, session
->_myNextKey
, session
->_theirPreviousKey
, session
->_theirKey
, theirKeyForMessage
);
1167 secnotice("OTR","session[%p] failed to decrypt, mktu: %@, mpi: %@, tpi: %@, m: %@", session
, myKeyForMessage
, myIDString
, theirIDString
, decodedBytesString
);
1170 CFReleaseNull(myPublicID
);
1173 CFReleaseNull(newKey
);
1177 static OSStatus
SecOTRVerifyAndExposeRawCompact_locked(SecOTRSessionRef session
,
1178 CFDataRef decodedBytes
,
1179 CFMutableDataRef exposedMessageContents
)
1181 SecOTRPublicDHKeyRef theirProposal
= NULL
;
1182 OSStatus result
= errSecDecode
;
1183 const uint8_t* bytes
;
1185 SecOTRPublicDHKeyRef theirKeyForMessage
= NULL
;
1186 bytes
= CFDataGetBytePtr(decodedBytes
);
1187 size
= CFDataGetLength(decodedBytes
);
1188 SecOTRFullDHKeyRef myKeyForMessage
= NULL
;
1189 const uint8_t* macDataStart
= bytes
;
1190 bool useEvenKey
= false;
1191 bool useCurrentKey
= false;
1192 bool sentHashes
= false;
1193 uint64_t counter
= 0;
1195 uint8_t type_byte
= 0;
1196 require_noerr_quiet(result
= ReadByte(&bytes
, &size
, &type_byte
), fail
);
1197 require_action_quiet(type_byte
== kOddCompactDataMessage
|| type_byte
== kEvenCompactDataMessage
1198 || type_byte
== kOddCompactDataMessageWithHashes
|| type_byte
== kEvenCompactDataMessageWithHashes
, fail
, result
= errSecDecode
);
1200 useEvenKey
= (type_byte
== kEvenCompactDataMessage
|| type_byte
== kEvenCompactDataMessageWithHashes
);
1201 sentHashes
= (type_byte
== kOddCompactDataMessageWithHashes
|| type_byte
== kEvenCompactDataMessageWithHashes
);
1203 useCurrentKey
= useEvenKey
^ (session
->_keyID
& 1);
1204 myKeyForMessage
= useCurrentKey
? session
->_myKey
: session
->_myNextKey
;
1206 require_action_quiet(myKeyForMessage
, fail
, result
= errSecDecode
);
1208 theirProposal
= SecOTRPublicDHKCreateFromCompactSerialization(kCFAllocatorDefault
, &bytes
, &size
);
1210 require_action_quiet(theirProposal
, fail
, result
= errSecDecode
);
1212 bool proposalIsNew
= !CFEqualSafe(theirProposal
, session
->_theirKey
);
1213 theirKeyForMessage
= proposalIsNew
? session
->_theirKey
: session
->_theirPreviousKey
;
1215 require_action_quiet(theirKeyForMessage
, fail
, result
= errSecDecode
);
1217 uint8_t *messageKey
;
1219 uint64_t *theirCounter
;
1222 SecOTRSFindKeysForMessage(session
, myKeyForMessage
, theirKeyForMessage
, false, &messageKey
, &macKey
, &theirCounter
);
1224 require_noerr_quiet(result
= ReadLongLongCompact(&bytes
, &size
, &counter
), fail
);
1225 require_action_quiet(counter
> *theirCounter
, fail
, result
= errSecOTRTooOld
);
1227 size_t messageSize
= size
- kCompactMessageMACSize
- (sentHashes
? 2 * kSecDHKHashSize
: 0); // It's all message except for the MAC and maybe hashes
1228 const uint8_t* messageStart
= bytes
;
1230 bytes
+= messageSize
;
1231 size
-= messageSize
;
1234 // Sender then receiver keys
1236 if (memcmp(SecPDHKGetHash(theirKeyForMessage
), bytes
, kSecDHKHashSize
) != 0) {
1237 // Wrong sender key WTF.
1239 BufferPerformWithHexString(bytes
, kSecDHKHashSize
, ^(CFStringRef dataString
) {
1240 secdebug("OTR","session[%p] Sender key hash doesn't match: %@ != %@", session
, theirKeyForMessage
, dataString
);
1245 bytes
+= kSecDHKHashSize
;
1246 size
-= kSecDHKHashSize
;
1248 if (memcmp(SecFDHKGetHash(myKeyForMessage
), bytes
, kSecDHKHashSize
) != 0) {
1249 // Wrong sender key WTF.
1251 BufferPerformWithHexString(bytes
, kSecDHKHashSize
, ^(CFStringRef dataString
) {
1252 secdebug("OTR","session[%p] Receiver key hash doesn't match: %@ != %@", session
, myKeyForMessage
, dataString
);
1257 bytes
+= kSecDHKHashSize
;
1258 size
-= kSecDHKHashSize
;
1262 uint8_t mac
[CCSHA1_OUTPUT_SIZE
];
1263 require_action_quiet(kCompactMessageMACSize
== size
, fail
, result
= errSecDecode
); // require space for the mac and some bytes
1265 size_t macDataSize
= (size_t)(bytes
- macDataStart
);
1268 kOTRMessageMacKeyBytes
, macKey
,
1269 macDataSize
, macDataStart
,
1272 require_noerr_action_quiet(timingsafe_bcmp(mac
, bytes
, kCompactMessageMACSize
), fail
, result
= errSecAuthFailed
);
1274 uint8_t* dataSpace
= CFDataIncreaseLengthAndGetMutableBytes(exposedMessageContents
, (CFIndex
)messageSize
);
1276 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
1278 messageSize
, messageStart
,
1281 // Everything is good, accept the meta data.
1282 *theirCounter
= counter
;
1284 if (proposalIsNew
) {
1285 SecOTRAcceptNewRemoteKey(session
, theirProposal
);
1288 if (!useCurrentKey
) {
1289 SecOTRSHandleProposalAcknowledge(session
);
1291 SecOTRSRollIfTime(session
);
1293 SecOTRSPrecalculateNextKeysInternal(session
);
1296 if(result
!= errSecSuccess
){
1297 CFDataPerformWithHexString(decodedBytes
, ^(CFStringRef decodedBytesString
) {
1298 SecOTRPublicIdentityRef myPublicID
= SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault
, session
->_me
, NULL
);
1299 SecOTRPIPerformWithSerializationString(myPublicID
, ^(CFStringRef myIDString
) {
1300 SecOTRPIPerformWithSerializationString(session
->_them
, ^(CFStringRef theirIDString
) {
1301 secnotice("OTR","session[%p] failed to decrypt, session: %@, mk: %@, mpk: %@, tpk: %@, tk: %@, chose tktu: %@", session
, session
,
1302 session
->_myKey
, session
->_myNextKey
, session
->_theirPreviousKey
, session
->_theirKey
, theirKeyForMessage
);
1303 secnotice("OTR","session[%p] failed to decrypt, mktu: %@, mpi: %@, tpi: %@, m: %@, tP: %@, tb: %hhx", session
, myKeyForMessage
, myIDString
, theirIDString
, decodedBytesString
, theirProposal
, type_byte
);
1306 CFReleaseNull(myPublicID
);
1309 CFReleaseNull(theirProposal
);
1314 OSStatus
SecOTRSVerifyAndExposeMessage(SecOTRSessionRef session
,
1315 CFDataRef incomingMessage
,
1316 CFMutableDataRef exposedMessageContents
)
1318 __block OSStatus result
= errSecParam
;
1321 require(session
, abort
);
1322 require(incomingMessage
, abort
);
1323 require(exposedMessageContents
, abort
);
1325 if(session
->_state
== kDone
){
1326 dispatch_sync(session
->_queue
, ^{
1327 CFDataRef decodedBytes
= SecOTRCopyIncomingBytes(incomingMessage
);
1329 OTRMessageType messageType
= SecOTRSGetMessageType(decodedBytes
);
1331 switch (messageType
) {
1333 result
= SecOTRVerifyAndExposeRaw_locked(session
, decodedBytes
, exposedMessageContents
);
1336 case kOddCompactDataMessage
:
1337 case kEvenCompactDataMessage
:
1338 case kOddCompactDataMessageWithHashes
:
1339 case kEvenCompactDataMessageWithHashes
:
1340 result
= SecOTRVerifyAndExposeRawCompact_locked(session
, decodedBytes
, exposedMessageContents
);
1344 result
= errSecUnsupportedFormat
;
1348 CFReleaseSafe(decodedBytes
);
1352 secnotice("OTR", "session[%p]Cannot process message:%@, session is not done negotiating, session state: %@", session
, incomingMessage
, session
);
1353 result
= errSecOTRNotReady
;
1360 OSStatus
SecOTRSEndSession(SecOTRSessionRef session
,
1361 CFMutableDataRef messageToSend
)
1363 return errSecUnimplemented
;
1366 static CFDataRef
data_to_data_error_request(enum SecXPCOperation op
, CFDataRef publicPeerId
, CFErrorRef
*error
) {
1367 __block CFDataRef result
= NULL
;
1368 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1369 return SecXPCDictionarySetDataOptional(message
, kSecXPCPublicPeerId
, publicPeerId
, error
);
1370 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1371 return (result
= SecXPCDictionaryCopyData(response
, kSecXPCKeyResult
, error
));
1376 static bool data_data_to_data_data_bool_error_request(enum SecXPCOperation op
, CFDataRef sessionData
, CFDataRef inputPacket
, CFDataRef
* outputSessionData
, CFDataRef
* outputPacket
, bool *readyForMessages
, CFErrorRef
*error
) {
1377 __block CFDataRef tempOutputSessionData
= NULL
;
1378 __block CFDataRef tempOutputPacket
= NULL
;
1379 __block
bool tempReadyForMessages
= false;
1381 bool result
= securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1382 return SecXPCDictionarySetDataOptional(message
, kSecXPCOTRSession
, sessionData
, error
)
1383 && SecXPCDictionarySetDataOptional(message
, kSecXPCData
, inputPacket
, error
);
1384 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1385 if (xpc_dictionary_get_bool(response
, kSecXPCKeyResult
)) {
1386 tempOutputSessionData
= SecXPCDictionaryCopyData(response
, kSecXPCOTRSession
, error
);
1387 tempOutputPacket
= SecXPCDictionaryCopyData(response
, kSecXPCData
, error
);
1388 tempReadyForMessages
= xpc_dictionary_get_bool(response
, kSecXPCOTRReady
);
1396 *outputSessionData
= tempOutputSessionData
;
1397 *outputPacket
= tempOutputPacket
;
1398 *readyForMessages
= tempReadyForMessages
;
1404 CFDataRef
SecOTRSessionCreateRemote(CFDataRef publicPeerId
, CFErrorRef
*error
) {
1405 __block CFDataRef result
;
1406 os_activity_initiate("SecOTRSessionCreateRemote", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1407 (void)SecOTRGetDefaultsWriteSeconds();
1408 result
= SECURITYD_XPC(sec_otr_session_create_remote
, data_to_data_error_request
, publicPeerId
, error
);
1413 bool SecOTRSessionProcessPacketRemote(CFDataRef sessionData
, CFDataRef inputPacket
, CFDataRef
* outputSessionData
, CFDataRef
* outputPacket
, bool *readyForMessages
, CFErrorRef
*error
) {
1414 __block
bool result
;
1415 os_activity_initiate("SecOTRSessionProcessPacketRemote", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1416 result
= SECURITYD_XPC(sec_otr_session_process_packet_remote
, data_data_to_data_data_bool_error_request
, sessionData
, inputPacket
, outputSessionData
, outputPacket
, readyForMessages
, error
);