2 * Copyright (c) 2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <sys/types.h>
27 #include "utilities/comparison.h"
28 #include <CoreFoundation/CFDate.h>
30 #include "SecOTRSession.h"
32 #include "SecOTRMath.h"
33 #include "SecOTRDHKey.h"
34 #include "SecOTRSessionPriv.h"
35 #include "SecOTRPackets.h"
36 #include "SecOTRPacketData.h"
37 #include "SecOTRIdentityPriv.h"
39 #include <utilities/SecCFWrappers.h>
41 #include <CoreFoundation/CFRuntime.h>
42 #include <CoreFoundation/CFString.h>
44 #include <Security/SecBasePriv.h>
45 #include <Security/SecRandom.h>
46 #include <Security/SecBase64.h>
47 #include <Security/SecKeyPriv.h>
49 #include <Security/SecureObjectSync/SOSPeerInfo.h>
50 #include <Security/SecureObjectSync/SOSCircle.h>
51 #include <Security/SecureObjectSync/SOSCloudCircle.h>
52 #include <Security/SecureObjectSync/SOSInternal.h>
53 #include <Security/SecureObjectSync/SOSUserKeygen.h>
55 #include <AssertMacros.h>
57 #include <corecrypto/cchmac.h>
58 #include <corecrypto/ccsha2.h>
59 #include <corecrypto/ccsha1.h>
65 #include <os/activity.h>
67 #include <utilities/array_size.h>
69 #include <ipc/securityd_client.h>
70 #include <Security/SecuritydXPC.h>
72 CFGiblisFor(SecOTRSession
);
74 static uint64_t setup_defaults_settings(){
76 Boolean keyExistsAndHasValue
= false;
78 seconds
= CFPreferencesGetAppIntegerValue(CFSTR("OTR"), CFSTR("com.apple.security"), &keyExistsAndHasValue
);
79 secdebug("OTR", "Retrieving OTR default settings was success? %d value retrieved: %llu", keyExistsAndHasValue
, seconds
);
80 return keyExistsAndHasValue
? seconds
: (kSecondsPerMinute
* 15); //15 minutes by default
83 static uint64_t SecOTRGetDefaultsWriteSeconds(void) {
84 static dispatch_once_t sdOnceToken
;
85 static uint64_t seconds
;
87 dispatch_once(&sdOnceToken
, ^{
88 seconds
= setup_defaults_settings();
94 static void SecOTRSEnableTimeToRoll(SecOTRSessionRef session
){
95 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
96 CFAbsoluteTime nextTimeToRoll
= now
+ session
->_stallSeconds
;
98 if(session
->_timeToRoll
== 0 || session
->_timeToRoll
> nextTimeToRoll
){
99 session
->_timeToRoll
= nextTimeToRoll
;
103 static void SecOTRSExpireCachedKeysForFullKey(SecOTRSessionRef session
, SecOTRFullDHKeyRef myKey
)
105 for(int i
= 0; i
< kOTRKeyCacheSize
; ++i
)
107 if (0 == constant_memcmp(session
->_keyCache
[i
]._fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
)) {
108 CFDataAppendBytes(session
->_macKeysToExpose
, session
->_keyCache
[i
]._receiveMacKey
, sizeof(session
->_keyCache
[i
]._receiveMacKey
));
109 bzero(&session
->_keyCache
[i
], sizeof(session
->_keyCache
[i
]));
114 static void SecOTRSExpireCachedKeysForPublicKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef theirKey
)
116 for(int i
= 0; i
< kOTRKeyCacheSize
; ++i
)
118 if (0 == constant_memcmp(session
->_keyCache
[i
]._publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
)) {
119 CFDataAppendBytes(session
->_macKeysToExpose
, session
->_keyCache
[i
]._receiveMacKey
, sizeof(session
->_keyCache
[i
]._receiveMacKey
));
121 bzero(&session
->_keyCache
[i
], sizeof(session
->_keyCache
[i
]));
126 static void SecOTRGenerateNewProposedKey(SecOTRSessionRef session
)
128 SecOTRSExpireCachedKeysForFullKey(session
, session
->_myKey
);
130 // Swap the keys so we know the current key.
132 SecOTRFullDHKeyRef oldKey
= session
->_myKey
;
133 session
->_myKey
= session
->_myNextKey
;
134 session
->_myNextKey
= oldKey
;
137 // Derive a new next key by regenerating over the old key.
138 SecFDHKNewKey(session
->_myNextKey
);
140 session
->_keyID
+= 1;
144 static void SecOTRSHandleProposalAcknowledge(SecOTRSessionRef session
){
145 if(session
->_missedAck
){
146 SecOTRGenerateNewProposedKey(session
);
147 session
->_missedAck
= false;
150 session
->_receivedAck
= true;
151 SecOTRSEnableTimeToRoll(session
);
155 static void SecOTRSRollIfTime(SecOTRSessionRef session
){
157 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
158 CFAbsoluteTime longestTimeToRoll
= now
+ session
->_stallSeconds
;
160 //in case time to roll becomes too large we're going to roll now!
161 if(session
->_timeToRoll
< now
|| session
->_timeToRoll
> longestTimeToRoll
){
162 SOSOTRSRoll(session
);
163 session
->_timeToRoll
= 0;
168 static OTRMessageType
SecOTRSGetMessageType(CFDataRef message
)
170 OTRMessageType type
= kInvalidMessage
;
172 CFDataRef decodedBytes
= SecOTRCopyIncomingBytes(message
);
174 const uint8_t *bytes
= CFDataGetBytePtr(decodedBytes
);
175 size_t size
= CFDataGetLength(decodedBytes
);
177 if (noErr
!= ReadHeader(&bytes
, &size
, &type
)) {
178 uint8_t firstByte
= *CFDataGetBytePtr(decodedBytes
);
180 case kOddCompactDataMessage
:
181 case kEvenCompactDataMessage
:
182 case kOddCompactDataMessageWithHashes
:
183 case kEvenCompactDataMessageWithHashes
:
192 CFReleaseNull(decodedBytes
);
199 static CFStringRef
SecOTRCacheElementCopyDescription(SecOTRCacheElement
*keyCache
){
200 __block CFStringRef description
= NULL
;
201 BufferPerformWithHexString(keyCache
->_fullKeyHash
, sizeof(keyCache
->_fullKeyHash
), ^(CFStringRef fullKeyHashString
) {
202 BufferPerformWithHexString(keyCache
->_publicKeyHash
,sizeof(keyCache
->_publicKeyHash
), ^(CFStringRef publicKeyHashString
) {
203 description
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("fkh: [%@], pkh: [%@], c: %llu tc: %llu"), fullKeyHashString
, publicKeyHashString
, keyCache
->_counter
, keyCache
->_theirCounter
);
210 const char *SecOTRPacketTypeString(CFDataRef message
)
212 if (!message
) return "NoMessage";
213 switch (SecOTRSGetMessageType(message
)) {
214 case kDHMessage
: return "DHMessage (0x02)";
215 case kDataMessage
: return "DataMessage (0x03)";
216 case kDHKeyMessage
: return "DHKeyMessage (0x0A)";
217 case kRevealSignatureMessage
: return "RevealSignatureMessage (0x11)";
218 case kSignatureMessage
: return "SignatureMessage (0x12)";
219 case kEvenCompactDataMessage
: return "kEvenCompactDatamessage (0x20)";
220 case kOddCompactDataMessage
: return "kOddCompactDataMessage (0x21)";
221 case kEvenCompactDataMessageWithHashes
: return "kEvenCompactDatamessage (0x30)";
222 case kOddCompactDataMessageWithHashes
: return "kOddCompactDataMessage (0x31)";
223 case kInvalidMessage
: return "InvalidMessage (0xFF)";
224 default: return "UnknownMessage";
228 static const char *SecOTRAuthStateString(SecOTRAuthState authState
)
231 case kIdle
: return "Idle";
232 case kAwaitingDHKey
: return "AwaitingDHKey";
233 case kAwaitingRevealSignature
: return "AwaitingRevealSignature";
234 case kAwaitingSignature
: return "AwaitingSignature";
235 case kDone
: return "Done";
236 default: return "InvalidState";
240 static CF_RETURNS_RETAINED CFStringRef
SecOTRSessionCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
241 SecOTRSessionRef session
= (SecOTRSessionRef
)cf
;
243 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
245 return CFStringCreateWithFormat(kCFAllocatorDefault
,NULL
,CFSTR("<%s %s %s %s%s%s%s %d:%d %s%s %llu %s%s%s%s>"),
246 SecOTRAuthStateString(session
->_state
),
247 session
->_compactAppleMessages
? "C" :"c",
248 session
->_includeHashes
? "I" : "i",
249 session
->_me
? "F" : "f",
250 session
->_them
? "P" : "p",
251 session
->_receivedDHMessage
? "D" : "d",
252 session
->_receivedDHKeyMessage
? "K" : "k",
254 session
->_theirKeyID
,
255 session
->_theirPreviousKey
? "P" : "p",
256 session
->_theirKey
? "T" : "t",
257 session
->_stallSeconds
,
258 session
->_missedAck
? "M" : "m",
259 session
->_receivedAck
? "R" : "r",
260 session
->_stallingTheirRoll
? "S" : "s",
261 (session
->_timeToRoll
> now
&& session
->_timeToRoll
!= 0) ? "E" : "e");
264 static void SecOTRSessionDestroy(CFTypeRef cf
) {
265 SecOTRSessionRef session
= (SecOTRSessionRef
)cf
;
267 CFReleaseNull(session
->_receivedDHMessage
);
268 CFReleaseNull(session
->_receivedDHKeyMessage
);
270 CFReleaseNull(session
->_me
);
271 CFReleaseNull(session
->_myKey
);
272 CFReleaseNull(session
->_myNextKey
);
274 CFReleaseNull(session
->_them
);
275 CFReleaseNull(session
->_theirKey
);
276 CFReleaseNull(session
->_theirPreviousKey
);
278 CFReleaseNull(session
->_macKeysToExpose
);
280 dispatch_release(session
->_queue
);
283 static void SecOTRSessionResetInternal(SecOTRSessionRef session
)
285 session
->_state
= kIdle
;
287 CFReleaseNull(session
->_receivedDHMessage
);
288 CFReleaseNull(session
->_receivedDHKeyMessage
);
291 CFReleaseNull(session
->_myKey
);
292 CFReleaseNull(session
->_myNextKey
);
293 //session->_myNextKey = SecOTRFullDHKCreate(kCFAllocatorDefault);
294 session
->_theirKeyID
= 0;
295 CFReleaseNull(session
->_theirKey
);
296 CFReleaseNull(session
->_theirPreviousKey
);
297 CFReleaseNull(session
->_macKeysToExpose
);
298 session
->_macKeysToExpose
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
300 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
303 int SecOTRSGetKeyID(SecOTRSessionRef session
){
304 return session
->_keyID
;
307 int SecOTRSGetTheirKeyID(SecOTRSessionRef session
){
308 return session
->_theirKeyID
;
311 void SecOTRSessionReset(SecOTRSessionRef session
)
313 dispatch_sync_f(session
->_queue
, session
, (dispatch_function_t
) SecOTRSessionResetInternal
);
317 static void SecOTRPIPerformWithSerializationString(SecOTRPublicIdentityRef id
, void (^action
)(CFStringRef string
)) {
318 CFMutableDataRef idData
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
319 SecOTRPIAppendSerialization(id
, idData
, NULL
);
320 CFDataPerformWithHexString(idData
, action
);
321 CFReleaseNull(idData
);
324 SecOTRSessionRef
SecOTRSessionCreateFromID(CFAllocatorRef allocator
,
325 SecOTRFullIdentityRef myID
,
326 SecOTRPublicIdentityRef theirID
)
328 SecOTRSessionRef newID
= CFTypeAllocate(SecOTRSession
, struct _SecOTRSession
, allocator
);
330 (void)SecOTRGetDefaultsWriteSeconds();
331 newID
->_queue
= dispatch_queue_create("OTRSession", DISPATCH_QUEUE_SERIAL
);
333 newID
->_me
= CFRetainSafe(myID
);
334 newID
->_them
= CFRetainSafe(theirID
);
335 newID
->_receivedDHMessage
= NULL
;
336 newID
->_receivedDHKeyMessage
= NULL
;
337 newID
->_myKey
= NULL
;
338 newID
->_myNextKey
= NULL
;
339 newID
->_theirKey
= NULL
;
340 newID
->_theirPreviousKey
= NULL
;
341 newID
->_macKeysToExpose
= NULL
;
342 newID
->_textOutput
= false;
343 newID
->_compactAppleMessages
= false;
344 newID
->_includeHashes
= false;
346 newID
->_timeToRoll
= 0;
347 newID
->_stallingTheirRoll
= false;
348 newID
->_stallSeconds
= 0;
349 newID
->_missedAck
= true;
350 newID
->_receivedAck
= false;
352 SecOTRSessionResetInternal(newID
);
355 SecOTRPublicIdentityRef myPublicID
= SecOTRPublicIdentityCopyFromPrivate(kCFAllocatorDefault
, newID
->_me
, NULL
);
356 SecOTRPIPerformWithSerializationString(myPublicID
, ^(CFStringRef myIDString
) {
357 SecOTRPIPerformWithSerializationString(newID
->_them
, ^(CFStringRef theirIDString
) {
358 secnotice("otr", "%@ Creating with M: %@, T: %@", newID
, myIDString
, theirIDString
);
361 CFReleaseNull(myPublicID
);
367 SecOTRSessionRef
SecOTRSessionCreateFromIDAndFlags(CFAllocatorRef allocator
,
368 SecOTRFullIdentityRef myID
,
369 SecOTRPublicIdentityRef theirID
,
373 uint64_t seconds
= SecOTRGetDefaultsWriteSeconds();
375 SecOTRSessionRef newID
= SecOTRSessionCreateFromID(allocator
, myID
, theirID
);
376 if (flags
& kSecOTRSendTextMessages
) {
377 newID
->_textOutput
= true;
379 if (flags
& kSecOTRUseAppleCustomMessageFormat
) {
380 newID
->_compactAppleMessages
= true;
382 if(flags
& kSecOTRIncludeHashesInMessages
)
384 newID
->_includeHashes
= true;
386 if(flags
& kSecOTRSlowRoll
)
388 newID
->_stallSeconds
= seconds
;
394 static uint64_t constant_zero
= 0;
396 static bool hashIsZero(uint8_t hash
[CCSHA1_OUTPUT_SIZE
])
399 for(size_t byte
= 0; isZero
&& byte
< CCSHA1_OUTPUT_SIZE
; ++byte
)
400 isZero
= (0 == hash
[byte
]);
405 static bool SOSOTRSCacheEntryIsEmpty(SecOTRCacheElement
*element
)
407 return hashIsZero(element
->_fullKeyHash
) && hashIsZero(element
->_publicKeyHash
);
412 static void WithCacheDescription(SecOTRSessionRef session
, void (^operation
)(CFStringRef cacheDescription
)) {
413 CFStringRef description
= NULL
;
415 CFStringRef keyCache0Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[0]);
416 CFStringRef keyCache1Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[1]);
417 CFStringRef keyCache2Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[2]);
418 CFStringRef keyCache3Description
= SecOTRCacheElementCopyDescription(&session
->_keyCache
[3]);
420 description
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("{%@, %@, %@, %@}"), keyCache0Description
, keyCache1Description
, keyCache2Description
, keyCache3Description
);
422 operation(description
);
424 CFReleaseNull(keyCache0Description
);
425 CFReleaseNull(keyCache1Description
);
426 CFReleaseNull(keyCache2Description
);
427 CFReleaseNull(keyCache3Description
);
428 CFReleaseNull(description
);
433 static void SecOTRSFindKeysForMessage(SecOTRSessionRef session
,
434 SecOTRFullDHKeyRef myKey
,
435 SecOTRPublicDHKeyRef theirKey
,
437 uint8_t** messageKey
, uint8_t** macKey
, uint64_t **counter
)
439 SecOTRCacheElement
* emptyKeys
= NULL
;
440 SecOTRCacheElement
* cachedKeys
= NULL
;
442 int emptyPosition
= kOTRKeyCacheSize
;
445 if ((NULL
== myKey
) || (NULL
== theirKey
)) {
451 *counter
= &constant_zero
;
456 for(int i
= 0; i
< kOTRKeyCacheSize
; ++i
)
458 if (0 == constant_memcmp(session
->_keyCache
[i
]._fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
)
459 && (0 == constant_memcmp(session
->_keyCache
[i
]._publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
))) {
460 cachedKeys
= &session
->_keyCache
[i
];
462 secdebug("OTR","session@[%p] found key match: mk: %@, tk: %@", session
, myKey
, theirKey
);
467 if (emptyKeys
== NULL
&& SOSOTRSCacheEntryIsEmpty(&(session
->_keyCache
[i
]))) {
472 emptyKeys
= &session
->_keyCache
[i
];
476 if (cachedKeys
== NULL
) {
477 if (emptyKeys
== NULL
) {
479 WithCacheDescription(session
, ^(CFStringRef cacheDescription
) {
480 secdebug("OTR","session@[%p] Cache miss, spooky for mk: %@, tk: %@ cache: %@", session
, myKey
, theirKey
, cacheDescription
);
485 emptyKeys
= &session
->_keyCache
[0];
489 // Fill in the entry.
490 memcpy(emptyKeys
->_fullKeyHash
, SecFDHKGetHash(myKey
), CCSHA1_OUTPUT_SIZE
);
491 memcpy(emptyKeys
->_publicKeyHash
, SecPDHKGetHash(theirKey
), CCSHA1_OUTPUT_SIZE
);
493 emptyKeys
->_counter
= 0;
494 emptyKeys
->_theirCounter
= 0;
496 SecOTRDHKGenerateOTRKeys(myKey
, theirKey
,
497 emptyKeys
->_sendEncryptionKey
, emptyKeys
->_sendMacKey
,
498 emptyKeys
->_receiveEncryptionKey
, emptyKeys
->_receiveMacKey
);
500 cachedKeys
= emptyKeys
;
502 WithCacheDescription(session
, ^(CFStringRef cacheDescription
) {
503 secdebug("OTR","mk %@, th: %@ session@[%p] new key cache state added key@[%d]: %@", myKey
, theirKey
, session
, emptyPosition
, cacheDescription
);
510 *messageKey
= sending
? cachedKeys
->_sendEncryptionKey
: cachedKeys
->_receiveEncryptionKey
;
512 *macKey
= sending
? cachedKeys
->_sendMacKey
: cachedKeys
->_receiveMacKey
;
514 *counter
= sending
? &cachedKeys
->_counter
: &cachedKeys
->_theirCounter
;
517 SecOTRSessionRef
SecOTRSessionCreateFromData(CFAllocatorRef allocator
, CFDataRef data
)
522 SecOTRSessionRef result
= NULL
;
523 SecOTRSessionRef session
= CFTypeAllocate(SecOTRSession
, struct _SecOTRSession
, allocator
);
525 uint8_t numberOfKeys
;
528 const uint8_t *bytes
= CFDataGetBytePtr(data
);
529 size_t size
= (size_t)CFDataGetLength(data
);
531 (void)SecOTRGetDefaultsWriteSeconds();
533 session
->_queue
= dispatch_queue_create("OTRSession", DISPATCH_QUEUE_SERIAL
);
536 session
->_them
= NULL
;
537 session
->_myKey
= NULL
;
538 session
->_myNextKey
= NULL
;
539 session
->_theirKey
= NULL
;
540 session
->_theirPreviousKey
= NULL
;
541 session
->_receivedDHMessage
= NULL
;
542 session
->_receivedDHKeyMessage
= NULL
;
543 session
->_textOutput
= false;
544 session
->_compactAppleMessages
= false;
545 session
->_timeToRoll
= 0;
546 session
->_stallingTheirRoll
= false;
547 session
->_stallSeconds
= 0;
548 session
->_missedAck
= true;
549 session
->_receivedAck
= false;
551 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
554 require_noerr(ReadByte(&bytes
, &size
, &version
), fail
);
555 require(version
<= 6, fail
);
557 require_noerr(ReadLong(&bytes
, &size
, &session
->_state
), fail
);
558 session
->_me
= SecOTRFullIdentityCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
, NULL
);
559 require(session
->_me
!= NULL
, fail
);
560 session
->_them
= SecOTRPublicIdentityCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
, NULL
);
561 require(session
->_them
!= NULL
, fail
);
563 require(size
> sizeof(session
->_r
), fail
);
564 memcpy(session
->_r
, bytes
, sizeof(session
->_r
));
565 bytes
+= sizeof(session
->_r
);
566 size
-= sizeof(session
->_r
);
569 uint8_t hasMessage
= false;
570 ReadByte(&bytes
, &size
, &hasMessage
);
572 session
->_receivedDHMessage
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
577 uint8_t hasMessage
= false;
578 ReadByte(&bytes
, &size
, &hasMessage
);
580 session
->_receivedDHKeyMessage
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
586 require_noerr(ReadByte(&bytes
, &size
, &ready
), fail
);
587 if (ready
&& session
->_state
== kIdle
)
588 session
->_state
= kDone
;
592 require_noerr(ReadLong(&bytes
, &size
, &session
->_keyID
), fail
);
593 if (session
->_keyID
> 0) {
594 session
->_myKey
= SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
);
595 require(session
->_myKey
!= NULL
, fail
);
596 session
->_myNextKey
= SecOTRFullDHKCreateFromBytes(kCFAllocatorDefault
, &bytes
, &size
);
597 require(session
->_myNextKey
!= NULL
, fail
);
601 require_noerr(ReadByte(&bytes
, &size
, &numberOfKeys
), fail
);
603 require_noerr(ReadLong(&bytes
, &size
, &session
->_theirKeyID
), fail
);
605 if (session
->_theirKeyID
> 0) {
606 if (session
->_theirKeyID
> 1) {
607 session
->_theirPreviousKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
608 require(session
->_theirPreviousKey
!= NULL
, fail
);
610 session
->_theirKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
611 require(session
->_theirKey
!= NULL
, fail
);
615 if(numberOfKeys
>= 1){
616 if (numberOfKeys
>= 2) {
617 session
->_theirPreviousKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
618 require(session
->_theirPreviousKey
!= NULL
, fail
);
620 session
->_theirKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, &bytes
, &size
);
621 require(session
->_theirKey
!= NULL
, fail
);
627 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
628 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
629 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
630 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
631 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
632 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
633 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
634 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
635 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
636 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
637 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
638 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
639 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
640 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
641 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
642 require_noerr(ReadLongLong(&bytes
, &size
, counter
), fail
);
644 session
->_macKeysToExpose
= CFDataCreateMutableFromOTRDATA(kCFAllocatorDefault
, &bytes
, &size
);
645 require(session
->_macKeysToExpose
!= NULL
, fail
);
647 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_textOutput
), fail
);
650 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_compactAppleMessages
), fail
);
653 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_includeHashes
), fail
);
656 require_noerr(ReadLongLong(&bytes
, &size
, &session
->_stallSeconds
), fail
);
657 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_stallingTheirRoll
), fail
);
658 require_noerr(ReadLongLong(&bytes
, &size
, &timeToRoll
), fail
);
659 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_missedAck
), fail
);
660 require_noerr(ReadByteAsBool(&bytes
, &size
, &session
->_receivedAck
), fail
);
661 session
->_timeToRoll
= timeToRoll
;
667 CFReleaseNull(session
);
672 OSStatus
SecOTRSAppendSerialization(SecOTRSessionRef session
, CFMutableDataRef serializeInto
)
674 __block OSStatus result
= errSecParam
;
676 require(session
, abort
);
677 require(serializeInto
, abort
);
679 CFIndex start
= CFDataGetLength(serializeInto
);
681 dispatch_sync(session
->_queue
, ^{
682 const uint8_t version
= 6;
683 uint8_t numberOfKeys
= 0;
684 CFDataAppendBytes(serializeInto
, &version
, sizeof(version
));
686 AppendLong(serializeInto
, session
->_state
);
688 result
= (SecOTRFIAppendSerialization(session
->_me
, serializeInto
, NULL
)) ? errSecSuccess
: errSecParam
;
690 if (result
== errSecSuccess
) {
691 result
= (SecOTRPIAppendSerialization(session
->_them
, serializeInto
, NULL
)) ? errSecSuccess
: errSecParam
;
694 if (result
== errSecSuccess
) {
695 CFDataAppendBytes(serializeInto
, session
->_r
, sizeof(session
->_r
));
697 if (session
->_receivedDHMessage
== NULL
) {
698 AppendByte(serializeInto
, 0);
700 AppendByte(serializeInto
, 1);
701 AppendCFDataAsDATA(serializeInto
, session
->_receivedDHMessage
);
704 if (session
->_receivedDHKeyMessage
== NULL
) {
705 AppendByte(serializeInto
, 0);
707 AppendByte(serializeInto
, 1);
708 AppendCFDataAsDATA(serializeInto
, session
->_receivedDHKeyMessage
);
711 AppendLong(serializeInto
, session
->_keyID
);
712 if (session
->_keyID
> 0) {
713 SecFDHKAppendSerialization(session
->_myKey
, serializeInto
);
714 SecFDHKAppendSerialization(session
->_myNextKey
, serializeInto
);
717 if(session
->_theirPreviousKey
!= NULL
)
719 if(session
->_theirKey
!= NULL
)
722 AppendByte(serializeInto
, numberOfKeys
);
724 AppendLong(serializeInto
, session
->_theirKeyID
);
726 if (session
->_theirPreviousKey
!= NULL
)
727 SecPDHKAppendSerialization(session
->_theirPreviousKey
, serializeInto
);
729 if (session
->_theirKey
!= NULL
)
730 SecPDHKAppendSerialization(session
->_theirKey
, serializeInto
);
734 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
735 AppendLongLong(serializeInto
, *counter
);
736 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
737 AppendLongLong(serializeInto
, *counter
);
738 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
739 AppendLongLong(serializeInto
, *counter
);
740 SecOTRSFindKeysForMessage(session
, session
->_myKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
741 AppendLongLong(serializeInto
, *counter
);
742 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, false, NULL
, NULL
, &counter
);
743 AppendLongLong(serializeInto
, *counter
);
744 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirKey
, true, NULL
, NULL
, &counter
);
745 AppendLongLong(serializeInto
, *counter
);
746 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, false, NULL
, NULL
, &counter
);
747 AppendLongLong(serializeInto
, *counter
);
748 SecOTRSFindKeysForMessage(session
, session
->_myNextKey
, session
->_theirPreviousKey
, true, NULL
, NULL
, &counter
);
749 AppendLongLong(serializeInto
, *counter
);
751 AppendCFDataAsDATA(serializeInto
, session
->_macKeysToExpose
);
753 AppendByte(serializeInto
, session
->_textOutput
? 1 : 0);
754 AppendByte(serializeInto
, session
->_compactAppleMessages
? 1 : 0);
755 AppendByte(serializeInto
, session
->_includeHashes
? 1 : 0);
757 AppendLongLong(serializeInto
, session
->_stallSeconds
? session
->_stallSeconds
: constant_zero
);
759 AppendByte(serializeInto
, session
->_stallingTheirRoll
? 1 : 0);
760 AppendLongLong(serializeInto
, (uint64_t)session
->_timeToRoll
);
761 AppendByte(serializeInto
, session
->_missedAck
? 1 : 0);
762 AppendByte(serializeInto
, session
->_receivedAck
? 1 : 0);
767 if (result
!= errSecSuccess
)
768 CFDataSetLength(serializeInto
, start
);
775 bool SecOTRSIsForKeys(SecOTRSessionRef session
, SecKeyRef myPublic
, SecKeyRef theirPublic
)
777 __block
bool isForKeys
= false;
779 dispatch_sync(session
->_queue
, ^{
780 isForKeys
= SecOTRFICompareToPublicKey(session
->_me
, myPublic
) &&
781 SecOTRPICompareToPublicKey(session
->_them
, theirPublic
);
787 bool SecOTRSGetIsReadyForMessages(SecOTRSessionRef session
)
791 dispatch_sync(session
->_queue
, ^{ result
= session
->_state
== kDone
; });
796 bool SecOTRSGetIsIdle(SecOTRSessionRef session
)
800 dispatch_sync(session
->_queue
, ^{ result
= session
->_state
== kIdle
; });
805 static void SecOTRSPrecalculateForPair(SecOTRSessionRef session
,
806 SecOTRFullDHKeyRef myKey
,
807 SecOTRPublicDHKeyRef theirKey
)
809 if (myKey
== NULL
|| theirKey
== NULL
)
812 SecOTRSFindKeysForMessage(session
, myKey
, theirKey
, true, NULL
, NULL
, NULL
);
813 SecOTRSFindKeysForMessage(session
, myKey
, theirKey
, false, NULL
, NULL
, NULL
);
816 static void SecOTRSPrecalculateKeysInternal(SecOTRSessionRef session
)
818 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirKey
);
819 SecOTRSPrecalculateForPair(session
, session
->_myNextKey
, session
->_theirKey
);
820 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirPreviousKey
);
821 SecOTRSPrecalculateForPair(session
, session
->_myNextKey
, session
->_theirPreviousKey
);
824 static void SecOTRSPrecalculateNextKeysInternal(SecOTRSessionRef session
)
826 SecOTRSPrecalculateForPair(session
, session
->_myKey
, session
->_theirKey
);
829 void SecOTRSPrecalculateKeys(SecOTRSessionRef session
)
831 dispatch_sync_f(session
->_queue
, session
, (dispatch_function_t
) SecOTRSPrecalculateKeysInternal
);
834 enum SecOTRSMessageKind
SecOTRSGetMessageKind(SecOTRSessionRef session
, CFDataRef message
)
836 OTRMessageType type
= SecOTRSGetMessageType(message
);
838 enum SecOTRSMessageKind kind
;
842 case kEvenCompactDataMessage
:
843 case kOddCompactDataMessage
:
844 case kEvenCompactDataMessageWithHashes
:
845 case kOddCompactDataMessageWithHashes
:
846 kind
= kOTRDataPacket
;
850 case kRevealSignatureMessage
:
851 case kSignatureMessage
:
852 kind
= kOTRNegotiationPacket
;
854 case kInvalidMessage
:
856 kind
= kOTRUnknownPacket
;
863 static OSStatus
SecOTRSSignAndProtectRaw_locked(SecOTRSessionRef session
,
864 CFDataRef sourceMessage
, CFMutableDataRef destinationMessage
,
865 uint8_t* messageKey
, uint8_t* macKey
, uint64_t* counter
, uint32_t theirKeyID
, SecOTRPublicDHKeyRef theirKey
)
867 CFIndex start
= CFDataGetLength(destinationMessage
);
869 AppendHeader(destinationMessage
, kDataMessage
);
870 AppendByte(destinationMessage
, 0); // Flags, all zero
872 AppendLong(destinationMessage
, session
->_keyID
);
873 AppendLong(destinationMessage
, theirKeyID
);
874 SecFDHKAppendPublicSerialization(session
->_myNextKey
, destinationMessage
);
875 AppendLongLong(destinationMessage
, ++*counter
);
877 CFIndex sourceSize
= CFDataGetLength(sourceMessage
);
878 assert(((unsigned long)sourceSize
)<=UINT32_MAX
); /* this is correct as long as CFIndex is a signed long */
879 AppendLong(destinationMessage
, (uint32_t)sourceSize
);
880 uint8_t* encryptedDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, sourceSize
);
881 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
883 (size_t)sourceSize
, CFDataGetBytePtr(sourceMessage
),
884 encryptedDataPointer
);
886 CFIndex macedContentsSize
= CFDataGetLength(destinationMessage
) - start
;
887 CFIndex macSize
= CCSHA1_OUTPUT_SIZE
;
888 uint8_t* macDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, macSize
);
891 kOTRMessageMacKeyBytes
, macKey
,
892 macedContentsSize
, CFDataGetBytePtr(destinationMessage
) + start
,
895 CFDataAppend(destinationMessage
, session
->_macKeysToExpose
);
897 return errSecSuccess
;
900 const size_t kCompactMessageMACSize
= 16;
902 static OSStatus
SecOTRSSignAndProtectCompact_locked(SecOTRSessionRef session
,
903 CFDataRef sourceMessage
, CFMutableDataRef destinationMessage
,
904 uint8_t* messageKey
, uint8_t* macKey
, uint64_t* counter
, uint32_t theirKeyID
, SecOTRPublicDHKeyRef theirKey
)
906 CFIndex start
= CFDataGetLength(destinationMessage
);
907 bool sendHashes
= session
->_includeHashes
;
909 const uint8_t messageType
= sendHashes
? ((theirKeyID
& 0x1) ? kOddCompactDataMessageWithHashes
: kEvenCompactDataMessageWithHashes
)
910 : ((theirKeyID
& 0x1) ? kOddCompactDataMessage
: kEvenCompactDataMessage
);
912 AppendByte(destinationMessage
, messageType
);
914 SecFDHKAppendCompactPublicSerialization(session
->_myNextKey
, destinationMessage
);
915 AppendLongLongCompact(destinationMessage
, ++*counter
);
917 CFIndex sourceSize
= CFDataGetLength(sourceMessage
);
918 assert(((unsigned long)sourceSize
)<=UINT32_MAX
); /* this is correct as long as CFIndex is a signed long */
919 uint8_t* encryptedDataPointer
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, sourceSize
);
920 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
922 (size_t)sourceSize
, CFDataGetBytePtr(sourceMessage
),
923 encryptedDataPointer
);
926 uint8_t *senderHashPtr
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, kSecDHKHashSize
);
928 memcpy(senderHashPtr
, SecFDHKGetHash(session
->_myKey
), kSecDHKHashSize
);
930 uint8_t *receiverHashPtr
= CFDataIncreaseLengthAndGetMutableBytes(destinationMessage
, kSecDHKHashSize
);
932 memcpy(receiverHashPtr
, SecPDHKGetHash(theirKey
), kSecDHKHashSize
);
936 CFIndex macedContentsSize
= CFDataGetLength(destinationMessage
) - start
;
937 CFIndex macSize
= CCSHA1_OUTPUT_SIZE
;
938 uint8_t mac
[macSize
];
940 kOTRMessageMacKeyBytes
, macKey
,
941 macedContentsSize
, CFDataGetBytePtr(destinationMessage
) + start
,
944 CFDataAppendBytes(destinationMessage
, mac
, kCompactMessageMACSize
);
946 return errSecSuccess
;
949 OSStatus
SecOTRSSignAndProtectMessage(SecOTRSessionRef session
,
950 CFDataRef sourceMessage
,
951 CFMutableDataRef protectedMessage
)
953 __block OSStatus result
= errSecParam
;
955 require(session
, abort
);
956 require(sourceMessage
, abort
);
957 require(protectedMessage
, abort
);
959 if(session
->_state
!= kDone
){
960 secdebug("OTR", "Cannot sign and protect messages, we are not done negotiating sesion[%p]", session
);
961 require_quiet( session
->_state
== kDone
, abort
);
964 dispatch_sync(session
->_queue
, ^{
965 if (session
->_myKey
== NULL
||
966 session
->_theirKey
== NULL
) {
973 uint32_t theirKeyID
= session
->_theirKeyID
;
975 SecOTRPublicDHKeyRef theirKeyToUse
= session
->_theirKey
;
977 SecOTRSRollIfTime(session
);
979 if(session
->_stallingTheirRoll
&& session
->_theirPreviousKey
){
980 theirKeyToUse
= session
->_theirPreviousKey
;
981 theirKeyID
= session
->_theirKeyID
- 1;
984 SecOTRSFindKeysForMessage(session
, session
->_myKey
, theirKeyToUse
,
986 &messageKey
, &macKey
, &counter
);
987 // The || !protectedMessage below is only here to shut the static analyzer up, the require(protectedMessage, abort) outside the block already ensures this new term is never true.
988 CFMutableDataRef destinationMessage
= session
->_textOutput
|| !protectedMessage
? CFDataCreateMutable(kCFAllocatorDefault
, 0) : CFRetainSafe(protectedMessage
);
990 result
= session
->_compactAppleMessages
? SecOTRSSignAndProtectCompact_locked(session
, sourceMessage
, destinationMessage
, messageKey
, macKey
, counter
, theirKeyID
, theirKeyToUse
)
991 : SecOTRSSignAndProtectRaw_locked(session
, sourceMessage
, destinationMessage
, messageKey
, macKey
, counter
, theirKeyID
, theirKeyToUse
);
993 if (result
== errSecSuccess
) {
994 if (session
->_textOutput
) {
995 SecOTRPrepareOutgoingBytes(destinationMessage
, protectedMessage
);
998 CFDataSetLength(session
->_macKeysToExpose
, 0);
1001 CFReleaseSafe(destinationMessage
);
1003 result
= errSecSuccess
;
1010 void SecOTRSKickTimeToRoll(SecOTRSessionRef session
){
1011 session
->_timeToRoll
= CFAbsoluteTimeGetCurrent();
1014 static void SecOTRAcceptNewRemoteKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef newKey
)
1016 if (session
->_theirPreviousKey
) {
1017 SecOTRSExpireCachedKeysForPublicKey(session
, session
->_theirPreviousKey
);
1020 CFReleaseNull(session
->_theirPreviousKey
);
1021 session
->_theirPreviousKey
= session
->_theirKey
;
1022 session
->_theirKey
= CFRetainSafe(newKey
);
1023 session
->_stallingTheirRoll
= true;
1025 session
->_theirKeyID
+= 1;
1027 SecOTRSEnableTimeToRoll(session
);
1030 OSStatus
SecOTRSetupInitialRemoteKey(SecOTRSessionRef session
, SecOTRPublicDHKeyRef initialKey
) {
1032 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
1034 CFReleaseNull(session
->_theirPreviousKey
);
1035 CFAssignRetained(session
->_theirKey
, initialKey
);
1036 session
->_theirKeyID
= 1;
1038 return errSecSuccess
;
1042 /// MARK: SLOW ROLLING
1045 void SOSOTRSRoll(SecOTRSessionRef session
){
1047 session
->_stallingTheirRoll
= false;
1049 //receiving side roll
1050 if(session
->_receivedAck
){
1051 SecOTRGenerateNewProposedKey(session
);
1052 session
->_missedAck
= false;
1053 session
->_receivedAck
= false;
1056 session
->_missedAck
= true;
1060 static OSStatus
SecOTRVerifyAndExposeRaw_locked(SecOTRSessionRef session
,
1061 CFDataRef decodedBytes
,
1062 CFMutableDataRef exposedMessageContents
)
1064 OSStatus result
= errSecDecode
;
1066 SecOTRPublicDHKeyRef newKey
= NULL
;
1067 const uint8_t* bytes
;
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 SecOTRFullDHKeyRef myKeyForMessage
= (myID
== session
->_keyID
) ? session
->_myKey
: session
->_myNextKey
;
1103 SecOTRPublicDHKeyRef 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(constant_memcmp(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 CFReleaseNull(newKey
);
1163 static OSStatus
SecOTRVerifyAndExposeRawCompact_locked(SecOTRSessionRef session
,
1164 CFDataRef decodedBytes
,
1165 CFMutableDataRef exposedMessageContents
)
1167 SecOTRPublicDHKeyRef theirProposal
= NULL
;
1168 OSStatus result
= errSecDecode
;
1169 const uint8_t* bytes
;
1171 SecOTRPublicDHKeyRef theirKeyForMessage
= NULL
;
1172 bytes
= CFDataGetBytePtr(decodedBytes
);
1173 size
= CFDataGetLength(decodedBytes
);
1174 SecOTRFullDHKeyRef myKeyForMessage
= NULL
;
1175 const uint8_t* macDataStart
= bytes
;
1176 bool useEvenKey
= false;
1177 bool useCurrentKey
= false;
1178 bool sentHashes
= false;
1179 uint64_t counter
= 0;
1181 uint8_t type_byte
= 0;
1182 require_noerr_quiet(result
= ReadByte(&bytes
, &size
, &type_byte
), fail
);
1183 require_action_quiet(type_byte
== kOddCompactDataMessage
|| type_byte
== kEvenCompactDataMessage
1184 || type_byte
== kOddCompactDataMessageWithHashes
|| type_byte
== kEvenCompactDataMessageWithHashes
, fail
, result
= errSecDecode
);
1186 useEvenKey
= (type_byte
== kEvenCompactDataMessage
|| type_byte
== kEvenCompactDataMessageWithHashes
);
1187 sentHashes
= (type_byte
== kOddCompactDataMessageWithHashes
|| type_byte
== kEvenCompactDataMessageWithHashes
);
1189 useCurrentKey
= useEvenKey
^ (session
->_keyID
& 1);
1190 myKeyForMessage
= useCurrentKey
? session
->_myKey
: session
->_myNextKey
;
1192 require_action_quiet(myKeyForMessage
, fail
, result
= errSecDecode
);
1194 theirProposal
= SecOTRPublicDHKCreateFromCompactSerialization(kCFAllocatorDefault
, &bytes
, &size
);
1196 require_action_quiet(theirProposal
, fail
, result
= errSecDecode
);
1198 bool proposalIsNew
= !CFEqualSafe(theirProposal
, session
->_theirKey
);
1199 theirKeyForMessage
= proposalIsNew
? session
->_theirKey
: session
->_theirPreviousKey
;
1201 require_action_quiet(theirKeyForMessage
, fail
, result
= errSecDecode
);
1203 uint8_t *messageKey
;
1205 uint64_t *theirCounter
;
1208 SecOTRSFindKeysForMessage(session
, myKeyForMessage
, theirKeyForMessage
, false, &messageKey
, &macKey
, &theirCounter
);
1210 require_noerr_quiet(result
= ReadLongLongCompact(&bytes
, &size
, &counter
), fail
);
1211 require_action_quiet(counter
> *theirCounter
, fail
, result
= errSecOTRTooOld
);
1213 size_t messageSize
= size
- kCompactMessageMACSize
- (sentHashes
? 2 * kSecDHKHashSize
: 0); // It's all message except for the MAC and maybe hashes
1214 const uint8_t* messageStart
= bytes
;
1216 bytes
+= messageSize
;
1217 size
-= messageSize
;
1220 // Sender then receiver keys
1222 if (memcmp(SecPDHKGetHash(theirKeyForMessage
), bytes
, kSecDHKHashSize
) != 0) {
1223 // Wrong sender key WTF.
1225 BufferPerformWithHexString(bytes
, kSecDHKHashSize
, ^(CFStringRef dataString
) {
1226 secdebug("OTR","session[%p] Sender key hash doesn't match: %@ != %@", session
, theirKeyForMessage
, dataString
);
1231 bytes
+= kSecDHKHashSize
;
1232 size
-= kSecDHKHashSize
;
1234 if (memcmp(SecFDHKGetHash(myKeyForMessage
), bytes
, kSecDHKHashSize
) != 0) {
1235 // Wrong sender key WTF.
1237 BufferPerformWithHexString(bytes
, kSecDHKHashSize
, ^(CFStringRef dataString
) {
1238 secdebug("OTR","session[%p] Receiver key hash doesn't match: %@ != %@", session
, myKeyForMessage
, dataString
);
1243 bytes
+= kSecDHKHashSize
;
1244 size
-= kSecDHKHashSize
;
1248 uint8_t mac
[CCSHA1_OUTPUT_SIZE
];
1249 require_action_quiet(kCompactMessageMACSize
== size
, fail
, result
= errSecDecode
); // require space for the mac and some bytes
1251 size_t macDataSize
= (size_t)(bytes
- macDataStart
);
1254 kOTRMessageMacKeyBytes
, macKey
,
1255 macDataSize
, macDataStart
,
1258 require_noerr_action_quiet(constant_memcmp(mac
, bytes
, kCompactMessageMACSize
), fail
, result
= errSecAuthFailed
);
1260 uint8_t* dataSpace
= CFDataIncreaseLengthAndGetMutableBytes(exposedMessageContents
, (CFIndex
)messageSize
);
1262 AES_CTR_HighHalf_Transform(kOTRMessageKeyBytes
, messageKey
,
1264 messageSize
, messageStart
,
1267 // Everything is good, accept the meta data.
1268 *theirCounter
= counter
;
1270 if (proposalIsNew
) {
1271 SecOTRAcceptNewRemoteKey(session
, theirProposal
);
1274 if (!useCurrentKey
) {
1275 SecOTRSHandleProposalAcknowledge(session
);
1277 SecOTRSRollIfTime(session
);
1279 SecOTRSPrecalculateNextKeysInternal(session
);
1283 if(result
!= errSecSuccess
){
1284 CFDataPerformWithHexString(decodedBytes
, ^(CFStringRef decodedBytesString
) {
1285 secdebug("OTR","session[%p] failed to decrypt, session: %@, mk: %@, mpk: %@, tpk: %@, tk: %@, chose tktu: %@, mktu: %@, m: %@, tP: %@, tb: %hhx", session
, session
,
1286 session
->_myKey
, session
->_myNextKey
, session
->_theirPreviousKey
, session
->_theirKey
, theirKeyForMessage
, myKeyForMessage
, decodedBytesString
, theirProposal
, type_byte
);
1290 CFReleaseNull(theirProposal
);
1295 OSStatus
SecOTRSVerifyAndExposeMessage(SecOTRSessionRef session
,
1296 CFDataRef incomingMessage
,
1297 CFMutableDataRef exposedMessageContents
)
1299 __block OSStatus result
= errSecParam
;
1302 require(session
, abort
);
1303 require(incomingMessage
, abort
);
1304 require(exposedMessageContents
, abort
);
1306 if(session
->_state
== kDone
){
1307 dispatch_sync(session
->_queue
, ^{
1308 CFDataRef decodedBytes
= SecOTRCopyIncomingBytes(incomingMessage
);
1310 OTRMessageType messageType
= SecOTRSGetMessageType(decodedBytes
);
1312 switch (messageType
) {
1314 result
= SecOTRVerifyAndExposeRaw_locked(session
, decodedBytes
, exposedMessageContents
);
1317 case kOddCompactDataMessage
:
1318 case kEvenCompactDataMessage
:
1319 case kOddCompactDataMessageWithHashes
:
1320 case kEvenCompactDataMessageWithHashes
:
1321 result
= SecOTRVerifyAndExposeRawCompact_locked(session
, decodedBytes
, exposedMessageContents
);
1325 result
= errSecUnsupportedFormat
;
1329 CFReleaseSafe(decodedBytes
);
1333 secnotice("OTR", "session[%p]Cannot process message:%@, session is not done negotiating, session state: %@", session
, incomingMessage
, session
);
1334 result
= errSecOTRNotReady
;
1341 OSStatus
SecOTRSEndSession(SecOTRSessionRef session
,
1342 CFMutableDataRef messageToSend
)
1344 return errSecUnimplemented
;
1347 static CFDataRef
data_to_data_error_request(enum SecXPCOperation op
, CFDataRef publicPeerId
, CFErrorRef
*error
) {
1348 __block CFDataRef result
= NULL
;
1349 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1350 return SecXPCDictionarySetDataOptional(message
, kSecXPCPublicPeerId
, publicPeerId
, error
);
1351 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1352 return (result
= SecXPCDictionaryCopyData(response
, kSecXPCKeyResult
, error
));
1357 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
) {
1358 __block CFDataRef tempOutputSessionData
= NULL
;
1359 __block CFDataRef tempOutputPacket
= NULL
;
1360 __block
bool tempReadyForMessages
= false;
1362 bool result
= securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
1363 return SecXPCDictionarySetDataOptional(message
, kSecXPCOTRSession
, sessionData
, error
)
1364 && SecXPCDictionarySetDataOptional(message
, kSecXPCData
, inputPacket
, error
);
1365 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1366 if (xpc_dictionary_get_bool(response
, kSecXPCKeyResult
)) {
1367 tempOutputSessionData
= SecXPCDictionaryCopyData(response
, kSecXPCOTRSession
, error
);
1368 tempOutputPacket
= SecXPCDictionaryCopyData(response
, kSecXPCData
, error
);
1369 tempReadyForMessages
= xpc_dictionary_get_bool(response
, kSecXPCOTRReady
);
1377 *outputSessionData
= tempOutputSessionData
;
1378 *outputPacket
= tempOutputPacket
;
1379 *readyForMessages
= tempReadyForMessages
;
1385 CFDataRef
SecOTRSessionCreateRemote(CFDataRef publicPeerId
, CFErrorRef
*error
) {
1386 __block CFDataRef result
;
1387 os_activity_initiate("SecOTRSessionCreateRemote", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1388 (void)SecOTRGetDefaultsWriteSeconds();
1389 result
= SECURITYD_XPC(sec_otr_session_create_remote
, data_to_data_error_request
, publicPeerId
, error
);
1394 bool SecOTRSessionProcessPacketRemote(CFDataRef sessionData
, CFDataRef inputPacket
, CFDataRef
* outputSessionData
, CFDataRef
* outputPacket
, bool *readyForMessages
, CFErrorRef
*error
) {
1395 __block
bool result
;
1396 os_activity_initiate("SecOTRSessionProcessPacketRemote", OS_ACTIVITY_FLAG_DEFAULT
, ^{
1397 result
= SECURITYD_XPC(sec_otr_session_process_packet_remote
, data_data_to_data_data_bool_error_request
, sessionData
, inputPacket
, outputSessionData
, outputPacket
, readyForMessages
, error
);