3 // libsecurity_libSecOTR
5 // Created by Mitch Adler on 2/22/11.
6 // Copyright 2011 Apple Inc. All rights reserved.
9 #include "SecOTRSession.h"
11 #include "SecOTRMath.h"
12 #include "SecOTRIdentityPriv.h"
13 #include "SecOTRSessionPriv.h"
14 #include "SecOTRPackets.h"
15 #include "SecOTRPacketData.h"
16 #include "SecOTRDHKey.h"
18 #include <utilities/SecCFWrappers.h>
20 #include <CoreFoundation/CFRuntime.h>
21 #include <CoreFoundation/CFString.h>
23 #include <Security/SecBase.h>
24 #include <Security/SecRandom.h>
26 #include <AssertMacros.h>
28 #include <corecrypto/cchmac.h>
29 #include <corecrypto/ccsha2.h>
33 static void SecOTRInitMyDHKeys(SecOTRSessionRef session
)
35 CFReleaseNull(session
->_myKey
);
36 session
->_myKey
= SecOTRFullDHKCreate(kCFAllocatorDefault
);
37 CFReleaseNull(session
->_myNextKey
);
38 session
->_myNextKey
= SecOTRFullDHKCreate(kCFAllocatorDefault
);
41 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
44 OSStatus
SecOTRSAppendStartPacket(SecOTRSessionRef session
, CFMutableDataRef appendPacket
)
46 __block OSStatus result
= errSecSuccess
;
48 dispatch_sync(session
->_queue
, ^{
49 session
->_state
= kAwaitingDHKey
;
51 // Generate r and x and calculate gx:
52 SecOTRInitMyDHKeys(session
);
54 CFMutableDataRef destinationMessage
;
55 if (session
->_textOutput
) {
56 destinationMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
58 destinationMessage
= appendPacket
;
62 result
= SecRandomCopyBytes(kSecRandomDefault
, sizeof(session
->_r
), session
->_r
);
63 if (result
== errSecSuccess
) {
64 SecOTRAppendDHMessage(session
, destinationMessage
);
65 if (session
->_textOutput
) {
66 SecOTRPrepareOutgoingBytes(destinationMessage
, appendPacket
);
67 CFReleaseSafe(destinationMessage
);
75 OSStatus
SecOTRSAppendRestartPacket(SecOTRSessionRef session
, CFMutableDataRef appendPacket
)
77 __block OSStatus result
= errSecSuccess
;
79 dispatch_sync(session
->_queue
, ^{
80 if (!session
->_myKey
) {
81 secerror("_myKey is NULL, avoiding crash");
82 result
= errSecDecode
;
85 CFMutableDataRef destinationMessage
;
86 if (session
->_textOutput
) {
87 destinationMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
89 destinationMessage
= appendPacket
;
92 session
->_state
= kAwaitingDHKey
;
93 CFReleaseNull(session
->_receivedDHMessage
);
94 CFReleaseNull(session
->_receivedDHKeyMessage
);
96 SecOTRAppendDHMessage(session
, destinationMessage
);
97 if (session
->_textOutput
) {
98 SecOTRPrepareOutgoingBytes(destinationMessage
, appendPacket
);
99 CFReleaseSafe(destinationMessage
);
106 static const uint8_t* FindGXHash(CFDataRef dhPacket
)
108 const uint8_t* messageBytes
= CFDataGetBytePtr(dhPacket
);
109 size_t remainingBytes
= (size_t)CFDataGetLength(dhPacket
);
111 OTRMessageType messageType
;
113 require_noerr(ReadHeader(&messageBytes
, &remainingBytes
, &messageType
), fail
);
114 require(messageType
== kDHMessage
, fail
);
116 uint32_t egxiLength
= 0;
117 require_noerr(ReadLong(&messageBytes
, &remainingBytes
, & egxiLength
), fail
);
118 require(egxiLength
<= remainingBytes
, fail
);
119 messageBytes
+= egxiLength
;
120 remainingBytes
-= egxiLength
;
122 uint32_t dataLength
= 0;
123 require_noerr(ReadLong(&messageBytes
, &remainingBytes
, &dataLength
), fail
);
124 require(dataLength
<= remainingBytes
, fail
);
125 require(dataLength
== CCSHA256_OUTPUT_SIZE
, fail
);
133 static bool SecOTRMyGXHashIsBigger(SecOTRSessionRef session
, CFDataRef dhCommitMessage
)
135 bool mineIsBigger
= false;
137 CFMutableDataRef myDHCommitMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
139 SecOTRAppendDHMessage(session
, myDHCommitMessage
);
141 const uint8_t* myHash
= FindGXHash(myDHCommitMessage
);
142 const uint8_t* theirHash
= FindGXHash(dhCommitMessage
);
144 require(myHash
, fail
);
145 require(theirHash
, fail
);
147 mineIsBigger
= 0 < memcmp(myHash
, theirHash
, CCSHA256_OUTPUT_SIZE
);
150 CFReleaseNull(myDHCommitMessage
);
154 static OSStatus
SecOTRSProcessDHMessage(SecOTRSessionRef session
,
155 CFDataRef incomingPacket
,
156 CFMutableDataRef negotiationResponse
)
158 OSStatus result
= errSecParam
;
160 switch (session
->_state
) {
162 // Compare hash values.
163 if (SecOTRMyGXHashIsBigger(session
, incomingPacket
)) {
164 // If we're bigger we resend to force them to deal.
165 CFReleaseNull(session
->_receivedDHMessage
);
166 SecOTRAppendDHMessage(session
, negotiationResponse
);
167 result
= errSecSuccess
;
169 } // Else intentionally fall through to idle
170 case kAwaitingSignature
:
173 // Generate a new X and GX..
174 SecOTRInitMyDHKeys(session
);
175 // If we were already waiting on reveal, then just send the packet again
176 case kAwaitingRevealSignature
:
177 SecOTRAppendDHKeyMessage(session
, negotiationResponse
);
179 // Keep the packet for use later.
180 CFReleaseNull(session
->_receivedDHMessage
);
181 session
->_receivedDHMessage
= CFDataCreateCopy(kCFAllocatorDefault
, incomingPacket
);
183 session
->_state
= kAwaitingRevealSignature
;
184 result
= errSecSuccess
;
187 result
= errSecInteractionNotAllowed
;
194 static OSStatus
SecOTRSetupTheirKeyFrom(SecOTRSessionRef session
, const uint8_t**data
, size_t*size
)
196 SecOTRPublicDHKeyRef tempKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, data
, size
);
197 require(tempKey
!= NULL
, fail
);
198 session
->_theirKey
= tempKey
;
199 session
->_theirKeyID
= 1;
201 return errSecSuccess
;
207 static OSStatus
SecOTRSExtractTheirPublicDHKey(SecOTRSessionRef session
, CFDataRef dhPacket
)
209 OSStatus result
= errSecParam
;
211 const uint8_t *messageBytes
= CFDataGetBytePtr(dhPacket
);
212 size_t messageSize
= (size_t)CFDataGetLength(dhPacket
);
213 OTRMessageType messageType
= kDHMessage
; // Suppress warning.
215 ReadHeader(&messageBytes
, &messageSize
, &messageType
);
216 require(messageType
== kDHKeyMessage
, exit
);
218 result
= SecOTRSetupTheirKeyFrom(session
, &messageBytes
, &messageSize
);
225 static OSStatus
SecOTRSProcessDHKeyMessage(SecOTRSessionRef session
,
226 CFDataRef incomingPacket
,
227 CFMutableDataRef negotiationResponse
)
229 OSStatus result
= errSecUnimplemented
;
231 result
= SecOTRSExtractTheirPublicDHKey(session
, incomingPacket
);
232 require_noerr(result
, exit
);
234 switch (session
->_state
) {
236 CFReleaseNull(session
->_receivedDHKeyMessage
);
237 SecOTRAppendRevealSignatureMessage(session
, negotiationResponse
);
238 session
->_state
= kAwaitingSignature
;
239 session
->_receivedDHKeyMessage
= CFDataCreateCopy(kCFAllocatorDefault
, incomingPacket
);
240 CFRetain(incomingPacket
);
241 result
= errSecSuccess
;
243 case kAwaitingSignature
:
244 if (CFEqualSafe(incomingPacket
, session
->_receivedDHKeyMessage
))
245 SecOTRAppendRevealSignatureMessage(session
, negotiationResponse
);
246 result
= errSecSuccess
;
250 case kAwaitingRevealSignature
:
251 result
= errSecSuccess
;
254 result
= errSecInteractionNotAllowed
;
263 static OSStatus
SecOTRSExtractR(SecOTRSessionRef session
,
264 const uint8_t **messageBytes
,
267 OSStatus result
= errSecDecode
;
269 OTRMessageType messageType
= kDHMessage
; // Suppress warning
271 ReadHeader(messageBytes
, messageSize
, &messageType
);
272 require(messageType
== kRevealSignatureMessage
, exit
);
276 ReadLong(messageBytes
, messageSize
, &rSize
);
277 require(rSize
== kOTRAuthKeyBytes
, exit
);
280 memcpy(session
->_r
, *messageBytes
, kOTRAuthKeyBytes
);
282 *messageBytes
+= kOTRAuthKeyBytes
;
283 *messageSize
-= kOTRAuthKeyBytes
;
285 result
= errSecSuccess
;
290 static OSStatus
FindEncGYInDHPacket(SecOTRSessionRef session
,
291 const uint8_t **dhMessageBytesPtr
,
292 size_t *messageSizePtr
,
293 size_t* encGYBufferSize
)
295 OSStatus result
= errSecParam
;
296 require_action(*encGYBufferSize
>= kExponentiationBytes
+ 4, exit
, result
= errSecParam
);
298 OTRMessageType messageType
;
299 result
= ReadHeader(dhMessageBytesPtr
, messageSizePtr
, &messageType
);
300 require_noerr(result
, exit
);
301 require_action(messageType
== kDHMessage
, exit
, result
= errSecDecode
);
303 uint32_t readEncSize
;
304 result
= ReadLong(dhMessageBytesPtr
, messageSizePtr
, &readEncSize
);
305 require_noerr(result
, exit
);
307 *encGYBufferSize
= readEncSize
;
309 // Don't bother erasing the public gy decrypted, it's public after all.
314 static OSStatus
SecOTRSExtractRAndTheirDHKey(SecOTRSessionRef session
,
315 const uint8_t **messageBytes
,
318 OSStatus result
= errSecDecode
;
320 require(session
->_receivedDHMessage
!= NULL
, exit
);
321 result
= SecOTRSExtractR(session
, messageBytes
, messageSize
);
322 require_noerr(result
, exit
);
324 uint8_t gxiDecrypted
[kExponentiationBytes
+ 4];
325 const uint8_t *gxiDecryptedBuffer
= gxiDecrypted
;
327 const uint8_t* dhMessageBytes
= CFDataGetBytePtr(session
->_receivedDHMessage
);
328 size_t dhMessageSize
= (size_t)CFDataGetLength(session
->_receivedDHMessage
);
330 size_t encGYSize
= sizeof(gxiDecrypted
);
331 result
= FindEncGYInDHPacket(session
, &dhMessageBytes
, &dhMessageSize
, &encGYSize
);
332 require_noerr(result
, exit
);
333 require_action(encGYSize
<= kExponentiationBytes
+ 4, exit
, result
= errSecDecode
);
335 AES_CTR_IV0_Transform(sizeof(session
->_r
), session
->_r
, encGYSize
, dhMessageBytes
, gxiDecrypted
);
337 result
= SecOTRSetupTheirKeyFrom(session
, &gxiDecryptedBuffer
, &encGYSize
);
340 // Don't bother erasing the public gy decrypted, it's public after all.
344 static OSStatus
SecVerifySignatureAndMac(SecOTRSessionRef session
,
346 const uint8_t **signatureAndMacBytes
,
347 size_t *signatureAndMacSize
)
349 OSStatus result
= errSecDecode
;
351 uint8_t m1
[kOTRAuthMACKeyBytes
];
352 uint8_t m2
[kOTRAuthMACKeyBytes
];
353 uint8_t c
[kOTRAuthKeyBytes
];
356 cc_unit s
[kExponentiationUnits
];
358 SecPDHKeyGenerateS(session
->_myKey
, session
->_theirKey
, s
);
359 // Derive M1, M2 and C, either prime or normal versions.
360 DeriveOTR256BitsFromS(usePrimes
? kM1Prime
: kM1
,
361 kExponentiationUnits
, s
, sizeof(m1
), m1
);
362 DeriveOTR256BitsFromS(usePrimes
? kM2Prime
: kM2
,
363 kExponentiationUnits
, s
, sizeof(m2
), m2
);
364 DeriveOTR128BitPairFromS(kCs
,
365 kExponentiationUnits
, s
,
366 sizeof(c
),usePrimes
? NULL
: c
,
367 sizeof(c
), usePrimes
? c
: NULL
);
371 cchmac_di_decl(ccsha256_di(), mBContext
);
373 cchmac_init(ccsha256_di(), mBContext
, sizeof(m1
), m1
);
376 CFMutableDataRef toHash
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
378 SecPDHKAppendSerialization(session
->_theirKey
, toHash
);
379 SecFDHKAppendPublicSerialization(session
->_myKey
, toHash
);
381 cchmac_update(ccsha256_di(), mBContext
, (size_t)CFDataGetLength(toHash
), CFDataGetBytePtr(toHash
));
383 CFReleaseNull(toHash
);
386 const uint8_t* encSigDataBlobStart
= *signatureAndMacBytes
;
389 result
= ReadLong(signatureAndMacBytes
, signatureAndMacSize
, &xbSize
);
390 require_noerr(result
, exit
);
391 require(xbSize
> 4, exit
);
392 require(xbSize
<= *signatureAndMacSize
, exit
);
394 uint8_t signatureMac
[CCSHA256_OUTPUT_SIZE
];
395 cchmac(ccsha256_di(), sizeof(m2
), m2
, xbSize
+ 4, encSigDataBlobStart
, signatureMac
);
397 require(xbSize
+ kSHA256HMAC160Bytes
<= *signatureAndMacSize
, exit
);
398 const uint8_t *macStart
= *signatureAndMacBytes
+ xbSize
;
400 // check the outer hmac
401 require(0 == memcmp(macStart
, signatureMac
, kSHA256HMAC160Bytes
), exit
);
406 // Decrypt and copy the signature block
407 AES_CTR_IV0_Transform(sizeof(c
), c
, xbSize
, *signatureAndMacBytes
, xb
);
409 const uint8_t* signaturePacket
= xb
;
410 size_t signaturePacketSize
= xbSize
;
413 result
= ReadShort(&signaturePacket
, &signaturePacketSize
, &pubKeyType
);
414 require_noerr(result
, exit
);
415 require_action(pubKeyType
== 0xF000, exit
, result
= errSecUnimplemented
);
418 result
= ReadLong(&signaturePacket
, &signaturePacketSize
, &pubKeySize
);
419 require_noerr(result
, exit
);
420 require_action(pubKeySize
<= signaturePacketSize
, exit
, result
= errSecDecode
);
421 require(((CFIndex
)pubKeySize
) >= 0, exit
);
423 // Add the signature and keyid to the hash.
424 // PUBKEY of our type is 2 bytes of type, 2 bytes of size and size bytes.
425 // Key ID is 4 bytes.
426 cchmac_update(ccsha256_di(), mBContext
, 2 + 4 + pubKeySize
+ 4, xb
);
428 uint8_t mb
[CCSHA256_OUTPUT_SIZE
];
429 cchmac_final(ccsha256_di(), mBContext
, mb
);
431 // Make reference to the deflated key
432 require_action(SecOTRPIEqualToBytes(session
->_them
, signaturePacket
, (CFIndex
)pubKeySize
), exit
, result
= errSecAuthFailed
);
434 signaturePacket
+= pubKeySize
;
435 signaturePacketSize
-= pubKeySize
;
437 result
= ReadLong(&signaturePacket
, &signaturePacketSize
, &session
->_theirKeyID
);
438 require_noerr(result
, exit
);
441 result
= ReadLong(&signaturePacket
, &signaturePacketSize
, &sigSize
);
442 require_noerr(result
, exit
);
443 require_action(sigSize
<= signaturePacketSize
, exit
, result
= errSecDecode
);
445 bool bresult
= SecOTRPIVerifySignature(session
->_them
, mb
, sizeof(mb
), signaturePacket
, sigSize
, NULL
);
446 result
= bresult
? errSecSuccess
: errSecDecode
;
447 require_noerr(result
, exit
);
452 bzero(m1
, sizeof(m1
));
453 bzero(m2
, sizeof(m2
));
459 static OSStatus
SecOTRSProcessRevealSignatureMessage(SecOTRSessionRef session
,
460 CFDataRef incomingPacket
,
461 CFMutableDataRef negotiationResponse
)
463 OSStatus result
= errSecParam
;
465 require_action_quiet(session
->_state
== kAwaitingRevealSignature
, exit
, result
= errSecSuccess
);
467 const uint8_t *messageBytes
= CFDataGetBytePtr(incomingPacket
);
468 size_t messageSize
= (size_t)CFDataGetLength(incomingPacket
);
470 result
= SecOTRSExtractRAndTheirDHKey(session
, &messageBytes
, &messageSize
);
471 require_noerr(result
, exit
);
473 result
= SecVerifySignatureAndMac(session
, false, &messageBytes
, &messageSize
);
474 require_noerr(result
, exit
);
476 SecOTRAppendSignatureMessage(session
, negotiationResponse
);
478 session
->_state
= kDone
;
479 result
= errSecSuccess
;
484 static OSStatus
SecOTRSProcessSignatureMessage(SecOTRSessionRef session
,
485 CFDataRef incomingPacket
,
486 CFMutableDataRef negotiationResponse
)
488 OSStatus result
= errSecParam
;
490 require_action_quiet(session
->_state
== kAwaitingSignature
, exit
, result
= errSecSuccess
);
492 const uint8_t *messageBytes
= CFDataGetBytePtr(incomingPacket
);
493 size_t messageSize
= (size_t)CFDataGetLength(incomingPacket
);
495 OTRMessageType messageType
;
496 result
= ReadHeader(&messageBytes
, &messageSize
, &messageType
);
497 require_noerr(result
, exit
);
498 require_action(messageType
== kSignatureMessage
, exit
, result
= errSecDecode
);
500 result
= SecVerifySignatureAndMac(session
, true, &messageBytes
, &messageSize
);
501 require_noerr(result
, exit
);
503 CFReleaseNull(session
->_receivedDHKeyMessage
);
504 session
->_state
= kDone
;
506 result
= errSecSuccess
;
511 OSStatus
SecOTRSProcessPacket(SecOTRSessionRef session
,
512 CFDataRef incomingPacket
,
513 CFMutableDataRef negotiationResponse
)
515 __block OSStatus result
= errSecParam
;
517 require(CFDataGetLength(incomingPacket
) > 0, fail
);
518 dispatch_sync(session
->_queue
, ^{
519 CFMutableDataRef decodedBytes
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
520 SecOTRGetIncomingBytes(incomingPacket
, decodedBytes
);
522 const uint8_t* bytes
= CFDataGetBytePtr(decodedBytes
);
523 size_t size
= CFDataGetLength(decodedBytes
);
525 OTRMessageType packetType
= kInvalidMessage
;
526 if (ReadHeader(&bytes
, &size
, &packetType
))
527 packetType
= kInvalidMessage
;
529 CFMutableDataRef destinationMessage
;
530 if (session
->_textOutput
) {
531 destinationMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
533 destinationMessage
= negotiationResponse
;
536 switch (packetType
) {
538 result
= SecOTRSProcessDHMessage(session
, decodedBytes
, destinationMessage
);
541 result
= SecOTRSProcessDHKeyMessage(session
, decodedBytes
, destinationMessage
);
543 case kRevealSignatureMessage
:
544 result
= SecOTRSProcessRevealSignatureMessage(session
, decodedBytes
, destinationMessage
);
546 case kSignatureMessage
:
547 result
= SecOTRSProcessSignatureMessage(session
, decodedBytes
, destinationMessage
);
550 result
= errSecDecode
;
554 if (result
!= errSecSuccess
) {
555 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
);
558 if (session
->_textOutput
) {
559 SecOTRPrepareOutgoingBytes(destinationMessage
, negotiationResponse
);
560 CFReleaseSafe(destinationMessage
);
562 CFReleaseSafe(decodedBytes
);