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 "keychain/SecureObjectSync/SOSCircle.h"
50 #include <Security/SecureObjectSync/SOSCloudCircle.h>
51 #include "keychain/SecureObjectSync/SOSInternal.h"
52 #include "keychain/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 OSStatus
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 OSStatus ret
= SecFDHKNewKey(session
->_myNextKey
);
139 session
->_keyID
+= 1;
145 static void SecOTRSHandleProposalAcknowledge(SecOTRSessionRef session
){
146 if(session
->_missedAck
){
147 SecOTRGenerateNewProposedKey(session
);
148 session
->_missedAck
= false;
151 session
->_receivedAck
= true;
152 SecOTRSEnableTimeToRoll(session
);
156 static void SecOTRSRollIfTime(SecOTRSessionRef session
){
158 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
159 CFAbsoluteTime longestTimeToRoll
= now
+ session
->_stallSeconds
;
161 //in case time to roll becomes too large we're going to roll now!
162 if(session
->_timeToRoll
< now
|| session
->_timeToRoll
> longestTimeToRoll
){
163 SOSOTRSRoll(session
);
164 session
->_timeToRoll
= 0;
169 static OTRMessageType
SecOTRSGetMessageType(CFDataRef message
)
171 OTRMessageType type
= kInvalidMessage
;
173 CFDataRef decodedBytes
= SecOTRCopyIncomingBytes(message
);
175 const uint8_t *bytes
= CFDataGetBytePtr(decodedBytes
);
176 size_t size
= CFDataGetLength(decodedBytes
);
178 if (noErr
!= ReadHeader(&bytes
, &size
, &type
)) {
179 uint8_t firstByte
= *CFDataGetBytePtr(decodedBytes
);
181 case kOddCompactDataMessage
:
182 case kEvenCompactDataMessage
:
183 case kOddCompactDataMessageWithHashes
:
184 case kEvenCompactDataMessageWithHashes
:
193 CFReleaseNull(decodedBytes
);
200 static CFStringRef
SecOTRCacheElementCopyDescription(SecOTRCacheElement
*keyCache
){
201 __block CFStringRef description
= NULL
;
202 BufferPerformWithHexString(keyCache
->_fullKeyHash
, sizeof(keyCache
->_fullKeyHash
), ^(CFStringRef fullKeyHashString
) {
203 BufferPerformWithHexString(keyCache
->_publicKeyHash
,sizeof(keyCache
->_publicKeyHash
), ^(CFStringRef publicKeyHashString
) {
204 description
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("fkh: [%@], pkh: [%@], c: %llu tc: %llu"), fullKeyHashString
, publicKeyHashString
, keyCache
->_counter
, keyCache
->_theirCounter
);
211 const char *SecOTRPacketTypeString(CFDataRef message
)
213 if (!message
) return "NoMessage";
214 switch (SecOTRSGetMessageType(message
)) {
215 case kDHMessage
: return "DHMessage (0x02)";
216 case kDataMessage
: return "DataMessage (0x03)";
217 case kDHKeyMessage
: return "DHKeyMessage (0x0A)";
218 case kRevealSignatureMessage
: return "RevealSignatureMessage (0x11)";
219 case kSignatureMessage
: return "SignatureMessage (0x12)";
220 case kEvenCompactDataMessage
: return "kEvenCompactDatamessage (0x20)";
221 case kOddCompactDataMessage
: return "kOddCompactDataMessage (0x21)";
222 case kEvenCompactDataMessageWithHashes
: return "kEvenCompactDatamessage (0x30)";
223 case kOddCompactDataMessageWithHashes
: return "kOddCompactDataMessage (0x31)";
224 case kInvalidMessage
: return "InvalidMessage (0xFF)";
225 default: return "UnknownMessage";
229 static const char *SecOTRAuthStateString(SecOTRAuthState authState
)
232 case kIdle
: return "Idle";
233 case kAwaitingDHKey
: return "AwaitingDHKey";
234 case kAwaitingRevealSignature
: return "AwaitingRevealSignature";
235 case kAwaitingSignature
: return "AwaitingSignature";
236 case kDone
: return "Done";
237 default: return "InvalidState";
241 static CF_RETURNS_RETAINED CFStringRef
SecOTRSessionCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
242 SecOTRSessionRef session
= (SecOTRSessionRef
)cf
;
244 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
246 return CFStringCreateWithFormat(kCFAllocatorDefault
,NULL
,CFSTR("<%s %s %s %s%s%s%s %d:%d %s%s %llu %s%s%s%s>"),
247 SecOTRAuthStateString(session
->_state
),
248 session
->_compactAppleMessages
? "C" :"c",
249 session
->_includeHashes
? "I" : "i",
250 session
->_me
? "F" : "f",
251 session
->_them
? "P" : "p",
252 session
->_receivedDHMessage
? "D" : "d",
253 session
->_receivedDHKeyMessage
? "K" : "k",
255 session
->_theirKeyID
,
256 session
->_theirPreviousKey
? "P" : "p",
257 session
->_theirKey
? "T" : "t",
258 session
->_stallSeconds
,
259 session
->_missedAck
? "M" : "m",
260 session
->_receivedAck
? "R" : "r",
261 session
->_stallingTheirRoll
? "S" : "s",
262 (session
->_timeToRoll
> now
&& session
->_timeToRoll
!= 0) ? "E" : "e");
265 static void SecOTRSessionDestroy(CFTypeRef cf
) {
266 SecOTRSessionRef session
= (SecOTRSessionRef
)cf
;
268 CFReleaseNull(session
->_receivedDHMessage
);
269 CFReleaseNull(session
->_receivedDHKeyMessage
);
271 CFReleaseNull(session
->_me
);
272 CFReleaseNull(session
->_myKey
);
273 CFReleaseNull(session
->_myNextKey
);
275 CFReleaseNull(session
->_them
);
276 CFReleaseNull(session
->_theirKey
);
277 CFReleaseNull(session
->_theirPreviousKey
);
279 CFReleaseNull(session
->_macKeysToExpose
);
281 dispatch_release(session
->_queue
);
284 static void SecOTRSessionResetInternal(SecOTRSessionRef session
)
286 session
->_state
= kIdle
;
288 CFReleaseNull(session
->_receivedDHMessage
);
289 CFReleaseNull(session
->_receivedDHKeyMessage
);
292 CFReleaseNull(session
->_myKey
);
293 CFReleaseNull(session
->_myNextKey
);
294 //session->_myNextKey = SecOTRFullDHKCreate(kCFAllocatorDefault);
295 session
->_theirKeyID
= 0;
296 CFReleaseNull(session
->_theirKey
);
297 CFReleaseNull(session
->_theirPreviousKey
);
298 CFReleaseNull(session
->_macKeysToExpose
);
299 session
->_macKeysToExpose
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
301 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
304 int SecOTRSGetKeyID(SecOTRSessionRef session
){
305 return session
->_keyID
;
308 int SecOTRSGetTheirKeyID(SecOTRSessionRef session
){
309 return session
->_theirKeyID
;
312 void SecOTRSessionReset(SecOTRSessionRef session
)
314 dispatch_sync_f(session
->_queue
, session
, (dispatch_function_t
) SecOTRSessionResetInternal
);
318 static void SecOTRPIPerformWithSerializationString(SecOTRPublicIdentityRef id
, void (^action
)(CFStringRef string
)) {
319 CFMutableDataRef idData
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
320 SecOTRPIAppendSerialization(id
, idData
, NULL
);
321 CFDataPerformWithHexString(idData
, action
);
322 CFReleaseNull(idData
);
325 SecOTRSessionRef
SecOTRSessionCreateFromID(CFAllocatorRef allocator
,
326 SecOTRFullIdentityRef myID
,
327 SecOTRPublicIdentityRef theirID
)
329 SecOTRSessionRef newID
= CFTypeAllocate(SecOTRSession
, struct _SecOTRSession
, allocator
);
331 (void)SecOTRGetDefaultsWriteSeconds();
332 newID
->_queue
= dispatch_queue_create("OTRSession", DISPATCH_QUEUE_SERIAL
);
334 newID
->_me
= CFRetainSafe(myID
);
335 newID
->_them
= CFRetainSafe(theirID
);
336 newID
->_receivedDHMessage
= NULL
;
337 newID
->_receivedDHKeyMessage
= NULL
;
338 newID
->_myKey
= NULL
;
339 newID
->_myNextKey
= NULL
;
340 newID
->_theirKey
= NULL
;
341 newID
->_theirPreviousKey
= NULL
;
342 newID
->_macKeysToExpose
= NULL
;
343 newID
->_textOutput
= false;
344 newID
->_compactAppleMessages
= false;
345 newID
->_includeHashes
= false;
347 newID
->_timeToRoll
= 0;
348 newID
->_stallingTheirRoll
= false;
349 newID
->_stallSeconds
= 0;
350 newID
->_missedAck
= true;
351 newID
->_receivedAck
= false;
353 SecOTRSessionResetInternal(newID
);
356 SecOTRPublicIdentityRef myPublicID
= SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault
, newID
->_me
, NULL
);
357 SecOTRPIPerformWithSerializationString(myPublicID
, ^(CFStringRef myIDString
) {
358 SecOTRPIPerformWithSerializationString(newID
->_them
, ^(CFStringRef theirIDString
) {
359 secnotice("otr", "%@ Creating with M: %@, T: %@", newID
, myIDString
, theirIDString
);
362 CFReleaseNull(myPublicID
);
368 SecOTRSessionRef
SecOTRSessionCreateFromIDAndFlags(CFAllocatorRef allocator
,
369 SecOTRFullIdentityRef myID
,
370 SecOTRPublicIdentityRef theirID
,
374 uint64_t seconds
= SecOTRGetDefaultsWriteSeconds();
376 SecOTRSessionRef newID
= SecOTRSessionCreateFromID(allocator
, myID
, theirID
);
377 if (flags
& kSecOTRSendTextMessages
) {
378 newID
->_textOutput
= true;
380 if (flags
& kSecOTRUseAppleCustomMessageFormat
) {
381 newID
->_compactAppleMessages
= true;
383 if(flags
& kSecOTRIncludeHashesInMessages
)
385 newID
->_includeHashes
= true;
387 if(flags
& kSecOTRSlowRoll
)
389 newID
->_stallSeconds
= seconds
;
395 static uint64_t constant_zero
= 0;
397 static bool hashIsZero(uint8_t hash
[CCSHA1_OUTPUT_SIZE
])
400 for(size_t byte
= 0; isZero
&& byte
< CCSHA1_OUTPUT_SIZE
; ++byte
)
401 isZero
= (0 == hash
[byte
]);
406 static bool SOSOTRSCacheEntryIsEmpty(SecOTRCacheElement
*element
)
408 return hashIsZero(element
->_fullKeyHash
) && hashIsZero(element
->_publicKeyHash
);
413 static void WithCacheDescription(SecOTRSessionRef session
, void (^operation
)(CFStringRef cacheDescription
)) {
414 CFStringRef description
= NULL
;
416 CFStringRef keyCache0Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[0]);
417 CFStringRef keyCache1Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[1]);
418 CFStringRef keyCache2Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[2]);
419 CFStringRef keyCache3Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[3]);
421 description
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("{%@, %@, %@, %@}"), keyCache0Description
, keyCache1Description
, keyCache2Description
, keyCache3Description
);
423 operation(description
);
425 CFReleaseNull(keyCache0Description
);
426 CFReleaseNull(keyCache1Description
);
427 CFReleaseNull(keyCache2Description
);
428 CFReleaseNull(keyCache3Description
);
429 CFReleaseNull(description
);
434 static void SecOTRSFindKeysForMessage(SecOTRSessionRef session
,
435 SecOTRFullDHKeyRef myKey
,
436 SecOTRPublicDHKeyRef theirKey
,
438 uint8_t** messageKey
, uint8_t** macKey
, uint64_t **counter
)
440 SecOTRCacheElement
* emptyKeys
= NULL
;
441 SecOTRCacheElement
* cachedKeys
= NULL
;
443 int emptyPosition
= kOTRKeyCacheSize
;
446 if ((NULL
== myKey
) || (NULL
== theirKey
)) {
452 *counter
= &constant_zero
;
457 for(int i
= 0; i
< kOTRKeyCacheSize
; ++i
)
459 if (0 == timingsafe_bcmp(session
->_keyCache
[i
]._fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
)
460 && (0 == timingsafe_bcmp(session
->_keyCache
[i
]._publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
))) {
461 cachedKeys
= &session
->_keyCache
[i
];
463 secdebug("OTR","session@[%p] found key match: mk: %@, tk: %@", session
, myKey
, theirKey
);
468 if (emptyKeys
== NULL
&& SOSOTRSCacheEntryIsEmpty(&(session
->_keyCache
[i
]))) {
473 emptyKeys
= &session
->_keyCache
[i
];
477 if (cachedKeys
== NULL
) {
478 if (emptyKeys
== NULL
) {
480 WithCacheDescription(session
, ^(CFStringRef cacheDescription
) {
481 secdebug("OTR","session@[%p] Cache miss, spooky for mk: %@, tk: %@ cache: %@", session
, myKey
, theirKey
, cacheDescription
);
486 emptyKeys
= &session
->_keyCache
[0];
491 // Fill in the entry.
492 memcpy(emptyKeys
->_fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
);
493 memcpy(emptyKeys
->_publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
);
495 emptyKeys
->_counter
= 0;
496 emptyKeys
->_theirCounter
= 0;
498 SecOTRDHKGenerateOTRKeys(myKey
, theirKey
,
499 emptyKeys
->_sendEncryptionKey
, emptyKeys
->_sendMacKey
,
500 emptyKeys
->_receiveEncryptionKey
, emptyKeys
->_receiveMacKey
);
502 cachedKeys
= emptyKeys
;
504 WithCacheDescription(session
, ^(CFStringRef cacheDescription
) {
505 secdebug("OTR","mk %@, th: %@ session@[%p] new key cache state added key@[%d]: %@", myKey
, theirKey
, session
, emptyPosition
, cacheDescription
);
512 *messageKey
= sending
? cachedKeys
->_sendEncryptionKey
: cachedKeys
->_receiveEncryptionKey
;
514 *macKey
= sending
? cachedKeys
->_sendMacKey
: cachedKeys
->_receiveMacKey
;
516 *counter
= sending
? &cachedKeys
->_counter
: &cachedKeys
->_theirCounter
;
519 SecOTRSessionRef
SecOTRSessionCreateFromData(CFAllocatorRef allocator
, CFDataRef data
)
524 SecOTRSessionRef result
= NULL
;
525 SecOTRSessionRef session
= CFTypeAllocate(SecOTRSession
, struct _SecOTRSession
, allocator
);
527 uint8_t numberOfKeys
;
530 const uint8_t *bytes
= CFDataGetBytePtr(data
);
531 size_t size
= (size_t)CFDataGetLength(data
);
533 (void)SecOTRGetDefaultsWriteSeconds();
535 session
->_queue
= dispatch_queue_create("OTRSession", DISPATCH_QUEUE_SERIAL
);
538 session
->_them
= NULL
;
539 session
->_myKey
= NULL
;
540 session
->_myNextKey
= NULL
;
541 session
->_theirKey
= NULL
;
542 session
->_theirPreviousKey
= NULL
;
543 session
->_receivedDHMessage
= NULL
;
544 session
->_receivedDHKeyMessage
= NULL
;
545 session
->_textOutput
= false;
546 session
->_compactAppleMessages
= false;
547 session
->_timeToRoll
= 0;
548 session
->_stallingTheirRoll
= false;
549 session
->_stallSeconds
= 0;
550 session
->_missedAck
= true;
551 session
->_receivedAck
= false;
553 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
556 require_noerr(ReadByte(&bytes
, &size
, &version
), fail
);
557 require(version
<= 6, fail
);
559 require_noerr(ReadLong(&bytes
, &size
, &session
->_state
), fail
);
560 session
->_me
= SecOTRFullIdentityCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
, NULL
);
561 require(session
->_me
!= NULL
, fail
);
562 session
->_them
= SecOTRPublicIdentityCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
, NULL
);
563 require(session
->_them
!= NULL
, fail
);
565 require(size
> sizeof(session
->_r
), fail
);
566 memcpy(session
->_r
, bytes
, sizeof(session
->_r
));
567 bytes
+= sizeof(session
->_r
);
568 size
-= sizeof(session
->_r
);
571 uint8_t hasMessage
= false;
572 ReadByte(&bytes
, &size
, &hasMessage
);
574 session
->_receivedDHMessage
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
579 uint8_t hasMessage
= false;
580 ReadByte(&bytes
, &size
, &hasMessage
);
582 session
->_receivedDHKeyMessage
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
588 require_noerr(ReadByte(&bytes
, &size
, &ready
), fail
);
589 if (ready
&& session
->_state
== kIdle
)
590 session
->_state
= kDone
;
594 require_noerr(ReadLong(&bytes
, &size
, &session
->_keyID
), fail
);
595 if (session
->_keyID
> 0) {
596 session
->_myKey
= SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
);
597 require(session
->_myKey
!= NULL
, fail
);
598 session
->_myNextKey
= SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
);
599 require(session
->_myNextKey
!= NULL
, fail
);
603 require_noerr(ReadByte(&bytes
, &size
, &numberOfKeys
), fail
);
605 require_noerr(ReadLong(&bytes
, &size
, &session
->_theirKeyID
), fail
);
607 if (session
->_theirKeyID
> 0) {
608 if (session
->_theirKeyID
> 1) {
609 session
->_theirPreviousKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
610 require(session
->_theirPreviousKey
!= NULL
, fail
);
612 session
->_theirKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
613 require(session
->_theirKey
!= NULL
, fail
);
617 if(numberOfKeys
>= 1){
618 if (numberOfKeys
>= 2) {
619 session
->_theirPreviousKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
620 require(session
->_theirPreviousKey
!= NULL
, fail
);
622 session
->_theirKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
623 require(session
->_theirKey
!= NULL
, fail
);
629 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
630 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
631 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
632 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
633 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
634 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
635 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
636 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
637 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
638 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
639 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
640 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
641 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
642 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
643 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
644 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
646 session
->_macKeysToExpose
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
647 require(session
->_macKeysToExpose
!= NULL
, fail
);
649 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_textOutput
), fail
);
652 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_compactAppleMessages
), fail
);
655 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_includeHashes
), fail
);
658 require_noerr(ReadLongLong(&bytes
, &size
, &session
->_stallSeconds
), fail
);
659 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_stallingTheirRoll
), fail
);
660 require_noerr(ReadLongLong(&bytes
, &size
, &timeToRoll
), fail
);
661 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_missedAck
), fail
);
662 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_receivedAck
), fail
);
663 session
->_timeToRoll
= timeToRoll
;
669 CFReleaseNull(session
);
674 OSStatus
SecOTRSAppendSerialization(SecOTRSessionRef session
, CFMutableDataRef serializeInto
)
676 __block OSStatus result
= errSecParam
;
678 require(session
, abort
);
679 require(serializeInto
, abort
);
681 CFIndex start
= CFDataGetLength(serializeInto
);
683 dispatch_sync(session
->_queue
, ^{
684 const uint8_t version
= 6;
685 uint8_t numberOfKeys
= 0;
686 CFDataAppendBytes(serializeInto
, &version
, sizeof(version
));
688 AppendLong(serializeInto
, session
->_state
);
690 result
= (SecOTRFIAppendSerialization(session
->_me
, serializeInto
, NULL
)) ? errSecSuccess
: errSecParam
;
692 if (result
== errSecSuccess
) {
693 result
= (SecOTRPIAppendSerialization(session
->_them
, serializeInto
, NULL
)) ? errSecSuccess
: errSecParam
;
696 if (result
== errSecSuccess
) {
697 CFDataAppendBytes(serializeInto
, session
->_r
, sizeof(session
->_r
));
699 if (session
->_receivedDHMessage
== NULL
) {
700 AppendByte(serializeInto
, 0);
702 AppendByte(serializeInto
, 1);
703 AppendCFDataAsDATA(serializeInto
, session
->_receivedDHMessage
);
706 if (session
->_receivedDHKeyMessage
== NULL
) {
707 AppendByte(serializeInto
, 0);
709 AppendByte(serializeInto
, 1);
710 AppendCFDataAsDATA(serializeInto
, session
->_receivedDHKeyMessage
);
713 AppendLong(serializeInto
, session
->_keyID
);
714 if (session
->_keyID
> 0) {
715 SecFDHKAppendSerialization(session
->_myKey
, serializeInto
);
716 SecFDHKAppendSerialization(session
->_myNextKey
, serializeInto
);
719 if(session
->_theirPreviousKey
!= NULL
)
721 if(session
->_theirKey
!= NULL
)
724 AppendByte(serializeInto
, numberOfKeys
);
726 AppendLong(serializeInto
, session
->_theirKeyID
);
728 if (session
->_theirPreviousKey
!= NULL
)
729 SecPDHKAppendSerialization(session
->_theirPreviousKey
, serializeInto
);
731 if (session
->_theirKey
!= NULL
)
732 SecPDHKAppendSerialization(session
->_theirKey
, serializeInto
);
736 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
737 AppendLongLong(serializeInto
, *counter
);
738 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
739 AppendLongLong(serializeInto
, *counter
);
740 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
741 AppendLongLong(serializeInto
, *counter
);
742 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
743 AppendLongLong(serializeInto
, *counter
);
744 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
745 AppendLongLong(serializeInto
, *counter
);
746 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
747 AppendLongLong(serializeInto
, *counter
);
748 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
749 AppendLongLong(serializeInto
, *counter
);
750 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
751 AppendLongLong(serializeInto
, *counter
);
753 AppendCFDataAsDATA(serializeInto
, session
->_macKeysToExpose
);
755 AppendByte(serializeInto
, session
->_textOutput
? 1 : 0);
756 AppendByte(serializeInto
, session
->_compactAppleMessages
? 1 : 0);
757 AppendByte(serializeInto
, session
->_includeHashes
? 1 : 0);
759 AppendLongLong(serializeInto
, session
->_stallSeconds
? session
->_stallSeconds
: constant_zero
);
761 AppendByte(serializeInto
, session
->_stallingTheirRoll
? 1 : 0);
762 AppendLongLong(serializeInto
, (uint64_t)session
->_timeToRoll
);
763 AppendByte(serializeInto
, session
->_missedAck
? 1 : 0);
764 AppendByte(serializeInto
, session
->_receivedAck
? 1 : 0);
769 if (result
!= errSecSuccess
)
770 CFDataSetLength(serializeInto
, start
);
777 bool SecOTRSIsForKeys(SecOTRSessionRef session
, SecKeyRef myPublic
, SecKeyRef theirPublic
)
779 __block
bool isForKeys
= false;
781 dispatch_sync(session
->_queue
, ^{
782 isForKeys
= SecOTRFICompareToPublicKey(session
->_me
, myPublic
) &&
783 SecOTRPICompareToPublicKey(session
->_them
, theirPublic
);
789 bool SecOTRSGetIsReadyForMessages(SecOTRSessionRef session
)
793 dispatch_sync(session
->_queue
, ^{ result
= session
->_state
== kDone
; });
798 bool SecOTRSGetIsIdle(SecOTRSessionRef session
)
802 dispatch_sync(session
->_queue
, ^{ result
= session
->_state
== kIdle
; });
807 static void SecOTRSPrecalculateForPair(SecOTRSessionRef session
,
808 SecOTRFullDHKeyRef myKey
,
809 SecOTRPublicDHKeyRef theirKey
)
811 if (myKey
== NULL
|| theirKey
== NULL
)
814 SecOTRSFindKeysForMessage(session
, myKey
, theirKey
, true, NULL
, NULL
, NULL
);
815 SecOTRSFindKeysForMessage(session
, myKey
, theirKey
, false, NULL
, NULL
, NULL
);
818 static void SecOTRSPrecalculateKeysInternal(SecOTRSessionRef session
)
820 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirKey
);
821 SecOTRSPrecalculateForPair(session
, session
->_myNextKey
, session
->_theirKey
);
822 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirPreviousKey
);
823 SecOTRSPrecalculateForPair(session
, session
->_myNextKey
, session
->_theirPreviousKey
);
826 static void SecOTRSPrecalculateNextKeysInternal(SecOTRSessionRef session
)
828 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirKey
);
831 void SecOTRSPrecalculateKeys(SecOTRSessionRef session
)
833 dispatch_sync_f(session
->_queue
, session
, (dispatch_function_t
) SecOTRSPrecalculateKeysInternal
);
836 enum SecOTRSMessageKind
SecOTRSGetMessageKind(SecOTRSessionRef session
, CFDataRef message
)
838 OTRMessageType type
= SecOTRSGetMessageType(message
);
840 enum SecOTRSMessageKind kind
;
844 case kEvenCompactDataMessage
:
845 case kOddCompactDataMessage
:
846 case kEvenCompactDataMessageWithHashes
:
847 case kOddCompactDataMessageWithHashes
:
848 kind
= kOTRDataPacket
;
852 case kRevealSignatureMessage
:
853 case kSignatureMessage
:
854 kind
= kOTRNegotiationPacket
;
856 case kInvalidMessage
:
858 kind
= kOTRUnknownPacket
;
865 static OSStatus
SecOTRSSignAndProtectRaw_locked(SecOTRSessionRef session
,
866 CFDataRef sourceMessage
, CFMutableDataRef destinationMessage
,
867 uint8_t* messageKey
, uint8_t* macKey
, uint64_t* counter
, uint32_t theirKeyID
, SecOTRPublicDHKeyRef theirKey
)
869 CFIndex start
= CFDataGetLength(destinationMessage
);
871 AppendHeader(destinationMessage
, kDataMessage
);
872 AppendByte(destinationMessage
, 0); // Flags, all zero
874 AppendLong(destinationMessage
, session
->_keyID
);
875 AppendLong(destinationMessage
, theirKeyID
);
876 SecFDHKAppendPublicSerialization(session
->_myNextKey
, destinationMessage
);
877 AppendLongLong(destinationMessage
, ++*counter
);
879 CFIndex sourceSize
= CFDataGetLength(sourceMessage
);
880 assert(((unsigned long)sourceSize
)<=UINT32_MAX
); /* this is correct as long as CFIndex is a signed long */
881 AppendLong(destinationMessage
, (uint32_t)sourceSize
);
882 uint8_t* encryptedDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, sourceSize
);
883 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
885 (size_t)sourceSize
, CFDataGetBytePtr(sourceMessage
),
886 encryptedDataPointer
);
888 CFIndex macedContentsSize
= CFDataGetLength(destinationMessage
) - start
;
889 CFIndex macSize
= CCSHA1_OUTPUT_SIZE
;
890 uint8_t* macDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, macSize
);
893 kOTRMessageMacKeyBytes
, macKey
,
894 macedContentsSize
, CFDataGetBytePtr(destinationMessage
) + start
,
897 CFDataAppend(destinationMessage
, session
->_macKeysToExpose
);
899 return errSecSuccess
;
902 const size_t kCompactMessageMACSize
= 16;
904 static OSStatus
SecOTRSSignAndProtectCompact_locked(SecOTRSessionRef session
,
905 CFDataRef sourceMessage
, CFMutableDataRef destinationMessage
,
906 uint8_t* messageKey
, uint8_t* macKey
, uint64_t* counter
, uint32_t theirKeyID
, SecOTRPublicDHKeyRef theirKey
)
908 CFIndex start
= CFDataGetLength(destinationMessage
);
909 bool sendHashes
= session
->_includeHashes
;
911 const uint8_t messageType
= sendHashes
? ((theirKeyID
& 0x1) ? kOddCompactDataMessageWithHashes
: kEvenCompactDataMessageWithHashes
)
912 : ((theirKeyID
& 0x1) ? kOddCompactDataMessage
: kEvenCompactDataMessage
);
914 AppendByte(destinationMessage
, messageType
);
916 SecFDHKAppendCompactPublicSerialization(session
->_myNextKey
, destinationMessage
);
917 AppendLongLongCompact(destinationMessage
, ++*counter
);
919 CFIndex sourceSize
= CFDataGetLength(sourceMessage
);
920 assert(((unsigned long)sourceSize
)<=UINT32_MAX
); /* this is correct as long as CFIndex is a signed long */
921 uint8_t* encryptedDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, sourceSize
);
922 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
924 (size_t)sourceSize
, CFDataGetBytePtr(sourceMessage
),
925 encryptedDataPointer
);
928 uint8_t *senderHashPtr
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, kSecDHKHashSize
);
930 memcpy(senderHashPtr
, SecFDHKGetHash(session
->_myKey
), kSecDHKHashSize
);
932 uint8_t *receiverHashPtr
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, kSecDHKHashSize
);
934 memcpy(receiverHashPtr
, SecPDHKGetHash(theirKey
), kSecDHKHashSize
);
938 CFIndex macedContentsSize
= CFDataGetLength(destinationMessage
) - start
;
939 CFIndex macSize
= CCSHA1_OUTPUT_SIZE
;
940 uint8_t mac
[macSize
];
942 kOTRMessageMacKeyBytes
, macKey
,
943 macedContentsSize
, CFDataGetBytePtr(destinationMessage
) + start
,
946 CFDataAppendBytes(destinationMessage
, mac
, kCompactMessageMACSize
);
948 return errSecSuccess
;
951 OSStatus
SecOTRSSignAndProtectMessage(SecOTRSessionRef session
,
952 CFDataRef sourceMessage
,
953 CFMutableDataRef protectedMessage
)
955 __block OSStatus result
= errSecParam
;
957 require(session
, abort
);
958 require(sourceMessage
, abort
);
959 require(protectedMessage
, abort
);
961 if(session
->_state
!= kDone
){
962 secdebug("OTR", "Cannot sign and protect messages, we are not done negotiating sesion[%p]", session
);
963 require_quiet( session
->_state
== kDone
, abort
);
966 dispatch_sync(session
->_queue
, ^{
967 if (session
->_myKey
== NULL
||
968 session
->_theirKey
== NULL
) {
975 uint32_t theirKeyID
= session
->_theirKeyID
;
977 SecOTRPublicDHKeyRef theirKeyToUse
= session
->_theirKey
;
979 SecOTRSRollIfTime(session
);
981 if(session
->_stallingTheirRoll
&& session
->_theirPreviousKey
){
982 theirKeyToUse
= session
->_theirPreviousKey
;
983 theirKeyID
= session
->_theirKeyID
- 1;
986 SecOTRSFindKeysForMessage(session
, session
->_myKey
, theirKeyToUse
,
988 &messageKey
, &macKey
, &counter
);
989 // 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.
990 CFMutableDataRef destinationMessage
= session
->_textOutput
|| !protectedMessage
? CFDataCreateMutable(kCFAllocatorDefault
, 0) : CFRetainSafe(protectedMessage
);
992 result
= session
->_compactAppleMessages
? SecOTRSSignAndProtectCompact_locked(session
, sourceMessage
, destinationMessage
, messageKey
, macKey
, counter
, theirKeyID
, theirKeyToUse
)
993 : SecOTRSSignAndProtectRaw_locked(session
, sourceMessage
, destinationMessage
, messageKey
, macKey
, counter
, theirKeyID
, theirKeyToUse
);
995 if (result
== errSecSuccess
) {
996 if (session
->_textOutput
) {
997 SecOTRPrepareOutgoingBytes(destinationMessage
, protectedMessage
);
1000 CFDataSetLength(session
->_macKeysToExpose
, 0);
1003 CFReleaseSafe(destinationMessage
);
1005 result
= errSecSuccess
;
1012 void SecOTRSKickTimeToRoll(SecOTRSessionRef session
){
1013 session
->_timeToRoll
= CFAbsoluteTimeGetCurrent();
1016 static void SecOTRAcceptNewRemoteKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef newKey
)
1018 if (session
->_theirPreviousKey
) {
1019 SecOTRSExpireCachedKeysForPublicKey(session
, session
->_theirPreviousKey
);
1022 CFReleaseNull(session
->_theirPreviousKey
);
1023 session
->_theirPreviousKey
= session
->_theirKey
;
1024 session
->_theirKey
= CFRetainSafe(newKey
);
1025 session
->_stallingTheirRoll
= true;
1027 session
->_theirKeyID
+= 1;
1029 SecOTRSEnableTimeToRoll(session
);
1032 OSStatus
SecOTRSetupInitialRemoteKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef CF_CONSUMED initialKey
) {
1034 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
1036 CFReleaseNull(session
->_theirPreviousKey
);
1037 CFAssignRetained(session
->_theirKey
, initialKey
);
1038 session
->_theirKeyID
= 1;
1040 return errSecSuccess
;
1044 /// MARK: SLOW ROLLING
1047 void SOSOTRSRoll(SecOTRSessionRef session
){
1049 session
->_stallingTheirRoll
= false;
1051 //receiving side roll
1052 if(session
->_receivedAck
){
1053 SecOTRGenerateNewProposedKey(session
);
1054 session
->_missedAck
= false;
1055 session
->_receivedAck
= false;
1058 session
->_missedAck
= true;
1062 static OSStatus
SecOTRVerifyAndExposeRaw_locked(SecOTRSessionRef session
,
1063 CFDataRef decodedBytes
,
1064 CFMutableDataRef exposedMessageContents
)
1066 OSStatus result
= errSecDecode
;
1068 SecOTRPublicDHKeyRef newKey
= NULL
;
1069 const uint8_t* bytes
;
1071 SecOTRFullDHKeyRef myKeyForMessage
= NULL
;
1072 SecOTRPublicDHKeyRef theirKeyForMessage
= NULL
;
1073 bytes
= CFDataGetBytePtr(decodedBytes
);
1074 size
= CFDataGetLength(decodedBytes
);
1076 const uint8_t* macDataStart
= bytes
;
1081 require_noerr_quiet(result
= ReadAndVerifyHeader(&bytes
, &size
, kDataMessage
), fail
);
1082 require_action_quiet(size
> 0, fail
, result
= errSecDecode
);
1084 require_noerr_quiet(result
= ReadAndVerifyByte(&bytes
, &size
, 0), fail
); // Flags, always zero
1086 require_noerr_quiet(result
= ReadLong(&bytes
, &size
, &theirID
), fail
);
1088 require_action_quiet(theirID
== session
->_theirKeyID
|| (theirID
== (session
->_theirKeyID
- 1) && session
->_theirPreviousKey
!= NULL
),
1090 result
= ((theirID
+ 1) < session
->_theirKeyID
) ? errSecOTRTooOld
: errSecOTRIDTooNew
);
1092 require_noerr_quiet(result
= ReadLong(&bytes
, &size
, &myID
), fail
);
1094 require_action_quiet(myID
== session
->_keyID
|| (myID
== session
->_keyID
+ 1 && session
->_myNextKey
!= NULL
),
1096 result
= (myID
< session
->_keyID
) ? errSecOTRTooOld
: errSecOTRIDTooNew
);
1099 // Choose appripriate keys for message:
1101 uint8_t *messageKey
;
1103 uint64_t *theirCounter
;
1105 myKeyForMessage
= (myID
== session
->_keyID
) ? session
->_myKey
: session
->_myNextKey
;
1106 theirKeyForMessage
= (theirID
== session
->_theirKeyID
) ? session
->_theirKey
: session
->_theirPreviousKey
;
1108 SecOTRSFindKeysForMessage(session
, myKeyForMessage
, theirKeyForMessage
, false,
1109 &messageKey
, &macKey
, &theirCounter
);
1111 size_t nextKeyMPISize
;
1112 const uint8_t* nextKeyMPIBytes
;
1113 require_noerr_quiet(result
= SizeAndSkipMPI(&bytes
, &size
, &nextKeyMPIBytes
, &nextKeyMPISize
), fail
);
1116 require_noerr_quiet(result
= ReadLongLong(&bytes
, &size
, &counter
), fail
);
1117 require_action_quiet(counter
> *theirCounter
, fail
, result
= errSecOTRTooOld
);
1120 const uint8_t* messageStart
;
1121 require_noerr_quiet(result
= SizeAndSkipDATA(&bytes
, &size
, &messageStart
, &messageSize
), fail
);
1123 size_t macDataSize
= (bytes
- macDataStart
) ? (size_t)(bytes
- macDataStart
) : 0;
1124 uint8_t mac
[CCSHA1_OUTPUT_SIZE
];
1125 require_action_quiet(sizeof(mac
) <= size
, fail
, result
= errSecDecode
);
1128 kOTRMessageMacKeyBytes
, macKey
,
1129 macDataSize
, macDataStart
,
1132 require_noerr_action_quiet(timingsafe_bcmp(mac
, bytes
, sizeof(mac
)), fail
, result
= errSecAuthFailed
);
1134 uint8_t* dataSpace
= CFDataIncreaseLengthAndGetMutableBytes(exposedMessageContents
, (CFIndex
)messageSize
);
1136 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
1138 messageSize
, messageStart
,
1141 // Everything is good, accept the meta data.
1142 *theirCounter
= counter
;
1144 newKey
= SecOTRPublicDHKCreateFromBytes(kCFAllocatorDefault
, &nextKeyMPIBytes
, &nextKeyMPISize
);
1147 bool acceptTheirNewKey
= newKey
!= NULL
&& theirID
== session
->_theirKeyID
;
1149 if (acceptTheirNewKey
) {
1150 SecOTRAcceptNewRemoteKey(session
, newKey
);
1153 if (myID
== (session
->_keyID
+ 1)) {
1154 SecOTRSHandleProposalAcknowledge(session
);
1157 SecOTRSRollIfTime(session
);
1159 SecOTRSPrecalculateNextKeysInternal(session
);
1162 if(result
!= errSecSuccess
){
1163 CFDataPerformWithHexString(decodedBytes
, ^(CFStringRef decodedBytesString
) {
1164 SecOTRPublicIdentityRef myPublicID
= SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault
, session
->_me
, NULL
);
1165 SecOTRPIPerformWithSerializationString(myPublicID
, ^(CFStringRef myIDString
) {
1166 SecOTRPIPerformWithSerializationString(session
->_them
, ^(CFStringRef theirIDString
) {
1167 secnotice("OTR","session[%p] failed to decrypt, session: %@, mk: %@, mpk: %@, tpk: %@, tk: %@, chose tktu: %@", session
, session
,
1168 session
->_myKey
, session
->_myNextKey
, session
->_theirPreviousKey
, session
->_theirKey
, theirKeyForMessage
);
1169 secnotice("OTR","session[%p] failed to decrypt, mktu: %@, mpi: %@, tpi: %@, m: %@", session
, myKeyForMessage
, myIDString
, theirIDString
, decodedBytesString
);
1172 CFReleaseNull(myPublicID
);
1175 CFReleaseNull(newKey
);
1179 static OSStatus
SecOTRVerifyAndExposeRawCompact_locked(SecOTRSessionRef session
,
1180 CFDataRef decodedBytes
,
1181 CFMutableDataRef exposedMessageContents
)
1183 SecOTRPublicDHKeyRef theirProposal
= NULL
;
1184 OSStatus result
= errSecDecode
;
1185 const uint8_t* bytes
;
1187 SecOTRPublicDHKeyRef theirKeyForMessage
= NULL
;
1188 bytes
= CFDataGetBytePtr(decodedBytes
);
1189 size
= CFDataGetLength(decodedBytes
);
1190 SecOTRFullDHKeyRef myKeyForMessage
= NULL
;
1191 const uint8_t* macDataStart
= bytes
;
1192 bool useEvenKey
= false;
1193 bool useCurrentKey
= false;
1194 bool sentHashes
= false;
1195 uint64_t counter
= 0;
1197 uint8_t type_byte
= 0;
1198 require_noerr_quiet(result
= ReadByte(&bytes
, &size
, &type_byte
), fail
);
1199 require_action_quiet(type_byte
== kOddCompactDataMessage
|| type_byte
== kEvenCompactDataMessage
1200 || type_byte
== kOddCompactDataMessageWithHashes
|| type_byte
== kEvenCompactDataMessageWithHashes
, fail
, result
= errSecDecode
);
1202 useEvenKey
= (type_byte
== kEvenCompactDataMessage
|| type_byte
== kEvenCompactDataMessageWithHashes
);
1203 sentHashes
= (type_byte
== kOddCompactDataMessageWithHashes
|| type_byte
== kEvenCompactDataMessageWithHashes
);
1205 useCurrentKey
= useEvenKey
^ (session
->_keyID
& 1);
1206 myKeyForMessage
= useCurrentKey
? session
->_myKey
: session
->_myNextKey
;
1208 require_action_quiet(myKeyForMessage
, fail
, result
= errSecDecode
);
1210 theirProposal
= SecOTRPublicDHKCreateFromCompactSerialization(kCFAllocatorDefault
, &bytes
, &size
);
1212 require_action_quiet(theirProposal
, fail
, result
= errSecDecode
);
1214 bool proposalIsNew
= !CFEqualSafe(theirProposal
, session
->_theirKey
);
1215 theirKeyForMessage
= proposalIsNew
? session
->_theirKey
: session
->_theirPreviousKey
;
1217 require_action_quiet(theirKeyForMessage
, fail
, result
= errSecDecode
);
1219 uint8_t *messageKey
;
1221 uint64_t *theirCounter
;
1224 SecOTRSFindKeysForMessage(session
, myKeyForMessage
, theirKeyForMessage
, false, &messageKey
, &macKey
, &theirCounter
);
1226 require_noerr_quiet(result
= ReadLongLongCompact(&bytes
, &size
, &counter
), fail
);
1227 require_action_quiet(counter
> *theirCounter
, fail
, result
= errSecOTRTooOld
);
1229 size_t messageSize
= size
- kCompactMessageMACSize
- (sentHashes
? 2 * kSecDHKHashSize
: 0); // It's all message except for the MAC and maybe hashes
1230 const uint8_t* messageStart
= bytes
;
1232 bytes
+= messageSize
;
1233 size
-= messageSize
;
1236 // Sender then receiver keys
1238 if (memcmp(SecPDHKGetHash(theirKeyForMessage
), bytes
, kSecDHKHashSize
) != 0) {
1239 // Wrong sender key WTF.
1241 BufferPerformWithHexString(bytes
, kSecDHKHashSize
, ^(CFStringRef dataString
) {
1242 secdebug("OTR","session[%p] Sender key hash doesn't match: %@ != %@", session
, theirKeyForMessage
, dataString
);
1247 bytes
+= kSecDHKHashSize
;
1248 size
-= kSecDHKHashSize
;
1250 if (memcmp(SecFDHKGetHash(myKeyForMessage
), bytes
, kSecDHKHashSize
) != 0) {
1251 // Wrong sender key WTF.
1253 BufferPerformWithHexString(bytes
, kSecDHKHashSize
, ^(CFStringRef dataString
) {
1254 secdebug("OTR","session[%p] Receiver key hash doesn't match: %@ != %@", session
, myKeyForMessage
, dataString
);
1259 bytes
+= kSecDHKHashSize
;
1260 size
-= kSecDHKHashSize
;
1264 uint8_t mac
[CCSHA1_OUTPUT_SIZE
];
1265 require_action_quiet(kCompactMessageMACSize
== size
, fail
, result
= errSecDecode
); // require space for the mac and some bytes
1267 size_t macDataSize
= (size_t)(bytes
- macDataStart
);
1270 kOTRMessageMacKeyBytes
, macKey
,
1271 macDataSize
, macDataStart
,
1274 require_noerr_action_quiet(timingsafe_bcmp(mac
, bytes
, kCompactMessageMACSize
), fail
, result
= errSecAuthFailed
);
1276 uint8_t* dataSpace
= CFDataIncreaseLengthAndGetMutableBytes(exposedMessageContents
, (CFIndex
)messageSize
);
1278 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
1280 messageSize
, messageStart
,
1283 // Everything is good, accept the meta data.
1284 *theirCounter
= counter
;
1286 if (proposalIsNew
) {
1287 SecOTRAcceptNewRemoteKey(session
, theirProposal
);
1290 if (!useCurrentKey
) {
1291 SecOTRSHandleProposalAcknowledge(session
);
1293 SecOTRSRollIfTime(session
);
1295 SecOTRSPrecalculateNextKeysInternal(session
);
1298 if(result
!= errSecSuccess
){
1299 CFDataPerformWithHexString(decodedBytes
, ^(CFStringRef decodedBytesString
) {
1300 SecOTRPublicIdentityRef myPublicID
= SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault
, session
->_me
, NULL
);
1301 SecOTRPIPerformWithSerializationString(myPublicID
, ^(CFStringRef myIDString
) {
1302 SecOTRPIPerformWithSerializationString(session
->_them
, ^(CFStringRef theirIDString
) {
1303 secnotice("OTR","session[%p] failed to decrypt, session: %@, mk: %@, mpk: %@, tpk: %@, tk: %@, chose tktu: %@", session
, session
,
1304 session
->_myKey
, session
->_myNextKey
, session
->_theirPreviousKey
, session
->_theirKey
, theirKeyForMessage
);
1305 secnotice("OTR","session[%p] failed to decrypt, mktu: %@, mpi: %@, tpi: %@, m: %@, tP: %@, tb: %hhx", session
, myKeyForMessage
, myIDString
, theirIDString
, decodedBytesString
, theirProposal
, type_byte
);
1308 CFReleaseNull(myPublicID
);
1311 CFReleaseNull(theirProposal
);
1316 OSStatus
SecOTRSVerifyAndExposeMessage(SecOTRSessionRef session
,
1317 CFDataRef incomingMessage
,
1318 CFMutableDataRef exposedMessageContents
)
1320 __block OSStatus result
= errSecParam
;
1323 require(session
, abort
);
1324 require(incomingMessage
, abort
);
1325 require(exposedMessageContents
, abort
);
1327 if(session
->_state
== kDone
){
1328 dispatch_sync(session
->_queue
, ^{
1329 CFDataRef decodedBytes
= SecOTRCopyIncomingBytes(incomingMessage
);
1331 OTRMessageType messageType
= SecOTRSGetMessageType(decodedBytes
);
1333 switch (messageType
) {
1335 result
= SecOTRVerifyAndExposeRaw_locked(session
, decodedBytes
, exposedMessageContents
);
1338 case kOddCompactDataMessage
:
1339 case kEvenCompactDataMessage
:
1340 case kOddCompactDataMessageWithHashes
:
1341 case kEvenCompactDataMessageWithHashes
:
1342 result
= SecOTRVerifyAndExposeRawCompact_locked(session
, decodedBytes
, exposedMessageContents
);
1346 result
= errSecUnsupportedFormat
;
1350 CFReleaseSafe(decodedBytes
);
1354 secnotice("OTR", "session[%p]Cannot process message:%@, session is not done negotiating, session state: %@", session
, incomingMessage
, session
);
1355 result
= errSecOTRNotReady
;
1362 static CFDataRef
data_to_data_error_request(enum SecXPCOperation op
, CFDataRef publicPeerId
, CFErrorRef
*error
) {
1363 __block CFDataRef result
= NULL
;
1364 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1365 return SecXPCDictionarySetDataOptional(message
, kSecXPCPublicPeerId
, publicPeerId
, error
);
1366 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1367 return (result
= SecXPCDictionaryCopyData(response
, kSecXPCKeyResult
, error
));
1372 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
) {
1373 __block CFDataRef tempOutputSessionData
= NULL
;
1374 __block CFDataRef tempOutputPacket
= NULL
;
1375 __block
bool tempReadyForMessages
= false;
1377 bool result
= securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1378 return SecXPCDictionarySetDataOptional(message
, kSecXPCOTRSession
, sessionData
, error
)
1379 && SecXPCDictionarySetDataOptional(message
, kSecXPCData
, inputPacket
, error
);
1380 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1381 if (xpc_dictionary_get_bool(response
, kSecXPCKeyResult
)) {
1382 tempOutputSessionData
= SecXPCDictionaryCopyData(response
, kSecXPCOTRSession
, error
);
1383 tempOutputPacket
= SecXPCDictionaryCopyData(response
, kSecXPCData
, error
);
1384 tempReadyForMessages
= xpc_dictionary_get_bool(response
, kSecXPCOTRReady
);
1392 *outputSessionData
= tempOutputSessionData
;
1393 *outputPacket
= tempOutputPacket
;
1394 *readyForMessages
= tempReadyForMessages
;
1400 CFDataRef
SecOTRSessionCreateRemote(CFDataRef publicPeerId
, CFErrorRef
*error
) {
1401 __block CFDataRef result
;
1402 os_activity_initiate("SecOTRSessionCreateRemote", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1403 (void)SecOTRGetDefaultsWriteSeconds();
1404 result
= SECURITYD_XPC(sec_otr_session_create_remote
, data_to_data_error_request
, publicPeerId
, error
);
1409 bool SecOTRSessionProcessPacketRemote(CFDataRef sessionData
, CFDataRef inputPacket
, CFDataRef
* outputSessionData
, CFDataRef
* outputPacket
, bool *readyForMessages
, CFErrorRef
*error
) {
1410 __block
bool result
;
1411 os_activity_initiate("SecOTRSessionProcessPacketRemote", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1412 result
= SECURITYD_XPC(sec_otr_session_process_packet_remote
, data_data_to_data_data_bool_error_request
, sessionData
, inputPacket
, outputSessionData
, outputPacket
, readyForMessages
, error
);