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];
488 // Fill in the entry.
489 memcpy(emptyKeys
->_fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
);
490 memcpy(emptyKeys
->_publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
);
492 emptyKeys
->_counter
= 0;
493 emptyKeys
->_theirCounter
= 0;
495 SecOTRDHKGenerateOTRKeys(myKey
, theirKey
,
496 emptyKeys
->_sendEncryptionKey
, emptyKeys
->_sendMacKey
,
497 emptyKeys
->_receiveEncryptionKey
, emptyKeys
->_receiveMacKey
);
499 cachedKeys
= emptyKeys
;
501 WithCacheDescription(session
, ^(CFStringRef cacheDescription
) {
502 secdebug("OTR","mk %@, th: %@ session@[%p] new key cache state added key@[%d]: %@", myKey
, theirKey
, session
, emptyPosition
, cacheDescription
);
509 *messageKey
= sending
? cachedKeys
->_sendEncryptionKey
: cachedKeys
->_receiveEncryptionKey
;
511 *macKey
= sending
? cachedKeys
->_sendMacKey
: cachedKeys
->_receiveMacKey
;
513 *counter
= sending
? &cachedKeys
->_counter
: &cachedKeys
->_theirCounter
;
516 SecOTRSessionRef
SecOTRSessionCreateFromData(CFAllocatorRef allocator
, CFDataRef data
)
521 SecOTRSessionRef result
= NULL
;
522 SecOTRSessionRef session
= CFTypeAllocate(SecOTRSession
, struct _SecOTRSession
, allocator
);
524 uint8_t numberOfKeys
;
527 const uint8_t *bytes
= CFDataGetBytePtr(data
);
528 size_t size
= (size_t)CFDataGetLength(data
);
530 (void)SecOTRGetDefaultsWriteSeconds();
532 session
->_queue
= dispatch_queue_create("OTRSession", DISPATCH_QUEUE_SERIAL
);
535 session
->_them
= NULL
;
536 session
->_myKey
= NULL
;
537 session
->_myNextKey
= NULL
;
538 session
->_theirKey
= NULL
;
539 session
->_theirPreviousKey
= NULL
;
540 session
->_receivedDHMessage
= NULL
;
541 session
->_receivedDHKeyMessage
= NULL
;
542 session
->_textOutput
= false;
543 session
->_compactAppleMessages
= false;
544 session
->_timeToRoll
= 0;
545 session
->_stallingTheirRoll
= false;
546 session
->_stallSeconds
= 0;
547 session
->_missedAck
= true;
548 session
->_receivedAck
= false;
550 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
553 require_noerr(ReadByte(&bytes
, &size
, &version
), fail
);
554 require(version
<= 6, fail
);
556 require_noerr(ReadLong(&bytes
, &size
, &session
->_state
), fail
);
557 session
->_me
= SecOTRFullIdentityCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
, NULL
);
558 require(session
->_me
!= NULL
, fail
);
559 session
->_them
= SecOTRPublicIdentityCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
, NULL
);
560 require(session
->_them
!= NULL
, fail
);
562 require(size
> sizeof(session
->_r
), fail
);
563 memcpy(session
->_r
, bytes
, sizeof(session
->_r
));
564 bytes
+= sizeof(session
->_r
);
565 size
-= sizeof(session
->_r
);
568 uint8_t hasMessage
= false;
569 ReadByte(&bytes
, &size
, &hasMessage
);
571 session
->_receivedDHMessage
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
576 uint8_t hasMessage
= false;
577 ReadByte(&bytes
, &size
, &hasMessage
);
579 session
->_receivedDHKeyMessage
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
585 require_noerr(ReadByte(&bytes
, &size
, &ready
), fail
);
586 if (ready
&& session
->_state
== kIdle
)
587 session
->_state
= kDone
;
591 require_noerr(ReadLong(&bytes
, &size
, &session
->_keyID
), fail
);
592 if (session
->_keyID
> 0) {
593 session
->_myKey
= SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
);
594 require(session
->_myKey
!= NULL
, fail
);
595 session
->_myNextKey
= SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
);
596 require(session
->_myNextKey
!= NULL
, fail
);
600 require_noerr(ReadByte(&bytes
, &size
, &numberOfKeys
), fail
);
602 require_noerr(ReadLong(&bytes
, &size
, &session
->_theirKeyID
), fail
);
604 if (session
->_theirKeyID
> 0) {
605 if (session
->_theirKeyID
> 1) {
606 session
->_theirPreviousKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
607 require(session
->_theirPreviousKey
!= NULL
, fail
);
609 session
->_theirKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
610 require(session
->_theirKey
!= NULL
, fail
);
614 if(numberOfKeys
>= 1){
615 if (numberOfKeys
>= 2) {
616 session
->_theirPreviousKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
617 require(session
->_theirPreviousKey
!= NULL
, fail
);
619 session
->_theirKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
620 require(session
->_theirKey
!= NULL
, fail
);
626 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
627 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
628 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
629 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
630 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
631 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
632 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
633 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
634 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
635 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
636 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
637 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
638 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
639 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
640 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
641 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
643 session
->_macKeysToExpose
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
644 require(session
->_macKeysToExpose
!= NULL
, fail
);
646 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_textOutput
), fail
);
649 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_compactAppleMessages
), fail
);
652 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_includeHashes
), fail
);
655 require_noerr(ReadLongLong(&bytes
, &size
, &session
->_stallSeconds
), fail
);
656 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_stallingTheirRoll
), fail
);
657 require_noerr(ReadLongLong(&bytes
, &size
, &timeToRoll
), fail
);
658 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_missedAck
), fail
);
659 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_receivedAck
), fail
);
660 session
->_timeToRoll
= timeToRoll
;
666 CFReleaseNull(session
);
671 OSStatus
SecOTRSAppendSerialization(SecOTRSessionRef session
, CFMutableDataRef serializeInto
)
673 __block OSStatus result
= errSecParam
;
675 require(session
, abort
);
676 require(serializeInto
, abort
);
678 CFIndex start
= CFDataGetLength(serializeInto
);
680 dispatch_sync(session
->_queue
, ^{
681 const uint8_t version
= 6;
682 uint8_t numberOfKeys
= 0;
683 CFDataAppendBytes(serializeInto
, &version
, sizeof(version
));
685 AppendLong(serializeInto
, session
->_state
);
687 result
= (SecOTRFIAppendSerialization(session
->_me
, serializeInto
, NULL
)) ? errSecSuccess
: errSecParam
;
689 if (result
== errSecSuccess
) {
690 result
= (SecOTRPIAppendSerialization(session
->_them
, serializeInto
, NULL
)) ? errSecSuccess
: errSecParam
;
693 if (result
== errSecSuccess
) {
694 CFDataAppendBytes(serializeInto
, session
->_r
, sizeof(session
->_r
));
696 if (session
->_receivedDHMessage
== NULL
) {
697 AppendByte(serializeInto
, 0);
699 AppendByte(serializeInto
, 1);
700 AppendCFDataAsDATA(serializeInto
, session
->_receivedDHMessage
);
703 if (session
->_receivedDHKeyMessage
== NULL
) {
704 AppendByte(serializeInto
, 0);
706 AppendByte(serializeInto
, 1);
707 AppendCFDataAsDATA(serializeInto
, session
->_receivedDHKeyMessage
);
710 AppendLong(serializeInto
, session
->_keyID
);
711 if (session
->_keyID
> 0) {
712 SecFDHKAppendSerialization(session
->_myKey
, serializeInto
);
713 SecFDHKAppendSerialization(session
->_myNextKey
, serializeInto
);
716 if(session
->_theirPreviousKey
!= NULL
)
718 if(session
->_theirKey
!= NULL
)
721 AppendByte(serializeInto
, numberOfKeys
);
723 AppendLong(serializeInto
, session
->_theirKeyID
);
725 if (session
->_theirPreviousKey
!= NULL
)
726 SecPDHKAppendSerialization(session
->_theirPreviousKey
, serializeInto
);
728 if (session
->_theirKey
!= NULL
)
729 SecPDHKAppendSerialization(session
->_theirKey
, serializeInto
);
733 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
734 AppendLongLong(serializeInto
, *counter
);
735 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
736 AppendLongLong(serializeInto
, *counter
);
737 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
738 AppendLongLong(serializeInto
, *counter
);
739 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
740 AppendLongLong(serializeInto
, *counter
);
741 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
742 AppendLongLong(serializeInto
, *counter
);
743 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
744 AppendLongLong(serializeInto
, *counter
);
745 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
746 AppendLongLong(serializeInto
, *counter
);
747 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
748 AppendLongLong(serializeInto
, *counter
);
750 AppendCFDataAsDATA(serializeInto
, session
->_macKeysToExpose
);
752 AppendByte(serializeInto
, session
->_textOutput
? 1 : 0);
753 AppendByte(serializeInto
, session
->_compactAppleMessages
? 1 : 0);
754 AppendByte(serializeInto
, session
->_includeHashes
? 1 : 0);
756 AppendLongLong(serializeInto
, session
->_stallSeconds
? session
->_stallSeconds
: constant_zero
);
758 AppendByte(serializeInto
, session
->_stallingTheirRoll
? 1 : 0);
759 AppendLongLong(serializeInto
, (uint64_t)session
->_timeToRoll
);
760 AppendByte(serializeInto
, session
->_missedAck
? 1 : 0);
761 AppendByte(serializeInto
, session
->_receivedAck
? 1 : 0);
766 if (result
!= errSecSuccess
)
767 CFDataSetLength(serializeInto
, start
);
774 bool SecOTRSIsForKeys(SecOTRSessionRef session
, SecKeyRef myPublic
, SecKeyRef theirPublic
)
776 __block
bool isForKeys
= false;
778 dispatch_sync(session
->_queue
, ^{
779 isForKeys
= SecOTRFICompareToPublicKey(session
->_me
, myPublic
) &&
780 SecOTRPICompareToPublicKey(session
->_them
, theirPublic
);
786 bool SecOTRSGetIsReadyForMessages(SecOTRSessionRef session
)
790 dispatch_sync(session
->_queue
, ^{ result
= session
->_state
== kDone
; });
795 bool SecOTRSGetIsIdle(SecOTRSessionRef session
)
799 dispatch_sync(session
->_queue
, ^{ result
= session
->_state
== kIdle
; });
804 static void SecOTRSPrecalculateForPair(SecOTRSessionRef session
,
805 SecOTRFullDHKeyRef myKey
,
806 SecOTRPublicDHKeyRef theirKey
)
808 if (myKey
== NULL
|| theirKey
== NULL
)
811 SecOTRSFindKeysForMessage(session
, myKey
, theirKey
, true, NULL
, NULL
, NULL
);
812 SecOTRSFindKeysForMessage(session
, myKey
, theirKey
, false, NULL
, NULL
, NULL
);
815 static void SecOTRSPrecalculateKeysInternal(SecOTRSessionRef session
)
817 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirKey
);
818 SecOTRSPrecalculateForPair(session
, session
->_myNextKey
, session
->_theirKey
);
819 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirPreviousKey
);
820 SecOTRSPrecalculateForPair(session
, session
->_myNextKey
, session
->_theirPreviousKey
);
823 static void SecOTRSPrecalculateNextKeysInternal(SecOTRSessionRef session
)
825 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirKey
);
828 void SecOTRSPrecalculateKeys(SecOTRSessionRef session
)
830 dispatch_sync_f(session
->_queue
, session
, (dispatch_function_t
) SecOTRSPrecalculateKeysInternal
);
833 enum SecOTRSMessageKind
SecOTRSGetMessageKind(SecOTRSessionRef session
, CFDataRef message
)
835 OTRMessageType type
= SecOTRSGetMessageType(message
);
837 enum SecOTRSMessageKind kind
;
841 case kEvenCompactDataMessage
:
842 case kOddCompactDataMessage
:
843 case kEvenCompactDataMessageWithHashes
:
844 case kOddCompactDataMessageWithHashes
:
845 kind
= kOTRDataPacket
;
849 case kRevealSignatureMessage
:
850 case kSignatureMessage
:
851 kind
= kOTRNegotiationPacket
;
853 case kInvalidMessage
:
855 kind
= kOTRUnknownPacket
;
862 static OSStatus
SecOTRSSignAndProtectRaw_locked(SecOTRSessionRef session
,
863 CFDataRef sourceMessage
, CFMutableDataRef destinationMessage
,
864 uint8_t* messageKey
, uint8_t* macKey
, uint64_t* counter
, uint32_t theirKeyID
, SecOTRPublicDHKeyRef theirKey
)
866 CFIndex start
= CFDataGetLength(destinationMessage
);
868 AppendHeader(destinationMessage
, kDataMessage
);
869 AppendByte(destinationMessage
, 0); // Flags, all zero
871 AppendLong(destinationMessage
, session
->_keyID
);
872 AppendLong(destinationMessage
, theirKeyID
);
873 SecFDHKAppendPublicSerialization(session
->_myNextKey
, destinationMessage
);
874 AppendLongLong(destinationMessage
, ++*counter
);
876 CFIndex sourceSize
= CFDataGetLength(sourceMessage
);
877 assert(((unsigned long)sourceSize
)<=UINT32_MAX
); /* this is correct as long as CFIndex is a signed long */
878 AppendLong(destinationMessage
, (uint32_t)sourceSize
);
879 uint8_t* encryptedDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, sourceSize
);
880 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
882 (size_t)sourceSize
, CFDataGetBytePtr(sourceMessage
),
883 encryptedDataPointer
);
885 CFIndex macedContentsSize
= CFDataGetLength(destinationMessage
) - start
;
886 CFIndex macSize
= CCSHA1_OUTPUT_SIZE
;
887 uint8_t* macDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, macSize
);
890 kOTRMessageMacKeyBytes
, macKey
,
891 macedContentsSize
, CFDataGetBytePtr(destinationMessage
) + start
,
894 CFDataAppend(destinationMessage
, session
->_macKeysToExpose
);
896 return errSecSuccess
;
899 const size_t kCompactMessageMACSize
= 16;
901 static OSStatus
SecOTRSSignAndProtectCompact_locked(SecOTRSessionRef session
,
902 CFDataRef sourceMessage
, CFMutableDataRef destinationMessage
,
903 uint8_t* messageKey
, uint8_t* macKey
, uint64_t* counter
, uint32_t theirKeyID
, SecOTRPublicDHKeyRef theirKey
)
905 CFIndex start
= CFDataGetLength(destinationMessage
);
906 bool sendHashes
= session
->_includeHashes
;
908 const uint8_t messageType
= sendHashes
? ((theirKeyID
& 0x1) ? kOddCompactDataMessageWithHashes
: kEvenCompactDataMessageWithHashes
)
909 : ((theirKeyID
& 0x1) ? kOddCompactDataMessage
: kEvenCompactDataMessage
);
911 AppendByte(destinationMessage
, messageType
);
913 SecFDHKAppendCompactPublicSerialization(session
->_myNextKey
, destinationMessage
);
914 AppendLongLongCompact(destinationMessage
, ++*counter
);
916 CFIndex sourceSize
= CFDataGetLength(sourceMessage
);
917 assert(((unsigned long)sourceSize
)<=UINT32_MAX
); /* this is correct as long as CFIndex is a signed long */
918 uint8_t* encryptedDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, sourceSize
);
919 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
921 (size_t)sourceSize
, CFDataGetBytePtr(sourceMessage
),
922 encryptedDataPointer
);
925 uint8_t *senderHashPtr
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, kSecDHKHashSize
);
927 memcpy(senderHashPtr
, SecFDHKGetHash(session
->_myKey
), kSecDHKHashSize
);
929 uint8_t *receiverHashPtr
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, kSecDHKHashSize
);
931 memcpy(receiverHashPtr
, SecPDHKGetHash(theirKey
), kSecDHKHashSize
);
935 CFIndex macedContentsSize
= CFDataGetLength(destinationMessage
) - start
;
936 CFIndex macSize
= CCSHA1_OUTPUT_SIZE
;
937 uint8_t mac
[macSize
];
939 kOTRMessageMacKeyBytes
, macKey
,
940 macedContentsSize
, CFDataGetBytePtr(destinationMessage
) + start
,
943 CFDataAppendBytes(destinationMessage
, mac
, kCompactMessageMACSize
);
945 return errSecSuccess
;
948 OSStatus
SecOTRSSignAndProtectMessage(SecOTRSessionRef session
,
949 CFDataRef sourceMessage
,
950 CFMutableDataRef protectedMessage
)
952 __block OSStatus result
= errSecParam
;
954 require(session
, abort
);
955 require(sourceMessage
, abort
);
956 require(protectedMessage
, abort
);
958 if(session
->_state
!= kDone
){
959 secdebug("OTR", "Cannot sign and protect messages, we are not done negotiating sesion[%p]", session
);
960 require_quiet( session
->_state
== kDone
, abort
);
963 dispatch_sync(session
->_queue
, ^{
964 if (session
->_myKey
== NULL
||
965 session
->_theirKey
== NULL
) {
972 uint32_t theirKeyID
= session
->_theirKeyID
;
974 SecOTRPublicDHKeyRef theirKeyToUse
= session
->_theirKey
;
976 SecOTRSRollIfTime(session
);
978 if(session
->_stallingTheirRoll
&& session
->_theirPreviousKey
){
979 theirKeyToUse
= session
->_theirPreviousKey
;
980 theirKeyID
= session
->_theirKeyID
- 1;
983 SecOTRSFindKeysForMessage(session
, session
->_myKey
, theirKeyToUse
,
985 &messageKey
, &macKey
, &counter
);
986 // 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.
987 CFMutableDataRef destinationMessage
= session
->_textOutput
|| !protectedMessage
? CFDataCreateMutable(kCFAllocatorDefault
, 0) : CFRetainSafe(protectedMessage
);
989 result
= session
->_compactAppleMessages
? SecOTRSSignAndProtectCompact_locked(session
, sourceMessage
, destinationMessage
, messageKey
, macKey
, counter
, theirKeyID
, theirKeyToUse
)
990 : SecOTRSSignAndProtectRaw_locked(session
, sourceMessage
, destinationMessage
, messageKey
, macKey
, counter
, theirKeyID
, theirKeyToUse
);
992 if (result
== errSecSuccess
) {
993 if (session
->_textOutput
) {
994 SecOTRPrepareOutgoingBytes(destinationMessage
, protectedMessage
);
997 CFDataSetLength(session
->_macKeysToExpose
, 0);
1000 CFReleaseSafe(destinationMessage
);
1002 result
= errSecSuccess
;
1009 void SecOTRSKickTimeToRoll(SecOTRSessionRef session
){
1010 session
->_timeToRoll
= CFAbsoluteTimeGetCurrent();
1013 static void SecOTRAcceptNewRemoteKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef newKey
)
1015 if (session
->_theirPreviousKey
) {
1016 SecOTRSExpireCachedKeysForPublicKey(session
, session
->_theirPreviousKey
);
1019 CFReleaseNull(session
->_theirPreviousKey
);
1020 session
->_theirPreviousKey
= session
->_theirKey
;
1021 session
->_theirKey
= CFRetainSafe(newKey
);
1022 session
->_stallingTheirRoll
= true;
1024 session
->_theirKeyID
+= 1;
1026 SecOTRSEnableTimeToRoll(session
);
1029 OSStatus
SecOTRSetupInitialRemoteKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef initialKey
) {
1031 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
1033 CFReleaseNull(session
->_theirPreviousKey
);
1034 CFAssignRetained(session
->_theirKey
, initialKey
);
1035 session
->_theirKeyID
= 1;
1037 return errSecSuccess
;
1041 /// MARK: SLOW ROLLING
1044 void SOSOTRSRoll(SecOTRSessionRef session
){
1046 session
->_stallingTheirRoll
= false;
1048 //receiving side roll
1049 if(session
->_receivedAck
){
1050 SecOTRGenerateNewProposedKey(session
);
1051 session
->_missedAck
= false;
1052 session
->_receivedAck
= false;
1055 session
->_missedAck
= true;
1059 static OSStatus
SecOTRVerifyAndExposeRaw_locked(SecOTRSessionRef session
,
1060 CFDataRef decodedBytes
,
1061 CFMutableDataRef exposedMessageContents
)
1063 OSStatus result
= errSecDecode
;
1065 SecOTRPublicDHKeyRef newKey
= NULL
;
1066 const uint8_t* bytes
;
1068 SecOTRFullDHKeyRef myKeyForMessage
= NULL
;
1069 SecOTRPublicDHKeyRef theirKeyForMessage
= NULL
;
1070 bytes
= CFDataGetBytePtr(decodedBytes
);
1071 size
= CFDataGetLength(decodedBytes
);
1073 const uint8_t* macDataStart
= bytes
;
1078 require_noerr_quiet(result
= ReadAndVerifyHeader(&bytes
, &size
, kDataMessage
), fail
);
1079 require_action_quiet(size
> 0, fail
, result
= errSecDecode
);
1081 require_noerr_quiet(result
= ReadAndVerifyByte(&bytes
, &size
, 0), fail
); // Flags, always zero
1083 require_noerr_quiet(result
= ReadLong(&bytes
, &size
, &theirID
), fail
);
1085 require_action_quiet(theirID
== session
->_theirKeyID
|| (theirID
== (session
->_theirKeyID
- 1) && session
->_theirPreviousKey
!= NULL
),
1087 result
= ((theirID
+ 1) < session
->_theirKeyID
) ? errSecOTRTooOld
: errSecOTRIDTooNew
);
1089 require_noerr_quiet(result
= ReadLong(&bytes
, &size
, &myID
), fail
);
1091 require_action_quiet(myID
== session
->_keyID
|| (myID
== session
->_keyID
+ 1 && session
->_myNextKey
!= NULL
),
1093 result
= (myID
< session
->_keyID
) ? errSecOTRTooOld
: errSecOTRIDTooNew
);
1096 // Choose appripriate keys for message:
1098 uint8_t *messageKey
;
1100 uint64_t *theirCounter
;
1102 myKeyForMessage
= (myID
== session
->_keyID
) ? session
->_myKey
: session
->_myNextKey
;
1103 theirKeyForMessage
= (theirID
== session
->_theirKeyID
) ? session
->_theirKey
: session
->_theirPreviousKey
;
1105 SecOTRSFindKeysForMessage(session
, myKeyForMessage
, theirKeyForMessage
, false,
1106 &messageKey
, &macKey
, &theirCounter
);
1108 size_t nextKeyMPISize
;
1109 const uint8_t* nextKeyMPIBytes
;
1110 require_noerr_quiet(result
= SizeAndSkipMPI(&bytes
, &size
, &nextKeyMPIBytes
, &nextKeyMPISize
), fail
);
1113 require_noerr_quiet(result
= ReadLongLong(&bytes
, &size
, &counter
), fail
);
1114 require_action_quiet(counter
> *theirCounter
, fail
, result
= errSecOTRTooOld
);
1117 const uint8_t* messageStart
;
1118 require_noerr_quiet(result
= SizeAndSkipDATA(&bytes
, &size
, &messageStart
, &messageSize
), fail
);
1120 size_t macDataSize
= (bytes
- macDataStart
) ? (size_t)(bytes
- macDataStart
) : 0;
1121 uint8_t mac
[CCSHA1_OUTPUT_SIZE
];
1122 require_action_quiet(sizeof(mac
) <= size
, fail
, result
= errSecDecode
);
1125 kOTRMessageMacKeyBytes
, macKey
,
1126 macDataSize
, macDataStart
,
1129 require_noerr_action_quiet(timingsafe_bcmp(mac
, bytes
, sizeof(mac
)), fail
, result
= errSecAuthFailed
);
1131 uint8_t* dataSpace
= CFDataIncreaseLengthAndGetMutableBytes(exposedMessageContents
, (CFIndex
)messageSize
);
1133 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
1135 messageSize
, messageStart
,
1138 // Everything is good, accept the meta data.
1139 *theirCounter
= counter
;
1141 newKey
= SecOTRPublicDHKCreateFromBytes(kCFAllocatorDefault
, &nextKeyMPIBytes
, &nextKeyMPISize
);
1144 bool acceptTheirNewKey
= newKey
!= NULL
&& theirID
== session
->_theirKeyID
;
1146 if (acceptTheirNewKey
) {
1147 SecOTRAcceptNewRemoteKey(session
, newKey
);
1150 if (myID
== (session
->_keyID
+ 1)) {
1151 SecOTRSHandleProposalAcknowledge(session
);
1154 SecOTRSRollIfTime(session
);
1156 SecOTRSPrecalculateNextKeysInternal(session
);
1159 if(result
!= errSecSuccess
){
1160 CFDataPerformWithHexString(decodedBytes
, ^(CFStringRef decodedBytesString
) {
1161 SecOTRPublicIdentityRef myPublicID
= SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault
, session
->_me
, NULL
);
1162 SecOTRPIPerformWithSerializationString(myPublicID
, ^(CFStringRef myIDString
) {
1163 SecOTRPIPerformWithSerializationString(session
->_them
, ^(CFStringRef theirIDString
) {
1164 secnotice("OTR","session[%p] failed to decrypt, session: %@, mk: %@, mpk: %@, tpk: %@, tk: %@, chose tktu: %@", session
, session
,
1165 session
->_myKey
, session
->_myNextKey
, session
->_theirPreviousKey
, session
->_theirKey
, theirKeyForMessage
);
1166 secnotice("OTR","session[%p] failed to decrypt, mktu: %@, mpi: %@, tpi: %@, m: %@", session
, myKeyForMessage
, myIDString
, theirIDString
, decodedBytesString
);
1169 CFReleaseNull(myPublicID
);
1172 CFReleaseNull(newKey
);
1176 static OSStatus
SecOTRVerifyAndExposeRawCompact_locked(SecOTRSessionRef session
,
1177 CFDataRef decodedBytes
,
1178 CFMutableDataRef exposedMessageContents
)
1180 SecOTRPublicDHKeyRef theirProposal
= NULL
;
1181 OSStatus result
= errSecDecode
;
1182 const uint8_t* bytes
;
1184 SecOTRPublicDHKeyRef theirKeyForMessage
= NULL
;
1185 bytes
= CFDataGetBytePtr(decodedBytes
);
1186 size
= CFDataGetLength(decodedBytes
);
1187 SecOTRFullDHKeyRef myKeyForMessage
= NULL
;
1188 const uint8_t* macDataStart
= bytes
;
1189 bool useEvenKey
= false;
1190 bool useCurrentKey
= false;
1191 bool sentHashes
= false;
1192 uint64_t counter
= 0;
1194 uint8_t type_byte
= 0;
1195 require_noerr_quiet(result
= ReadByte(&bytes
, &size
, &type_byte
), fail
);
1196 require_action_quiet(type_byte
== kOddCompactDataMessage
|| type_byte
== kEvenCompactDataMessage
1197 || type_byte
== kOddCompactDataMessageWithHashes
|| type_byte
== kEvenCompactDataMessageWithHashes
, fail
, result
= errSecDecode
);
1199 useEvenKey
= (type_byte
== kEvenCompactDataMessage
|| type_byte
== kEvenCompactDataMessageWithHashes
);
1200 sentHashes
= (type_byte
== kOddCompactDataMessageWithHashes
|| type_byte
== kEvenCompactDataMessageWithHashes
);
1202 useCurrentKey
= useEvenKey
^ (session
->_keyID
& 1);
1203 myKeyForMessage
= useCurrentKey
? session
->_myKey
: session
->_myNextKey
;
1205 require_action_quiet(myKeyForMessage
, fail
, result
= errSecDecode
);
1207 theirProposal
= SecOTRPublicDHKCreateFromCompactSerialization(kCFAllocatorDefault
, &bytes
, &size
);
1209 require_action_quiet(theirProposal
, fail
, result
= errSecDecode
);
1211 bool proposalIsNew
= !CFEqualSafe(theirProposal
, session
->_theirKey
);
1212 theirKeyForMessage
= proposalIsNew
? session
->_theirKey
: session
->_theirPreviousKey
;
1214 require_action_quiet(theirKeyForMessage
, fail
, result
= errSecDecode
);
1216 uint8_t *messageKey
;
1218 uint64_t *theirCounter
;
1221 SecOTRSFindKeysForMessage(session
, myKeyForMessage
, theirKeyForMessage
, false, &messageKey
, &macKey
, &theirCounter
);
1223 require_noerr_quiet(result
= ReadLongLongCompact(&bytes
, &size
, &counter
), fail
);
1224 require_action_quiet(counter
> *theirCounter
, fail
, result
= errSecOTRTooOld
);
1226 size_t messageSize
= size
- kCompactMessageMACSize
- (sentHashes
? 2 * kSecDHKHashSize
: 0); // It's all message except for the MAC and maybe hashes
1227 const uint8_t* messageStart
= bytes
;
1229 bytes
+= messageSize
;
1230 size
-= messageSize
;
1233 // Sender then receiver keys
1235 if (memcmp(SecPDHKGetHash(theirKeyForMessage
), bytes
, kSecDHKHashSize
) != 0) {
1236 // Wrong sender key WTF.
1238 BufferPerformWithHexString(bytes
, kSecDHKHashSize
, ^(CFStringRef dataString
) {
1239 secdebug("OTR","session[%p] Sender key hash doesn't match: %@ != %@", session
, theirKeyForMessage
, dataString
);
1244 bytes
+= kSecDHKHashSize
;
1245 size
-= kSecDHKHashSize
;
1247 if (memcmp(SecFDHKGetHash(myKeyForMessage
), bytes
, kSecDHKHashSize
) != 0) {
1248 // Wrong sender key WTF.
1250 BufferPerformWithHexString(bytes
, kSecDHKHashSize
, ^(CFStringRef dataString
) {
1251 secdebug("OTR","session[%p] Receiver key hash doesn't match: %@ != %@", session
, myKeyForMessage
, dataString
);
1256 bytes
+= kSecDHKHashSize
;
1257 size
-= kSecDHKHashSize
;
1261 uint8_t mac
[CCSHA1_OUTPUT_SIZE
];
1262 require_action_quiet(kCompactMessageMACSize
== size
, fail
, result
= errSecDecode
); // require space for the mac and some bytes
1264 size_t macDataSize
= (size_t)(bytes
- macDataStart
);
1267 kOTRMessageMacKeyBytes
, macKey
,
1268 macDataSize
, macDataStart
,
1271 require_noerr_action_quiet(timingsafe_bcmp(mac
, bytes
, kCompactMessageMACSize
), fail
, result
= errSecAuthFailed
);
1273 uint8_t* dataSpace
= CFDataIncreaseLengthAndGetMutableBytes(exposedMessageContents
, (CFIndex
)messageSize
);
1275 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
1277 messageSize
, messageStart
,
1280 // Everything is good, accept the meta data.
1281 *theirCounter
= counter
;
1283 if (proposalIsNew
) {
1284 SecOTRAcceptNewRemoteKey(session
, theirProposal
);
1287 if (!useCurrentKey
) {
1288 SecOTRSHandleProposalAcknowledge(session
);
1290 SecOTRSRollIfTime(session
);
1292 SecOTRSPrecalculateNextKeysInternal(session
);
1295 if(result
!= errSecSuccess
){
1296 CFDataPerformWithHexString(decodedBytes
, ^(CFStringRef decodedBytesString
) {
1297 SecOTRPublicIdentityRef myPublicID
= SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault
, session
->_me
, NULL
);
1298 SecOTRPIPerformWithSerializationString(myPublicID
, ^(CFStringRef myIDString
) {
1299 SecOTRPIPerformWithSerializationString(session
->_them
, ^(CFStringRef theirIDString
) {
1300 secnotice("OTR","session[%p] failed to decrypt, session: %@, mk: %@, mpk: %@, tpk: %@, tk: %@, chose tktu: %@", session
, session
,
1301 session
->_myKey
, session
->_myNextKey
, session
->_theirPreviousKey
, session
->_theirKey
, theirKeyForMessage
);
1302 secnotice("OTR","session[%p] failed to decrypt, mktu: %@, mpi: %@, tpi: %@, m: %@, tP: %@, tb: %hhx", session
, myKeyForMessage
, myIDString
, theirIDString
, decodedBytesString
, theirProposal
, type_byte
);
1305 CFReleaseNull(myPublicID
);
1308 CFReleaseNull(theirProposal
);
1313 OSStatus
SecOTRSVerifyAndExposeMessage(SecOTRSessionRef session
,
1314 CFDataRef incomingMessage
,
1315 CFMutableDataRef exposedMessageContents
)
1317 __block OSStatus result
= errSecParam
;
1320 require(session
, abort
);
1321 require(incomingMessage
, abort
);
1322 require(exposedMessageContents
, abort
);
1324 if(session
->_state
== kDone
){
1325 dispatch_sync(session
->_queue
, ^{
1326 CFDataRef decodedBytes
= SecOTRCopyIncomingBytes(incomingMessage
);
1328 OTRMessageType messageType
= SecOTRSGetMessageType(decodedBytes
);
1330 switch (messageType
) {
1332 result
= SecOTRVerifyAndExposeRaw_locked(session
, decodedBytes
, exposedMessageContents
);
1335 case kOddCompactDataMessage
:
1336 case kEvenCompactDataMessage
:
1337 case kOddCompactDataMessageWithHashes
:
1338 case kEvenCompactDataMessageWithHashes
:
1339 result
= SecOTRVerifyAndExposeRawCompact_locked(session
, decodedBytes
, exposedMessageContents
);
1343 result
= errSecUnsupportedFormat
;
1347 CFReleaseSafe(decodedBytes
);
1351 secnotice("OTR", "session[%p]Cannot process message:%@, session is not done negotiating, session state: %@", session
, incomingMessage
, session
);
1352 result
= errSecOTRNotReady
;
1359 OSStatus
SecOTRSEndSession(SecOTRSessionRef session
,
1360 CFMutableDataRef messageToSend
)
1362 return errSecUnimplemented
;
1365 static CFDataRef
data_to_data_error_request(enum SecXPCOperation op
, CFDataRef publicPeerId
, CFErrorRef
*error
) {
1366 __block CFDataRef result
= NULL
;
1367 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1368 return SecXPCDictionarySetDataOptional(message
, kSecXPCPublicPeerId
, publicPeerId
, error
);
1369 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1370 return (result
= SecXPCDictionaryCopyData(response
, kSecXPCKeyResult
, error
));
1375 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
) {
1376 __block CFDataRef tempOutputSessionData
= NULL
;
1377 __block CFDataRef tempOutputPacket
= NULL
;
1378 __block
bool tempReadyForMessages
= false;
1380 bool result
= securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1381 return SecXPCDictionarySetDataOptional(message
, kSecXPCOTRSession
, sessionData
, error
)
1382 && SecXPCDictionarySetDataOptional(message
, kSecXPCData
, inputPacket
, error
);
1383 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1384 if (xpc_dictionary_get_bool(response
, kSecXPCKeyResult
)) {
1385 tempOutputSessionData
= SecXPCDictionaryCopyData(response
, kSecXPCOTRSession
, error
);
1386 tempOutputPacket
= SecXPCDictionaryCopyData(response
, kSecXPCData
, error
);
1387 tempReadyForMessages
= xpc_dictionary_get_bool(response
, kSecXPCOTRReady
);
1395 *outputSessionData
= tempOutputSessionData
;
1396 *outputPacket
= tempOutputPacket
;
1397 *readyForMessages
= tempReadyForMessages
;
1403 CFDataRef
SecOTRSessionCreateRemote(CFDataRef publicPeerId
, CFErrorRef
*error
) {
1404 __block CFDataRef result
;
1405 os_activity_initiate("SecOTRSessionCreateRemote", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1406 (void)SecOTRGetDefaultsWriteSeconds();
1407 result
= SECURITYD_XPC(sec_otr_session_create_remote
, data_to_data_error_request
, publicPeerId
, error
);
1412 bool SecOTRSessionProcessPacketRemote(CFDataRef sessionData
, CFDataRef inputPacket
, CFDataRef
* outputSessionData
, CFDataRef
* outputPacket
, bool *readyForMessages
, CFErrorRef
*error
) {
1413 __block
bool result
;
1414 os_activity_initiate("SecOTRSessionProcessPacketRemote", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1415 result
= SECURITYD_XPC(sec_otr_session_process_packet_remote
, data_data_to_data_data_bool_error_request
, sessionData
, inputPacket
, outputSessionData
, outputPacket
, readyForMessages
, error
);