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 "utilities/comparison.h"
28 #include <CoreFoundation/CFDate.h>
30 #include "SecOTRSession.h"
32 #include "SecOTRMath.h"
33 #include "SecOTRDHKey.h"
34 #include "SecOTRSessionPriv.h"
35 #include "SecOTRPackets.h"
36 #include "SecOTRPacketData.h"
37 #include "SecOTRIdentityPriv.h"
39 #include <utilities/SecCFWrappers.h>
41 #include <CoreFoundation/CFRuntime.h>
42 #include <CoreFoundation/CFString.h>
44 #include <Security/SecBasePriv.h>
45 #include <Security/SecRandom.h>
46 #include <Security/SecBase64.h>
47 #include <Security/SecKeyPriv.h>
49 #include <Security/SecureObjectSync/SOSPeerInfo.h>
50 #include <Security/SecureObjectSync/SOSCircle.h>
51 #include <Security/SecureObjectSync/SOSCloudCircle.h>
52 #include <Security/SecureObjectSync/SOSInternal.h>
53 #include <Security/SecureObjectSync/SOSUserKeygen.h>
55 #include <AssertMacros.h>
57 #include <corecrypto/cchmac.h>
58 #include <corecrypto/ccsha2.h>
59 #include <corecrypto/ccsha1.h>
65 #include <os/activity.h>
67 #include <utilities/array_size.h>
69 #include <ipc/securityd_client.h>
70 #include <Security/SecuritydXPC.h>
72 CFGiblisFor(SecOTRSession
);
74 static uint64_t setup_defaults_settings(){
76 Boolean keyExistsAndHasValue
= false;
78 seconds
= CFPreferencesGetAppIntegerValue(CFSTR("OTR"), CFSTR("com.apple.security"), &keyExistsAndHasValue
);
79 secdebug("OTR", "Retrieving OTR default settings was success? %d value retrieved: %llu", keyExistsAndHasValue
, seconds
);
80 return keyExistsAndHasValue
? seconds
: (kSecondsPerMinute
* 15); //15 minutes by default
83 static uint64_t SecOTRGetDefaultsWriteSeconds(void) {
84 static dispatch_once_t sdOnceToken
;
85 static uint64_t seconds
;
87 dispatch_once(&sdOnceToken
, ^{
88 seconds
= setup_defaults_settings();
94 static void SecOTRSEnableTimeToRoll(SecOTRSessionRef session
){
95 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
96 CFAbsoluteTime nextTimeToRoll
= now
+ session
->_stallSeconds
;
98 if(session
->_timeToRoll
== 0 || session
->_timeToRoll
> nextTimeToRoll
){
99 session
->_timeToRoll
= nextTimeToRoll
;
103 static void SecOTRSExpireCachedKeysForFullKey(SecOTRSessionRef session
, SecOTRFullDHKeyRef myKey
)
105 for(int i
= 0; i
< kOTRKeyCacheSize
; ++i
)
107 if (0 == constant_memcmp(session
->_keyCache
[i
]._fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
)) {
108 CFDataAppendBytes(session
->_macKeysToExpose
, session
->_keyCache
[i
]._receiveMacKey
, sizeof(session
->_keyCache
[i
]._receiveMacKey
));
109 bzero(&session
->_keyCache
[i
], sizeof(session
->_keyCache
[i
]));
114 static void SecOTRSExpireCachedKeysForPublicKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef theirKey
)
116 for(int i
= 0; i
< kOTRKeyCacheSize
; ++i
)
118 if (0 == constant_memcmp(session
->_keyCache
[i
]._publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
)) {
119 CFDataAppendBytes(session
->_macKeysToExpose
, session
->_keyCache
[i
]._receiveMacKey
, sizeof(session
->_keyCache
[i
]._receiveMacKey
));
121 bzero(&session
->_keyCache
[i
], sizeof(session
->_keyCache
[i
]));
126 static void SecOTRGenerateNewProposedKey(SecOTRSessionRef session
)
128 SecOTRSExpireCachedKeysForFullKey(session
, session
->_myKey
);
130 // Swap the keys so we know the current key.
132 SecOTRFullDHKeyRef oldKey
= session
->_myKey
;
133 session
->_myKey
= session
->_myNextKey
;
134 session
->_myNextKey
= oldKey
;
137 // Derive a new next key by regenerating over the old key.
138 SecFDHKNewKey(session
->_myNextKey
);
140 session
->_keyID
+= 1;
144 static void SecOTRSHandleProposalAcknowledge(SecOTRSessionRef session
){
145 if(session
->_missedAck
){
146 SecOTRGenerateNewProposedKey(session
);
147 session
->_missedAck
= false;
150 session
->_receivedAck
= true;
151 SecOTRSEnableTimeToRoll(session
);
155 static void SecOTRSRollIfTime(SecOTRSessionRef session
){
157 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
158 CFAbsoluteTime longestTimeToRoll
= now
+ session
->_stallSeconds
;
160 //in case time to roll becomes too large we're going to roll now!
161 if(session
->_timeToRoll
< now
|| session
->_timeToRoll
> longestTimeToRoll
){
162 SOSOTRSRoll(session
);
163 session
->_timeToRoll
= 0;
168 static OTRMessageType
SecOTRSGetMessageType(CFDataRef message
)
170 OTRMessageType type
= kInvalidMessage
;
172 CFDataRef decodedBytes
= SecOTRCopyIncomingBytes(message
);
174 const uint8_t *bytes
= CFDataGetBytePtr(decodedBytes
);
175 size_t size
= CFDataGetLength(decodedBytes
);
177 if (noErr
!= ReadHeader(&bytes
, &size
, &type
)) {
178 uint8_t firstByte
= *CFDataGetBytePtr(decodedBytes
);
180 case kOddCompactDataMessage
:
181 case kEvenCompactDataMessage
:
182 case kOddCompactDataMessageWithHashes
:
183 case kEvenCompactDataMessageWithHashes
:
192 CFReleaseNull(decodedBytes
);
199 static CFStringRef
SecOTRCacheElementCopyDescription(SecOTRCacheElement
*keyCache
){
200 __block CFStringRef description
= NULL
;
201 BufferPerformWithHexString(keyCache
->_fullKeyHash
, sizeof(keyCache
->_fullKeyHash
), ^(CFStringRef fullKeyHashString
) {
202 BufferPerformWithHexString(keyCache
->_publicKeyHash
,sizeof(keyCache
->_publicKeyHash
), ^(CFStringRef publicKeyHashString
) {
203 description
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("fkh: [%@], pkh: [%@], c: %llu tc: %llu"), fullKeyHashString
, publicKeyHashString
, keyCache
->_counter
, keyCache
->_theirCounter
);
210 const char *SecOTRPacketTypeString(CFDataRef message
)
212 if (!message
) return "NoMessage";
213 switch (SecOTRSGetMessageType(message
)) {
214 case kDHMessage
: return "DHMessage (0x02)";
215 case kDataMessage
: return "DataMessage (0x03)";
216 case kDHKeyMessage
: return "DHKeyMessage (0x0A)";
217 case kRevealSignatureMessage
: return "RevealSignatureMessage (0x11)";
218 case kSignatureMessage
: return "SignatureMessage (0x12)";
219 case kEvenCompactDataMessage
: return "kEvenCompactDatamessage (0x20)";
220 case kOddCompactDataMessage
: return "kOddCompactDataMessage (0x21)";
221 case kEvenCompactDataMessageWithHashes
: return "kEvenCompactDatamessage (0x30)";
222 case kOddCompactDataMessageWithHashes
: return "kOddCompactDataMessage (0x31)";
223 case kInvalidMessage
: return "InvalidMessage (0xFF)";
224 default: return "UnknownMessage";
228 static const char *SecOTRAuthStateString(SecOTRAuthState authState
)
231 case kIdle
: return "Idle";
232 case kAwaitingDHKey
: return "AwaitingDHKey";
233 case kAwaitingRevealSignature
: return "AwaitingRevealSignature";
234 case kAwaitingSignature
: return "AwaitingSignature";
235 case kDone
: return "Done";
236 default: return "InvalidState";
240 static CF_RETURNS_RETAINED CFStringRef
SecOTRSessionCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
241 SecOTRSessionRef session
= (SecOTRSessionRef
)cf
;
243 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
245 return CFStringCreateWithFormat(kCFAllocatorDefault
,NULL
,CFSTR("<%s %s %s %s%s%s%s %d:%d %s%s %llu %s%s%s%s>"),
246 SecOTRAuthStateString(session
->_state
),
247 session
->_compactAppleMessages
? "C" :"c",
248 session
->_includeHashes
? "I" : "i",
249 session
->_me
? "F" : "f",
250 session
->_them
? "P" : "p",
251 session
->_receivedDHMessage
? "D" : "d",
252 session
->_receivedDHKeyMessage
? "K" : "k",
254 session
->_theirKeyID
,
255 session
->_theirPreviousKey
? "P" : "p",
256 session
->_theirKey
? "T" : "t",
257 session
->_stallSeconds
,
258 session
->_missedAck
? "M" : "m",
259 session
->_receivedAck
? "R" : "r",
260 session
->_stallingTheirRoll
? "S" : "s",
261 (session
->_timeToRoll
> now
&& session
->_timeToRoll
!= 0) ? "E" : "e");
264 static void SecOTRSessionDestroy(CFTypeRef cf
) {
265 SecOTRSessionRef session
= (SecOTRSessionRef
)cf
;
267 CFReleaseNull(session
->_receivedDHMessage
);
268 CFReleaseNull(session
->_receivedDHKeyMessage
);
270 CFReleaseNull(session
->_me
);
271 CFReleaseNull(session
->_myKey
);
272 CFReleaseNull(session
->_myNextKey
);
274 CFReleaseNull(session
->_them
);
275 CFReleaseNull(session
->_theirKey
);
276 CFReleaseNull(session
->_theirPreviousKey
);
278 CFReleaseNull(session
->_macKeysToExpose
);
280 dispatch_release(session
->_queue
);
283 static void SecOTRSessionResetInternal(SecOTRSessionRef session
)
285 session
->_state
= kIdle
;
287 CFReleaseNull(session
->_receivedDHMessage
);
288 CFReleaseNull(session
->_receivedDHKeyMessage
);
291 CFReleaseNull(session
->_myKey
);
292 CFReleaseNull(session
->_myNextKey
);
293 //session->_myNextKey = SecOTRFullDHKCreate(kCFAllocatorDefault);
294 session
->_theirKeyID
= 0;
295 CFReleaseNull(session
->_theirKey
);
296 CFReleaseNull(session
->_theirPreviousKey
);
297 CFReleaseNull(session
->_macKeysToExpose
);
298 session
->_macKeysToExpose
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
300 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
303 int SecOTRSGetKeyID(SecOTRSessionRef session
){
304 return session
->_keyID
;
307 int SecOTRSGetTheirKeyID(SecOTRSessionRef session
){
308 return session
->_theirKeyID
;
311 void SecOTRSessionReset(SecOTRSessionRef session
)
313 dispatch_sync_f(session
->_queue
, session
, (dispatch_function_t
) SecOTRSessionResetInternal
);
317 SecOTRSessionRef
SecOTRSessionCreateFromID(CFAllocatorRef allocator
,
318 SecOTRFullIdentityRef myID
,
319 SecOTRPublicIdentityRef theirID
)
321 SecOTRSessionRef newID
= CFTypeAllocate(SecOTRSession
, struct _SecOTRSession
, allocator
);
323 (void)SecOTRGetDefaultsWriteSeconds();
324 newID
->_queue
= dispatch_queue_create("OTRSession", DISPATCH_QUEUE_SERIAL
);
327 newID
->_them
= theirID
;
328 newID
->_receivedDHMessage
= NULL
;
329 newID
->_receivedDHKeyMessage
= NULL
;
330 newID
->_myKey
= NULL
;
331 newID
->_myNextKey
= NULL
;
332 newID
->_theirKey
= NULL
;
333 newID
->_theirPreviousKey
= NULL
;
334 newID
->_macKeysToExpose
= NULL
;
335 newID
->_textOutput
= false;
336 newID
->_compactAppleMessages
= false;
337 newID
->_includeHashes
= false;
339 newID
->_timeToRoll
= 0;
340 newID
->_stallingTheirRoll
= false;
341 newID
->_stallSeconds
= 0;
342 newID
->_missedAck
= true;
343 newID
->_receivedAck
= false;
345 SecOTRSessionResetInternal(newID
);
347 CFRetain(newID
->_me
);
348 CFRetain(newID
->_them
);
353 SecOTRSessionRef
SecOTRSessionCreateFromIDAndFlags(CFAllocatorRef allocator
,
354 SecOTRFullIdentityRef myID
,
355 SecOTRPublicIdentityRef theirID
,
359 uint64_t seconds
= SecOTRGetDefaultsWriteSeconds();
361 SecOTRSessionRef newID
= SecOTRSessionCreateFromID(allocator
, myID
, theirID
);
362 if (flags
& kSecOTRSendTextMessages
) {
363 newID
->_textOutput
= true;
365 if (flags
& kSecOTRUseAppleCustomMessageFormat
) {
366 newID
->_compactAppleMessages
= true;
368 if(flags
& kSecOTRIncludeHashesInMessages
)
370 newID
->_includeHashes
= true;
372 if(flags
& kSecOTRSlowRoll
)
374 newID
->_stallSeconds
= seconds
;
380 static uint64_t constant_zero
= 0;
382 static bool hashIsZero(uint8_t hash
[CCSHA1_OUTPUT_SIZE
])
385 for(size_t byte
= 0; isZero
&& byte
< CCSHA1_OUTPUT_SIZE
; ++byte
)
386 isZero
= (0 == hash
[byte
]);
391 static bool SOSOTRSCacheEntryIsEmpty(SecOTRCacheElement
*element
)
393 return hashIsZero(element
->_fullKeyHash
) && hashIsZero(element
->_publicKeyHash
);
398 static void WithCacheDescription(SecOTRSessionRef session
, void (^operation
)(CFStringRef cacheDescription
)) {
399 CFStringRef description
= NULL
;
401 CFStringRef keyCache0Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[0]);
402 CFStringRef keyCache1Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[1]);
403 CFStringRef keyCache2Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[2]);
404 CFStringRef keyCache3Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[3]);
406 description
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("{%@, %@, %@, %@}"), keyCache0Description
, keyCache1Description
, keyCache2Description
, keyCache3Description
);
408 operation(description
);
410 CFReleaseNull(keyCache0Description
);
411 CFReleaseNull(keyCache1Description
);
412 CFReleaseNull(keyCache2Description
);
413 CFReleaseNull(keyCache3Description
);
414 CFReleaseNull(description
);
419 static void SecOTRSFindKeysForMessage(SecOTRSessionRef session
,
420 SecOTRFullDHKeyRef myKey
,
421 SecOTRPublicDHKeyRef theirKey
,
423 uint8_t** messageKey
, uint8_t** macKey
, uint64_t **counter
)
425 SecOTRCacheElement
* emptyKeys
= NULL
;
426 SecOTRCacheElement
* cachedKeys
= NULL
;
428 int emptyPosition
= kOTRKeyCacheSize
;
431 if ((NULL
== myKey
) || (NULL
== theirKey
)) {
437 *counter
= &constant_zero
;
442 for(int i
= 0; i
< kOTRKeyCacheSize
; ++i
)
444 if (0 == constant_memcmp(session
->_keyCache
[i
]._fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
)
445 && (0 == constant_memcmp(session
->_keyCache
[i
]._publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
))) {
446 cachedKeys
= &session
->_keyCache
[i
];
448 secdebug("OTR","session@[%p] found key match: mk: %@, tk: %@", session
, myKey
, theirKey
);
453 if (emptyKeys
== NULL
&& SOSOTRSCacheEntryIsEmpty(&(session
->_keyCache
[i
]))) {
458 emptyKeys
= &session
->_keyCache
[i
];
462 if (cachedKeys
== NULL
) {
463 if (emptyKeys
== NULL
) {
465 WithCacheDescription(session
, ^(CFStringRef cacheDescription
) {
466 secdebug("OTR","session@[%p] Cache miss, spooky for mk: %@, tk: %@ cache: %@", session
, myKey
, theirKey
, cacheDescription
);
471 emptyKeys
= &session
->_keyCache
[0];
475 // Fill in the entry.
476 memcpy(emptyKeys
->_fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
);
477 memcpy(emptyKeys
->_publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
);
479 emptyKeys
->_counter
= 0;
480 emptyKeys
->_theirCounter
= 0;
482 SecOTRDHKGenerateOTRKeys(myKey
, theirKey
,
483 emptyKeys
->_sendEncryptionKey
, emptyKeys
->_sendMacKey
,
484 emptyKeys
->_receiveEncryptionKey
, emptyKeys
->_receiveMacKey
);
486 cachedKeys
= emptyKeys
;
488 WithCacheDescription(session
, ^(CFStringRef cacheDescription
) {
489 secdebug("OTR","mk %@, th: %@ session@[%p] new key cache state added key@[%d]: %@", myKey
, theirKey
, session
, emptyPosition
, cacheDescription
);
496 *messageKey
= sending
? cachedKeys
->_sendEncryptionKey
: cachedKeys
->_receiveEncryptionKey
;
498 *macKey
= sending
? cachedKeys
->_sendMacKey
: cachedKeys
->_receiveMacKey
;
500 *counter
= sending
? &cachedKeys
->_counter
: &cachedKeys
->_theirCounter
;
503 SecOTRSessionRef
SecOTRSessionCreateFromData(CFAllocatorRef allocator
, CFDataRef data
)
508 SecOTRSessionRef result
= NULL
;
509 SecOTRSessionRef session
= CFTypeAllocate(SecOTRSession
, struct _SecOTRSession
, allocator
);
511 uint8_t numberOfKeys
;
514 const uint8_t *bytes
= CFDataGetBytePtr(data
);
515 size_t size
= (size_t)CFDataGetLength(data
);
517 (void)SecOTRGetDefaultsWriteSeconds();
519 session
->_queue
= dispatch_queue_create("OTRSession", DISPATCH_QUEUE_SERIAL
);
522 session
->_them
= NULL
;
523 session
->_myKey
= NULL
;
524 session
->_myNextKey
= NULL
;
525 session
->_theirKey
= NULL
;
526 session
->_theirPreviousKey
= NULL
;
527 session
->_receivedDHMessage
= NULL
;
528 session
->_receivedDHKeyMessage
= NULL
;
529 session
->_textOutput
= false;
530 session
->_compactAppleMessages
= false;
531 session
->_timeToRoll
= 0;
532 session
->_stallingTheirRoll
= false;
533 session
->_stallSeconds
= 0;
534 session
->_missedAck
= true;
535 session
->_receivedAck
= false;
537 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
540 require_noerr(ReadByte(&bytes
, &size
, &version
), fail
);
541 require(version
<= 6, fail
);
543 require_noerr(ReadLong(&bytes
, &size
, &session
->_state
), fail
);
544 session
->_me
= SecOTRFullIdentityCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
, NULL
);
545 require(session
->_me
!= NULL
, fail
);
546 session
->_them
= SecOTRPublicIdentityCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
, NULL
);
547 require(session
->_them
!= NULL
, fail
);
549 require(size
> sizeof(session
->_r
), fail
);
550 memcpy(session
->_r
, bytes
, sizeof(session
->_r
));
551 bytes
+= sizeof(session
->_r
);
552 size
-= sizeof(session
->_r
);
555 uint8_t hasMessage
= false;
556 ReadByte(&bytes
, &size
, &hasMessage
);
558 session
->_receivedDHMessage
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
563 uint8_t hasMessage
= false;
564 ReadByte(&bytes
, &size
, &hasMessage
);
566 session
->_receivedDHKeyMessage
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
572 require_noerr(ReadByte(&bytes
, &size
, &ready
), fail
);
573 if (ready
&& session
->_state
== kIdle
)
574 session
->_state
= kDone
;
578 require_noerr(ReadLong(&bytes
, &size
, &session
->_keyID
), fail
);
579 if (session
->_keyID
> 0) {
580 session
->_myKey
= SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
);
581 require(session
->_myKey
!= NULL
, fail
);
582 session
->_myNextKey
= SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
);
583 require(session
->_myNextKey
!= NULL
, fail
);
587 require_noerr(ReadByte(&bytes
, &size
, &numberOfKeys
), fail
);
589 require_noerr(ReadLong(&bytes
, &size
, &session
->_theirKeyID
), fail
);
591 if (session
->_theirKeyID
> 0) {
592 if (session
->_theirKeyID
> 1) {
593 session
->_theirPreviousKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
594 require(session
->_theirPreviousKey
!= NULL
, fail
);
596 session
->_theirKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
597 require(session
->_theirKey
!= NULL
, fail
);
601 if(numberOfKeys
>= 1){
602 if (numberOfKeys
>= 2) {
603 session
->_theirPreviousKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
604 require(session
->_theirPreviousKey
!= NULL
, fail
);
606 session
->_theirKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
607 require(session
->_theirKey
!= NULL
, fail
);
613 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
614 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
615 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
616 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
617 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
618 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
619 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
620 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
621 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
622 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
623 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
624 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
625 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
626 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
627 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
628 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
630 session
->_macKeysToExpose
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
631 require(session
->_macKeysToExpose
!= NULL
, fail
);
633 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_textOutput
), fail
);
636 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_compactAppleMessages
), fail
);
639 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_includeHashes
), fail
);
642 require_noerr(ReadLongLong(&bytes
, &size
, &session
->_stallSeconds
), fail
);
643 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_stallingTheirRoll
), fail
);
644 require_noerr(ReadLongLong(&bytes
, &size
, &timeToRoll
), fail
);
645 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_missedAck
), fail
);
646 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_receivedAck
), fail
);
647 session
->_timeToRoll
= timeToRoll
;
653 CFReleaseNull(session
);
658 OSStatus
SecOTRSAppendSerialization(SecOTRSessionRef session
, CFMutableDataRef serializeInto
)
660 __block OSStatus result
= errSecParam
;
662 require(session
, abort
);
663 require(serializeInto
, abort
);
665 CFIndex start
= CFDataGetLength(serializeInto
);
667 dispatch_sync(session
->_queue
, ^{
668 const uint8_t version
= 6;
669 uint8_t numberOfKeys
= 0;
670 CFDataAppendBytes(serializeInto
, &version
, sizeof(version
));
672 AppendLong(serializeInto
, session
->_state
);
674 result
= (SecOTRFIAppendSerialization(session
->_me
, serializeInto
, NULL
)) ? errSecSuccess
: errSecParam
;
676 if (result
== errSecSuccess
) {
677 result
= (SecOTRPIAppendSerialization(session
->_them
, serializeInto
, NULL
)) ? errSecSuccess
: errSecParam
;
680 if (result
== errSecSuccess
) {
681 CFDataAppendBytes(serializeInto
, session
->_r
, sizeof(session
->_r
));
683 if (session
->_receivedDHMessage
== NULL
) {
684 AppendByte(serializeInto
, 0);
686 AppendByte(serializeInto
, 1);
687 AppendCFDataAsDATA(serializeInto
, session
->_receivedDHMessage
);
690 if (session
->_receivedDHKeyMessage
== NULL
) {
691 AppendByte(serializeInto
, 0);
693 AppendByte(serializeInto
, 1);
694 AppendCFDataAsDATA(serializeInto
, session
->_receivedDHKeyMessage
);
697 AppendLong(serializeInto
, session
->_keyID
);
698 if (session
->_keyID
> 0) {
699 SecFDHKAppendSerialization(session
->_myKey
, serializeInto
);
700 SecFDHKAppendSerialization(session
->_myNextKey
, serializeInto
);
703 if(session
->_theirPreviousKey
!= NULL
)
705 if(session
->_theirKey
!= NULL
)
708 AppendByte(serializeInto
, numberOfKeys
);
710 AppendLong(serializeInto
, session
->_theirKeyID
);
712 if (session
->_theirPreviousKey
!= NULL
)
713 SecPDHKAppendSerialization(session
->_theirPreviousKey
, serializeInto
);
715 if (session
->_theirKey
!= NULL
)
716 SecPDHKAppendSerialization(session
->_theirKey
, serializeInto
);
720 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
721 AppendLongLong(serializeInto
, *counter
);
722 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
723 AppendLongLong(serializeInto
, *counter
);
724 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
725 AppendLongLong(serializeInto
, *counter
);
726 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
727 AppendLongLong(serializeInto
, *counter
);
728 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
729 AppendLongLong(serializeInto
, *counter
);
730 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
731 AppendLongLong(serializeInto
, *counter
);
732 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
733 AppendLongLong(serializeInto
, *counter
);
734 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
735 AppendLongLong(serializeInto
, *counter
);
737 AppendCFDataAsDATA(serializeInto
, session
->_macKeysToExpose
);
739 AppendByte(serializeInto
, session
->_textOutput
? 1 : 0);
740 AppendByte(serializeInto
, session
->_compactAppleMessages
? 1 : 0);
741 AppendByte(serializeInto
, session
->_includeHashes
? 1 : 0);
743 AppendLongLong(serializeInto
, session
->_stallSeconds
? session
->_stallSeconds
: constant_zero
);
745 AppendByte(serializeInto
, session
->_stallingTheirRoll
? 1 : 0);
746 AppendLongLong(serializeInto
, (uint64_t)session
->_timeToRoll
);
747 AppendByte(serializeInto
, session
->_missedAck
? 1 : 0);
748 AppendByte(serializeInto
, session
->_receivedAck
? 1 : 0);
753 if (result
!= errSecSuccess
)
754 CFDataSetLength(serializeInto
, start
);
761 bool SecOTRSGetIsReadyForMessages(SecOTRSessionRef session
)
765 dispatch_sync(session
->_queue
, ^{ result
= session
->_state
== kDone
; });
770 bool SecOTRSGetIsIdle(SecOTRSessionRef session
)
774 dispatch_sync(session
->_queue
, ^{ result
= session
->_state
== kIdle
; });
779 static void SecOTRSPrecalculateForPair(SecOTRSessionRef session
,
780 SecOTRFullDHKeyRef myKey
,
781 SecOTRPublicDHKeyRef theirKey
)
783 if (myKey
== NULL
|| theirKey
== NULL
)
786 SecOTRSFindKeysForMessage(session
, myKey
, theirKey
, true, NULL
, NULL
, NULL
);
787 SecOTRSFindKeysForMessage(session
, myKey
, theirKey
, false, NULL
, NULL
, NULL
);
790 static void SecOTRSPrecalculateKeysInternal(SecOTRSessionRef session
)
792 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirKey
);
793 SecOTRSPrecalculateForPair(session
, session
->_myNextKey
, session
->_theirKey
);
794 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirPreviousKey
);
795 SecOTRSPrecalculateForPair(session
, session
->_myNextKey
, session
->_theirPreviousKey
);
798 static void SecOTRSPrecalculateNextKeysInternal(SecOTRSessionRef session
)
800 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirKey
);
803 void SecOTRSPrecalculateKeys(SecOTRSessionRef session
)
805 dispatch_sync_f(session
->_queue
, session
, (dispatch_function_t
) SecOTRSPrecalculateKeysInternal
);
808 enum SecOTRSMessageKind
SecOTRSGetMessageKind(SecOTRSessionRef session
, CFDataRef message
)
810 OTRMessageType type
= SecOTRSGetMessageType(message
);
812 enum SecOTRSMessageKind kind
;
816 case kEvenCompactDataMessage
:
817 case kOddCompactDataMessage
:
818 case kEvenCompactDataMessageWithHashes
:
819 case kOddCompactDataMessageWithHashes
:
820 kind
= kOTRDataPacket
;
824 case kRevealSignatureMessage
:
825 case kSignatureMessage
:
826 kind
= kOTRNegotiationPacket
;
828 case kInvalidMessage
:
830 kind
= kOTRUnknownPacket
;
837 static OSStatus
SecOTRSSignAndProtectRaw_locked(SecOTRSessionRef session
,
838 CFDataRef sourceMessage
, CFMutableDataRef destinationMessage
,
839 uint8_t* messageKey
, uint8_t* macKey
, uint64_t* counter
, uint32_t theirKeyID
, SecOTRPublicDHKeyRef theirKey
)
841 CFIndex start
= CFDataGetLength(destinationMessage
);
843 AppendHeader(destinationMessage
, kDataMessage
);
844 AppendByte(destinationMessage
, 0); // Flags, all zero
846 AppendLong(destinationMessage
, session
->_keyID
);
847 AppendLong(destinationMessage
, theirKeyID
);
848 SecFDHKAppendPublicSerialization(session
->_myNextKey
, destinationMessage
);
849 AppendLongLong(destinationMessage
, ++*counter
);
851 CFIndex sourceSize
= CFDataGetLength(sourceMessage
);
852 assert(((unsigned long)sourceSize
)<=UINT32_MAX
); /* this is correct as long as CFIndex is a signed long */
853 AppendLong(destinationMessage
, (uint32_t)sourceSize
);
854 uint8_t* encryptedDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, sourceSize
);
855 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
857 (size_t)sourceSize
, CFDataGetBytePtr(sourceMessage
),
858 encryptedDataPointer
);
860 CFIndex macedContentsSize
= CFDataGetLength(destinationMessage
) - start
;
861 CFIndex macSize
= CCSHA1_OUTPUT_SIZE
;
862 uint8_t* macDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, macSize
);
865 kOTRMessageMacKeyBytes
, macKey
,
866 macedContentsSize
, CFDataGetBytePtr(destinationMessage
) + start
,
869 CFDataAppend(destinationMessage
, session
->_macKeysToExpose
);
871 return errSecSuccess
;
874 const size_t kCompactMessageMACSize
= 16;
876 static OSStatus
SecOTRSSignAndProtectCompact_locked(SecOTRSessionRef session
,
877 CFDataRef sourceMessage
, CFMutableDataRef destinationMessage
,
878 uint8_t* messageKey
, uint8_t* macKey
, uint64_t* counter
, uint32_t theirKeyID
, SecOTRPublicDHKeyRef theirKey
)
880 CFIndex start
= CFDataGetLength(destinationMessage
);
881 bool sendHashes
= session
->_includeHashes
;
883 const uint8_t messageType
= sendHashes
? ((theirKeyID
& 0x1) ? kOddCompactDataMessageWithHashes
: kEvenCompactDataMessageWithHashes
)
884 : ((theirKeyID
& 0x1) ? kOddCompactDataMessage
: kEvenCompactDataMessage
);
886 AppendByte(destinationMessage
, messageType
);
888 SecFDHKAppendCompactPublicSerialization(session
->_myNextKey
, destinationMessage
);
889 AppendLongLongCompact(destinationMessage
, ++*counter
);
891 CFIndex sourceSize
= CFDataGetLength(sourceMessage
);
892 assert(((unsigned long)sourceSize
)<=UINT32_MAX
); /* this is correct as long as CFIndex is a signed long */
893 uint8_t* encryptedDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, sourceSize
);
894 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
896 (size_t)sourceSize
, CFDataGetBytePtr(sourceMessage
),
897 encryptedDataPointer
);
900 uint8_t *senderHashPtr
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, kSecDHKHashSize
);
902 memcpy(senderHashPtr
, SecFDHKGetHash(session
->_myKey
), kSecDHKHashSize
);
904 uint8_t *receiverHashPtr
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, kSecDHKHashSize
);
906 memcpy(receiverHashPtr
, SecPDHKGetHash(theirKey
), kSecDHKHashSize
);
910 CFIndex macedContentsSize
= CFDataGetLength(destinationMessage
) - start
;
911 CFIndex macSize
= CCSHA1_OUTPUT_SIZE
;
912 uint8_t mac
[macSize
];
914 kOTRMessageMacKeyBytes
, macKey
,
915 macedContentsSize
, CFDataGetBytePtr(destinationMessage
) + start
,
918 CFDataAppendBytes(destinationMessage
, mac
, kCompactMessageMACSize
);
920 return errSecSuccess
;
923 OSStatus
SecOTRSSignAndProtectMessage(SecOTRSessionRef session
,
924 CFDataRef sourceMessage
,
925 CFMutableDataRef protectedMessage
)
927 __block OSStatus result
= errSecParam
;
929 require(session
, abort
);
930 require(sourceMessage
, abort
);
931 require(protectedMessage
, abort
);
933 if(session
->_state
!= kDone
){
934 secdebug("OTR", "Cannot sign and protect messages, we are not done negotiating sesion[%p]", session
);
935 require_quiet( session
->_state
== kDone
, abort
);
938 dispatch_sync(session
->_queue
, ^{
939 if (session
->_myKey
== NULL
||
940 session
->_theirKey
== NULL
) {
947 uint32_t theirKeyID
= session
->_theirKeyID
;
949 SecOTRPublicDHKeyRef theirKeyToUse
= session
->_theirKey
;
951 SecOTRSRollIfTime(session
);
953 if(session
->_stallingTheirRoll
&& session
->_theirPreviousKey
){
954 theirKeyToUse
= session
->_theirPreviousKey
;
955 theirKeyID
= session
->_theirKeyID
- 1;
958 SecOTRSFindKeysForMessage(session
, session
->_myKey
, theirKeyToUse
,
960 &messageKey
, &macKey
, &counter
);
961 // 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.
962 CFMutableDataRef destinationMessage
= session
->_textOutput
|| !protectedMessage
? CFDataCreateMutable(kCFAllocatorDefault
, 0) : CFRetainSafe(protectedMessage
);
964 result
= session
->_compactAppleMessages
? SecOTRSSignAndProtectCompact_locked(session
, sourceMessage
, destinationMessage
, messageKey
, macKey
, counter
, theirKeyID
, theirKeyToUse
)
965 : SecOTRSSignAndProtectRaw_locked(session
, sourceMessage
, destinationMessage
, messageKey
, macKey
, counter
, theirKeyID
, theirKeyToUse
);
967 if (result
== errSecSuccess
) {
968 if (session
->_textOutput
) {
969 SecOTRPrepareOutgoingBytes(destinationMessage
, protectedMessage
);
972 CFDataSetLength(session
->_macKeysToExpose
, 0);
975 CFReleaseSafe(destinationMessage
);
977 result
= errSecSuccess
;
984 void SecOTRSKickTimeToRoll(SecOTRSessionRef session
){
985 session
->_timeToRoll
= CFAbsoluteTimeGetCurrent();
988 static void SecOTRAcceptNewRemoteKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef newKey
)
990 if (session
->_theirPreviousKey
) {
991 SecOTRSExpireCachedKeysForPublicKey(session
, session
->_theirPreviousKey
);
994 CFReleaseNull(session
->_theirPreviousKey
);
995 session
->_theirPreviousKey
= session
->_theirKey
;
996 session
->_theirKey
= CFRetainSafe(newKey
);
997 session
->_stallingTheirRoll
= true;
999 session
->_theirKeyID
+= 1;
1001 SecOTRSEnableTimeToRoll(session
);
1004 OSStatus
SecOTRSetupInitialRemoteKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef initialKey
) {
1006 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
1008 CFReleaseNull(session
->_theirPreviousKey
);
1009 CFAssignRetained(session
->_theirKey
, initialKey
);
1010 session
->_theirKeyID
= 1;
1012 return errSecSuccess
;
1016 /// MARK: SLOW ROLLING
1019 void SOSOTRSRoll(SecOTRSessionRef session
){
1021 session
->_stallingTheirRoll
= false;
1023 //receiving side roll
1024 if(session
->_receivedAck
){
1025 SecOTRGenerateNewProposedKey(session
);
1026 session
->_missedAck
= false;
1027 session
->_receivedAck
= false;
1030 session
->_missedAck
= true;
1034 static OSStatus
SecOTRVerifyAndExposeRaw_locked(SecOTRSessionRef session
,
1035 CFDataRef decodedBytes
,
1036 CFMutableDataRef exposedMessageContents
)
1038 OSStatus result
= errSecDecode
;
1040 SecOTRPublicDHKeyRef newKey
= NULL
;
1041 const uint8_t* bytes
;
1044 bytes
= CFDataGetBytePtr(decodedBytes
);
1045 size
= CFDataGetLength(decodedBytes
);
1047 const uint8_t* macDataStart
= bytes
;
1052 require_noerr_quiet(result
= ReadAndVerifyHeader(&bytes
, &size
, kDataMessage
), fail
);
1053 require_action_quiet(size
> 0, fail
, result
= errSecDecode
);
1055 require_noerr_quiet(result
= ReadAndVerifyByte(&bytes
, &size
, 0), fail
); // Flags, always zero
1057 require_noerr_quiet(result
= ReadLong(&bytes
, &size
, &theirID
), fail
);
1059 require_action_quiet(theirID
== session
->_theirKeyID
|| (theirID
== (session
->_theirKeyID
- 1) && session
->_theirPreviousKey
!= NULL
),
1061 result
= ((theirID
+ 1) < session
->_theirKeyID
) ? errSecOTRTooOld
: errSecOTRIDTooNew
);
1063 require_noerr_quiet(result
= ReadLong(&bytes
, &size
, &myID
), fail
);
1065 require_action_quiet(myID
== session
->_keyID
|| (myID
== session
->_keyID
+ 1 && session
->_myNextKey
!= NULL
),
1067 result
= (myID
< session
->_keyID
) ? errSecOTRTooOld
: errSecOTRIDTooNew
);
1070 // Choose appripriate keys for message:
1072 uint8_t *messageKey
;
1074 uint64_t *theirCounter
;
1076 SecOTRFullDHKeyRef myKeyForMessage
= (myID
== session
->_keyID
) ? session
->_myKey
: session
->_myNextKey
;
1077 SecOTRPublicDHKeyRef theirKeyForMessage
= (theirID
== session
->_theirKeyID
) ? session
->_theirKey
: session
->_theirPreviousKey
;
1079 SecOTRSFindKeysForMessage(session
, myKeyForMessage
, theirKeyForMessage
, false,
1080 &messageKey
, &macKey
, &theirCounter
);
1082 size_t nextKeyMPISize
;
1083 const uint8_t* nextKeyMPIBytes
;
1084 require_noerr_quiet(result
= SizeAndSkipMPI(&bytes
, &size
, &nextKeyMPIBytes
, &nextKeyMPISize
), fail
);
1087 require_noerr_quiet(result
= ReadLongLong(&bytes
, &size
, &counter
), fail
);
1088 require_action_quiet(counter
> *theirCounter
, fail
, result
= errSecOTRTooOld
);
1091 const uint8_t* messageStart
;
1092 require_noerr_quiet(result
= SizeAndSkipDATA(&bytes
, &size
, &messageStart
, &messageSize
), fail
);
1094 size_t macDataSize
= (bytes
- macDataStart
) ? (size_t)(bytes
- macDataStart
) : 0;
1095 uint8_t mac
[CCSHA1_OUTPUT_SIZE
];
1096 require_action_quiet(sizeof(mac
) <= size
, fail
, result
= errSecDecode
);
1099 kOTRMessageMacKeyBytes
, macKey
,
1100 macDataSize
, macDataStart
,
1103 require_noerr_action_quiet(constant_memcmp(mac
, bytes
, sizeof(mac
)), fail
, result
= errSecAuthFailed
);
1105 uint8_t* dataSpace
= CFDataIncreaseLengthAndGetMutableBytes(exposedMessageContents
, (CFIndex
)messageSize
);
1107 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
1109 messageSize
, messageStart
,
1112 // Everything is good, accept the meta data.
1113 *theirCounter
= counter
;
1115 newKey
= SecOTRPublicDHKCreateFromBytes(kCFAllocatorDefault
, &nextKeyMPIBytes
, &nextKeyMPISize
);
1118 bool acceptTheirNewKey
= newKey
!= NULL
&& theirID
== session
->_theirKeyID
;
1120 if (acceptTheirNewKey
) {
1121 SecOTRAcceptNewRemoteKey(session
, newKey
);
1124 if (myID
== (session
->_keyID
+ 1)) {
1125 SecOTRSHandleProposalAcknowledge(session
);
1128 SecOTRSRollIfTime(session
);
1130 SecOTRSPrecalculateNextKeysInternal(session
);
1133 CFReleaseNull(newKey
);
1137 static OSStatus
SecOTRVerifyAndExposeRawCompact_locked(SecOTRSessionRef session
,
1138 CFDataRef decodedBytes
,
1139 CFMutableDataRef exposedMessageContents
)
1141 SecOTRPublicDHKeyRef theirProposal
= NULL
;
1142 OSStatus result
= errSecDecode
;
1143 const uint8_t* bytes
;
1145 SecOTRPublicDHKeyRef theirKeyForMessage
= NULL
;
1146 bytes
= CFDataGetBytePtr(decodedBytes
);
1147 size
= CFDataGetLength(decodedBytes
);
1148 SecOTRFullDHKeyRef myKeyForMessage
= NULL
;
1149 const uint8_t* macDataStart
= bytes
;
1150 bool useEvenKey
= false;
1151 bool useCurrentKey
= false;
1152 bool sentHashes
= false;
1153 uint64_t counter
= 0;
1155 uint8_t type_byte
= 0;
1156 require_noerr_quiet(result
= ReadByte(&bytes
, &size
, &type_byte
), fail
);
1157 require_action_quiet(type_byte
== kOddCompactDataMessage
|| type_byte
== kEvenCompactDataMessage
1158 || type_byte
== kOddCompactDataMessageWithHashes
|| type_byte
== kEvenCompactDataMessageWithHashes
, fail
, result
= errSecDecode
);
1160 useEvenKey
= (type_byte
== kEvenCompactDataMessage
|| type_byte
== kEvenCompactDataMessageWithHashes
);
1161 sentHashes
= (type_byte
== kOddCompactDataMessageWithHashes
|| type_byte
== kEvenCompactDataMessageWithHashes
);
1163 useCurrentKey
= useEvenKey
^ (session
->_keyID
& 1);
1164 myKeyForMessage
= useCurrentKey
? session
->_myKey
: session
->_myNextKey
;
1166 require_action_quiet(myKeyForMessage
, fail
, result
= errSecDecode
);
1168 theirProposal
= SecOTRPublicDHKCreateFromCompactSerialization(kCFAllocatorDefault
, &bytes
, &size
);
1170 require_action_quiet(theirProposal
, fail
, result
= errSecDecode
);
1172 bool proposalIsNew
= !CFEqualSafe(theirProposal
, session
->_theirKey
);
1173 theirKeyForMessage
= proposalIsNew
? session
->_theirKey
: session
->_theirPreviousKey
;
1175 require_action_quiet(theirKeyForMessage
, fail
, result
= errSecDecode
);
1177 uint8_t *messageKey
;
1179 uint64_t *theirCounter
;
1182 SecOTRSFindKeysForMessage(session
, myKeyForMessage
, theirKeyForMessage
, false, &messageKey
, &macKey
, &theirCounter
);
1184 require_noerr_quiet(result
= ReadLongLongCompact(&bytes
, &size
, &counter
), fail
);
1185 require_action_quiet(counter
> *theirCounter
, fail
, result
= errSecOTRTooOld
);
1187 size_t messageSize
= size
- kCompactMessageMACSize
- (sentHashes
? 2 * kSecDHKHashSize
: 0); // It's all message except for the MAC and maybe hashes
1188 const uint8_t* messageStart
= bytes
;
1190 bytes
+= messageSize
;
1191 size
-= messageSize
;
1194 // Sender then receiver keys
1196 if (memcmp(SecPDHKGetHash(theirKeyForMessage
), bytes
, kSecDHKHashSize
) != 0) {
1197 // Wrong sender key WTF.
1199 BufferPerformWithHexString(bytes
, kSecDHKHashSize
, ^(CFStringRef dataString
) {
1200 secdebug("OTR","session[%p] Sender key hash doesn't match: %@ != %@", session
, theirKeyForMessage
, dataString
);
1205 bytes
+= kSecDHKHashSize
;
1206 size
-= kSecDHKHashSize
;
1208 if (memcmp(SecFDHKGetHash(myKeyForMessage
), bytes
, kSecDHKHashSize
) != 0) {
1209 // Wrong sender key WTF.
1211 BufferPerformWithHexString(bytes
, kSecDHKHashSize
, ^(CFStringRef dataString
) {
1212 secdebug("OTR","session[%p] Receiver key hash doesn't match: %@ != %@", session
, myKeyForMessage
, dataString
);
1217 bytes
+= kSecDHKHashSize
;
1218 size
-= kSecDHKHashSize
;
1222 uint8_t mac
[CCSHA1_OUTPUT_SIZE
];
1223 require_action_quiet(kCompactMessageMACSize
== size
, fail
, result
= errSecDecode
); // require space for the mac and some bytes
1225 size_t macDataSize
= (size_t)(bytes
- macDataStart
);
1228 kOTRMessageMacKeyBytes
, macKey
,
1229 macDataSize
, macDataStart
,
1232 require_noerr_action_quiet(constant_memcmp(mac
, bytes
, kCompactMessageMACSize
), fail
, result
= errSecAuthFailed
);
1234 uint8_t* dataSpace
= CFDataIncreaseLengthAndGetMutableBytes(exposedMessageContents
, (CFIndex
)messageSize
);
1236 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
1238 messageSize
, messageStart
,
1241 // Everything is good, accept the meta data.
1242 *theirCounter
= counter
;
1244 if (proposalIsNew
) {
1245 SecOTRAcceptNewRemoteKey(session
, theirProposal
);
1248 if (!useCurrentKey
) {
1249 SecOTRSHandleProposalAcknowledge(session
);
1251 SecOTRSRollIfTime(session
);
1253 SecOTRSPrecalculateNextKeysInternal(session
);
1257 if(result
!= errSecSuccess
){
1258 CFDataPerformWithHexString(decodedBytes
, ^(CFStringRef decodedBytesString
) {
1259 secdebug("OTR","session[%p] failed to decrypt, session: %@, mk: %@, mpk: %@, tpk: %@, tk: %@, chose tktu: %@, mktu: %@, m: %@, tP: %@, tb: %hhx", session
, session
,
1260 session
->_myKey
, session
->_myNextKey
, session
->_theirPreviousKey
, session
->_theirKey
, theirKeyForMessage
, myKeyForMessage
, decodedBytesString
, theirProposal
, type_byte
);
1264 CFReleaseNull(theirProposal
);
1269 OSStatus
SecOTRSVerifyAndExposeMessage(SecOTRSessionRef session
,
1270 CFDataRef incomingMessage
,
1271 CFMutableDataRef exposedMessageContents
)
1273 __block OSStatus result
= errSecParam
;
1276 require(session
, abort
);
1277 require(incomingMessage
, abort
);
1278 require(exposedMessageContents
, abort
);
1280 if(session
->_state
== kDone
){
1281 dispatch_sync(session
->_queue
, ^{
1282 CFDataRef decodedBytes
= SecOTRCopyIncomingBytes(incomingMessage
);
1284 OTRMessageType messageType
= SecOTRSGetMessageType(decodedBytes
);
1286 switch (messageType
) {
1288 result
= SecOTRVerifyAndExposeRaw_locked(session
, decodedBytes
, exposedMessageContents
);
1291 case kOddCompactDataMessage
:
1292 case kEvenCompactDataMessage
:
1293 case kOddCompactDataMessageWithHashes
:
1294 case kEvenCompactDataMessageWithHashes
:
1295 result
= SecOTRVerifyAndExposeRawCompact_locked(session
, decodedBytes
, exposedMessageContents
);
1299 result
= errSecUnsupportedFormat
;
1303 CFReleaseSafe(decodedBytes
);
1307 secnotice("OTR", "session[%p]Cannot process message:%@, session is not done negotiating, session state: %@", session
, incomingMessage
, session
);
1308 result
= errSecOTRNotReady
;
1315 OSStatus
SecOTRSEndSession(SecOTRSessionRef session
,
1316 CFMutableDataRef messageToSend
)
1318 return errSecUnimplemented
;
1321 static CFDataRef
data_to_data_error_request(enum SecXPCOperation op
, CFDataRef publicPeerId
, CFErrorRef
*error
) {
1322 __block CFDataRef result
= NULL
;
1323 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1324 return SecXPCDictionarySetDataOptional(message
, kSecXPCPublicPeerId
, publicPeerId
, error
);
1325 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1326 return (result
= SecXPCDictionaryCopyData(response
, kSecXPCKeyResult
, error
));
1331 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
) {
1332 __block CFDataRef tempOutputSessionData
= NULL
;
1333 __block CFDataRef tempOutputPacket
= NULL
;
1334 __block
bool tempReadyForMessages
= false;
1336 bool result
= securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1337 return SecXPCDictionarySetDataOptional(message
, kSecXPCOTRSession
, sessionData
, error
)
1338 && SecXPCDictionarySetDataOptional(message
, kSecXPCData
, inputPacket
, error
);
1339 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1340 if (xpc_dictionary_get_bool(response
, kSecXPCKeyResult
)) {
1341 tempOutputSessionData
= SecXPCDictionaryCopyData(response
, kSecXPCOTRSession
, error
);
1342 tempOutputPacket
= SecXPCDictionaryCopyData(response
, kSecXPCData
, error
);
1343 tempReadyForMessages
= xpc_dictionary_get_bool(response
, kSecXPCOTRReady
);
1351 *outputSessionData
= tempOutputSessionData
;
1352 *outputPacket
= tempOutputPacket
;
1353 *readyForMessages
= tempReadyForMessages
;
1359 CFDataRef
SecOTRSessionCreateRemote(CFDataRef publicPeerId
, CFErrorRef
*error
) {
1360 __block CFDataRef result
;
1361 os_activity_initiate("SecOTRSessionCreateRemote", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1362 (void)SecOTRGetDefaultsWriteSeconds();
1363 result
= SECURITYD_XPC(sec_otr_session_create_remote
, data_to_data_error_request
, publicPeerId
, error
);
1368 bool SecOTRSessionProcessPacketRemote(CFDataRef sessionData
, CFDataRef inputPacket
, CFDataRef
* outputSessionData
, CFDataRef
* outputPacket
, bool *readyForMessages
, CFErrorRef
*error
) {
1369 __block
bool result
;
1370 os_activity_initiate("SecOTRSessionProcessPacketRemote", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1371 result
= SECURITYD_XPC(sec_otr_session_process_packet_remote
, data_data_to_data_data_bool_error_request
, sessionData
, inputPacket
, outputSessionData
, outputPacket
, readyForMessages
, error
);