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@
25 #include "SecOTRSession.h"
27 #include "SecOTRMath.h"
28 #include "SecOTRIdentityPriv.h"
29 #include "SecOTRSessionPriv.h"
30 #include "SecOTRPackets.h"
31 #include "SecOTRPacketData.h"
32 #include "SecOTRDHKey.h"
34 #include <utilities/SecCFWrappers.h>
36 #include <CoreFoundation/CFRuntime.h>
37 #include <CoreFoundation/CFString.h>
39 #include <Security/SecBase.h>
40 #include <Security/SecRandom.h>
42 #include <AssertMacros.h>
44 #include <corecrypto/cchmac.h>
45 #include <corecrypto/ccsha2.h>
49 static void SecOTRInitMyDHKeys(SecOTRSessionRef session
)
51 CFReleaseNull(session
->_myKey
);
52 session
->_myKey
= SecOTRFullDHKCreate(kCFAllocatorDefault
);
53 CFReleaseNull(session
->_myNextKey
);
54 session
->_myNextKey
= SecOTRFullDHKCreate(kCFAllocatorDefault
);
56 session
->_missedAck
= true;
57 session
->_receivedAck
= false;
58 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
61 OSStatus
SecOTRSAppendStartPacket(SecOTRSessionRef session
, CFMutableDataRef appendPacket
)
63 __block OSStatus result
= errSecSuccess
;
65 dispatch_sync(session
->_queue
, ^{
66 session
->_state
= kAwaitingDHKey
;
68 // Generate r and x and calculate gx:
69 SecOTRInitMyDHKeys(session
);
71 CFMutableDataRef destinationMessage
= NULL
;
72 if (session
->_textOutput
) {
73 destinationMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
75 destinationMessage
= CFRetainSafe(appendPacket
);
79 result
= SecRandomCopyBytes(kSecRandomDefault
, sizeof(session
->_r
), session
->_r
);
80 if (result
== errSecSuccess
) {
81 SecOTRAppendDHMessage(session
, destinationMessage
);
82 if (session
->_textOutput
) {
83 SecOTRPrepareOutgoingBytes(destinationMessage
, appendPacket
);
86 CFReleaseSafe(destinationMessage
);
92 OSStatus
SecOTRSAppendRestartPacket(SecOTRSessionRef session
, CFMutableDataRef appendPacket
)
94 __block OSStatus result
= errSecSuccess
;
96 dispatch_sync(session
->_queue
, ^{
97 if (!session
->_myKey
) {
98 secerror("_myKey is NULL, avoiding crash");
99 result
= errSecDecode
;
102 CFMutableDataRef destinationMessage
;
103 if (session
->_textOutput
) {
104 destinationMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
106 destinationMessage
= CFRetainSafe(appendPacket
);
109 session
->_state
= kAwaitingDHKey
;
110 CFReleaseNull(session
->_receivedDHMessage
);
111 CFReleaseNull(session
->_receivedDHKeyMessage
);
113 SecOTRAppendDHMessage(session
, destinationMessage
);
114 if (session
->_textOutput
) {
115 SecOTRPrepareOutgoingBytes(destinationMessage
, appendPacket
);
117 CFReleaseSafe(destinationMessage
);
123 static const uint8_t* FindGXHash(CFDataRef dhPacket
)
125 const uint8_t* messageBytes
= CFDataGetBytePtr(dhPacket
);
126 size_t remainingBytes
= (size_t)CFDataGetLength(dhPacket
);
128 OTRMessageType messageType
;
130 require_noerr(ReadHeader(&messageBytes
, &remainingBytes
, &messageType
), fail
);
131 require(messageType
== kDHMessage
, fail
);
133 uint32_t egxiLength
= 0;
134 require_noerr(ReadLong(&messageBytes
, &remainingBytes
, & egxiLength
), fail
);
135 require(egxiLength
<= remainingBytes
, fail
);
136 messageBytes
+= egxiLength
;
137 remainingBytes
-= egxiLength
;
139 uint32_t dataLength
= 0;
140 require_noerr(ReadLong(&messageBytes
, &remainingBytes
, &dataLength
), fail
);
141 require(dataLength
<= remainingBytes
, fail
);
142 require(dataLength
== CCSHA256_OUTPUT_SIZE
, fail
);
150 static bool SecOTRMyGXHashIsBigger(SecOTRSessionRef session
, CFDataRef dhCommitMessage
)
152 bool mineIsBigger
= false;
154 CFMutableDataRef myDHCommitMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
156 SecOTRAppendDHMessage(session
, myDHCommitMessage
);
158 const uint8_t* myHash
= FindGXHash(myDHCommitMessage
);
159 const uint8_t* theirHash
= FindGXHash(dhCommitMessage
);
161 require(myHash
, fail
);
162 require(theirHash
, fail
);
164 mineIsBigger
= 0 < memcmp(myHash
, theirHash
, CCSHA256_OUTPUT_SIZE
);
167 CFReleaseNull(myDHCommitMessage
);
171 static OSStatus
SecOTRSProcessDHMessage(SecOTRSessionRef session
,
172 CFDataRef incomingPacket
,
173 CFMutableDataRef negotiationResponse
)
175 OSStatus result
= errSecParam
;
177 switch (session
->_state
) {
179 // Compare hash values.
180 if (SecOTRMyGXHashIsBigger(session
, incomingPacket
)) {
181 // If we're bigger we resend to force them to deal.
182 CFReleaseNull(session
->_receivedDHMessage
);
183 SecOTRAppendDHMessage(session
, negotiationResponse
);
184 result
= errSecSuccess
;
186 } // Else intentionally fall through to idle
187 case kAwaitingSignature
:
190 // Generate a new X and GX..
191 SecOTRInitMyDHKeys(session
);
192 // If we were already waiting on reveal, then just send the packet again
193 case kAwaitingRevealSignature
:
194 SecOTRAppendDHKeyMessage(session
, negotiationResponse
);
196 // Keep the packet for use later.
197 CFReleaseNull(session
->_receivedDHMessage
);
198 session
->_receivedDHMessage
= CFDataCreateCopy(kCFAllocatorDefault
, incomingPacket
);
200 session
->_state
= kAwaitingRevealSignature
;
201 result
= errSecSuccess
;
204 result
= errSecInteractionNotAllowed
;
211 static OSStatus
SecOTRSetupTheirKeyFrom(SecOTRSessionRef session
, const uint8_t**data
, size_t*size
)
213 SecOTRPublicDHKeyRef tempKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, data
, size
);
214 require(tempKey
!= NULL
, fail
);
216 return SecOTRSetupInitialRemoteKey(session
, tempKey
);
222 static OSStatus
SecOTRSExtractTheirPublicDHKey(SecOTRSessionRef session
, CFDataRef dhPacket
)
224 OSStatus result
= errSecParam
;
226 const uint8_t *messageBytes
= CFDataGetBytePtr(dhPacket
);
227 size_t messageSize
= (size_t)CFDataGetLength(dhPacket
);
228 OTRMessageType messageType
= kDHMessage
; // Suppress warning.
230 ReadHeader(&messageBytes
, &messageSize
, &messageType
);
231 require(messageType
== kDHKeyMessage
, exit
);
233 result
= SecOTRSetupTheirKeyFrom(session
, &messageBytes
, &messageSize
);
240 static OSStatus
SecOTRSProcessDHKeyMessage(SecOTRSessionRef session
,
241 CFDataRef incomingPacket
,
242 CFMutableDataRef negotiationResponse
)
244 OSStatus result
= errSecUnimplemented
;
246 result
= SecOTRSExtractTheirPublicDHKey(session
, incomingPacket
);
247 require_noerr(result
, exit
);
249 switch (session
->_state
) {
251 CFReleaseNull(session
->_receivedDHKeyMessage
);
252 SecOTRAppendRevealSignatureMessage(session
, negotiationResponse
);
253 session
->_state
= kAwaitingSignature
;
254 session
->_receivedDHKeyMessage
= CFDataCreateCopy(kCFAllocatorDefault
, incomingPacket
);
255 result
= errSecSuccess
;
257 case kAwaitingSignature
:
258 if (CFEqualSafe(incomingPacket
, session
->_receivedDHKeyMessage
))
259 SecOTRAppendRevealSignatureMessage(session
, negotiationResponse
);
260 result
= errSecSuccess
;
264 case kAwaitingRevealSignature
:
265 result
= errSecSuccess
;
268 result
= errSecInteractionNotAllowed
;
277 static OSStatus
SecOTRSExtractR(SecOTRSessionRef session
,
278 const uint8_t **messageBytes
,
281 OSStatus result
= errSecDecode
;
283 OTRMessageType messageType
= kDHMessage
; // Suppress warning
285 ReadHeader(messageBytes
, messageSize
, &messageType
);
286 require(messageType
== kRevealSignatureMessage
, exit
);
290 ReadLong(messageBytes
, messageSize
, &rSize
);
291 require(rSize
== kOTRAuthKeyBytes
, exit
);
294 memcpy(session
->_r
, *messageBytes
, kOTRAuthKeyBytes
);
296 *messageBytes
+= kOTRAuthKeyBytes
;
297 *messageSize
-= kOTRAuthKeyBytes
;
299 result
= errSecSuccess
;
304 static OSStatus
FindEncGYInDHPacket(SecOTRSessionRef session
,
305 const uint8_t **dhMessageBytesPtr
,
306 size_t *messageSizePtr
,
307 size_t* encGYBufferSize
)
309 OSStatus result
= errSecParam
;
310 require_action(*encGYBufferSize
>= kExponentiationBytes
+ 4, exit
, result
= errSecParam
);
312 OTRMessageType messageType
;
313 result
= ReadHeader(dhMessageBytesPtr
, messageSizePtr
, &messageType
);
314 require_noerr(result
, exit
);
315 require_action(messageType
== kDHMessage
, exit
, result
= errSecDecode
);
317 uint32_t readEncSize
;
318 result
= ReadLong(dhMessageBytesPtr
, messageSizePtr
, &readEncSize
);
319 require_noerr(result
, exit
);
321 *encGYBufferSize
= readEncSize
;
323 // Don't bother erasing the public gy decrypted, it's public after all.
328 static OSStatus
SecOTRSExtractRAndTheirDHKey(SecOTRSessionRef session
,
329 const uint8_t **messageBytes
,
332 OSStatus result
= errSecDecode
;
334 require(session
->_receivedDHMessage
!= NULL
, exit
);
335 result
= SecOTRSExtractR(session
, messageBytes
, messageSize
);
336 require_noerr(result
, exit
);
338 uint8_t gxiDecrypted
[kExponentiationBytes
+ 4];
339 const uint8_t *gxiDecryptedBuffer
= gxiDecrypted
;
341 const uint8_t* dhMessageBytes
= CFDataGetBytePtr(session
->_receivedDHMessage
);
342 size_t dhMessageSize
= (size_t)CFDataGetLength(session
->_receivedDHMessage
);
344 size_t encGYSize
= sizeof(gxiDecrypted
);
345 result
= FindEncGYInDHPacket(session
, &dhMessageBytes
, &dhMessageSize
, &encGYSize
);
346 require_noerr(result
, exit
);
347 require_action(encGYSize
<= kExponentiationBytes
+ 4, exit
, result
= errSecDecode
);
349 AES_CTR_IV0_Transform(sizeof(session
->_r
), session
->_r
, encGYSize
, dhMessageBytes
, gxiDecrypted
);
351 result
= SecOTRSetupTheirKeyFrom(session
, &gxiDecryptedBuffer
, &encGYSize
);
354 // Don't bother erasing the public gy decrypted, it's public after all.
358 static OSStatus
SecVerifySignatureAndMac(SecOTRSessionRef session
,
360 const uint8_t **signatureAndMacBytes
,
361 size_t *signatureAndMacSize
)
363 OSStatus result
= errSecDecode
;
365 uint8_t m1
[kOTRAuthMACKeyBytes
];
366 uint8_t m2
[kOTRAuthMACKeyBytes
];
367 uint8_t c
[kOTRAuthKeyBytes
];
370 cc_unit s
[kExponentiationUnits
];
372 SecPDHKeyGenerateS(session
->_myKey
, session
->_theirKey
, s
);
373 // Derive M1, M2 and C, either prime or normal versions.
374 DeriveOTR256BitsFromS(usePrimes
? kM1Prime
: kM1
,
375 kExponentiationUnits
, s
, sizeof(m1
), m1
);
376 DeriveOTR256BitsFromS(usePrimes
? kM2Prime
: kM2
,
377 kExponentiationUnits
, s
, sizeof(m2
), m2
);
378 DeriveOTR128BitPairFromS(kCs
,
379 kExponentiationUnits
, s
,
380 sizeof(c
),usePrimes
? NULL
: c
,
381 sizeof(c
), usePrimes
? c
: NULL
);
385 cchmac_di_decl(ccsha256_di(), mBContext
);
387 cchmac_init(ccsha256_di(), mBContext
, sizeof(m1
), m1
);
390 CFMutableDataRef toHash
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
392 SecPDHKAppendSerialization(session
->_theirKey
, toHash
);
393 SecFDHKAppendPublicSerialization(session
->_myKey
, toHash
);
395 cchmac_update(ccsha256_di(), mBContext
, (size_t)CFDataGetLength(toHash
), CFDataGetBytePtr(toHash
));
397 CFReleaseNull(toHash
);
400 const uint8_t* encSigDataBlobStart
= *signatureAndMacBytes
;
403 result
= ReadLong(signatureAndMacBytes
, signatureAndMacSize
, &xbSize
);
404 require_noerr(result
, exit
);
405 require_action(xbSize
> 4, exit
, result
= errSecDecode
);
406 require_action(xbSize
<= *signatureAndMacSize
, exit
, result
= errSecDecode
);
408 uint8_t signatureMac
[CCSHA256_OUTPUT_SIZE
];
409 cchmac(ccsha256_di(), sizeof(m2
), m2
, xbSize
+ 4, encSigDataBlobStart
, signatureMac
);
411 require(xbSize
+ kSHA256HMAC160Bytes
<= *signatureAndMacSize
, exit
);
412 const uint8_t *macStart
= *signatureAndMacBytes
+ xbSize
;
414 // check the outer hmac
415 require_action(0 == memcmp(macStart
, signatureMac
, kSHA256HMAC160Bytes
), exit
, result
= errSecDecode
);
420 // Decrypt and copy the signature block
421 AES_CTR_IV0_Transform(sizeof(c
), c
, xbSize
, *signatureAndMacBytes
, xb
);
423 const uint8_t* signaturePacket
= xb
;
424 size_t signaturePacketSize
= xbSize
;
427 result
= ReadShort(&signaturePacket
, &signaturePacketSize
, &pubKeyType
);
428 require_noerr(result
, exit
);
429 require_action(pubKeyType
== 0xF000, exit
, result
= errSecUnimplemented
);
432 result
= ReadLong(&signaturePacket
, &signaturePacketSize
, &pubKeySize
);
433 require_noerr(result
, exit
);
434 require_action(pubKeySize
<= signaturePacketSize
, exit
, result
= errSecDecode
);
435 require(((CFIndex
)pubKeySize
) >= 0, exit
);
437 // Add the signature and keyid to the hash.
438 // PUBKEY of our type is 2 bytes of type, 2 bytes of size and size bytes.
439 // Key ID is 4 bytes.
440 cchmac_update(ccsha256_di(), mBContext
, 2 + 4 + pubKeySize
+ 4, xb
);
442 uint8_t mb
[CCSHA256_OUTPUT_SIZE
];
443 cchmac_final(ccsha256_di(), mBContext
, mb
);
445 // Make reference to the deflated key
446 require_action(SecOTRPIEqualToBytes(session
->_them
, signaturePacket
, (CFIndex
)pubKeySize
), exit
, result
= errSecAuthFailed
);
448 signaturePacket
+= pubKeySize
;
449 signaturePacketSize
-= pubKeySize
;
451 result
= ReadLong(&signaturePacket
, &signaturePacketSize
, &session
->_theirKeyID
);
452 require_noerr(result
, exit
);
455 result
= ReadLong(&signaturePacket
, &signaturePacketSize
, &sigSize
);
456 require_noerr(result
, exit
);
457 require_action(sigSize
<= signaturePacketSize
, exit
, result
= errSecDecode
);
459 bool bresult
= SecOTRPIVerifySignature(session
->_them
, mb
, sizeof(mb
), signaturePacket
, sigSize
, NULL
);
460 result
= bresult
? errSecSuccess
: errSecDecode
;
461 require_noerr(result
, exit
);
466 bzero(m1
, sizeof(m1
));
467 bzero(m2
, sizeof(m2
));
473 static OSStatus
SecOTRSProcessRevealSignatureMessage(SecOTRSessionRef session
,
474 CFDataRef incomingPacket
,
475 CFMutableDataRef negotiationResponse
)
477 OSStatus result
= errSecParam
;
479 require_action_quiet(session
->_state
== kAwaitingRevealSignature
, exit
, result
= errSecSuccess
);
481 const uint8_t *messageBytes
= CFDataGetBytePtr(incomingPacket
);
482 size_t messageSize
= (size_t)CFDataGetLength(incomingPacket
);
484 result
= SecOTRSExtractRAndTheirDHKey(session
, &messageBytes
, &messageSize
);
485 require_noerr(result
, exit
);
487 result
= SecVerifySignatureAndMac(session
, false, &messageBytes
, &messageSize
);
488 require_noerr(result
, exit
);
490 SecOTRAppendSignatureMessage(session
, negotiationResponse
);
492 session
->_state
= kDone
;
493 result
= errSecSuccess
;
498 static OSStatus
SecOTRSProcessSignatureMessage(SecOTRSessionRef session
,
499 CFDataRef incomingPacket
,
500 CFMutableDataRef negotiationResponse
)
502 OSStatus result
= errSecParam
;
504 require_action_quiet(session
->_state
== kAwaitingSignature
, exit
, result
= errSecSuccess
);
506 const uint8_t *messageBytes
= CFDataGetBytePtr(incomingPacket
);
507 size_t messageSize
= (size_t)CFDataGetLength(incomingPacket
);
509 OTRMessageType messageType
;
510 result
= ReadHeader(&messageBytes
, &messageSize
, &messageType
);
511 require_noerr(result
, exit
);
512 require_action(messageType
== kSignatureMessage
, exit
, result
= errSecDecode
);
514 result
= SecVerifySignatureAndMac(session
, true, &messageBytes
, &messageSize
);
515 require_noerr(result
, exit
);
517 CFReleaseNull(session
->_receivedDHKeyMessage
);
518 session
->_state
= kDone
;
520 result
= errSecSuccess
;
525 OSStatus
SecOTRSProcessPacket(SecOTRSessionRef session
,
526 CFDataRef incomingPacket
,
527 CFMutableDataRef negotiationResponse
)
529 __block OSStatus result
= errSecParam
;
531 require(CFDataGetLength(incomingPacket
) > 0, fail
);
532 dispatch_sync(session
->_queue
, ^{
533 CFDataRef decodedBytes
= SecOTRCopyIncomingBytes(incomingPacket
);
535 const uint8_t* bytes
= CFDataGetBytePtr(decodedBytes
);
536 size_t size
= CFDataGetLength(decodedBytes
);
538 OTRMessageType packetType
= kInvalidMessage
;
539 if (ReadHeader(&bytes
, &size
, &packetType
))
540 packetType
= kInvalidMessage
;
542 CFMutableDataRef destinationMessage
;
543 if (session
->_textOutput
) {
544 destinationMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
546 destinationMessage
= CFRetainSafe(negotiationResponse
);
549 switch (packetType
) {
551 result
= SecOTRSProcessDHMessage(session
, decodedBytes
, destinationMessage
);
554 result
= SecOTRSProcessDHKeyMessage(session
, decodedBytes
, destinationMessage
);
556 case kRevealSignatureMessage
:
557 result
= SecOTRSProcessRevealSignatureMessage(session
, decodedBytes
, destinationMessage
);
559 case kSignatureMessage
:
560 result
= SecOTRSProcessSignatureMessage(session
, decodedBytes
, destinationMessage
);
563 result
= errSecDecode
;
567 if (result
!= errSecSuccess
) {
568 secnotice("session", "Error %d processing packet type %d, session state %d, keyid %d, myKey %p, myNextKey %p, theirKeyId %d, theirKey %p, theirPreviousKey %p, bytes %@", (int)result
, packetType
, session
->_state
, session
->_keyID
, session
->_myKey
, session
->_myNextKey
, session
->_theirKeyID
, session
->_theirKey
, session
->_theirPreviousKey
, decodedBytes
);
571 if (session
->_textOutput
) {
572 SecOTRPrepareOutgoingBytes(destinationMessage
, negotiationResponse
);
574 CFReleaseSafe(destinationMessage
);
575 CFReleaseSafe(decodedBytes
);