]> git.saurik.com Git - apple/security.git/blob - sec/Security/SecOTRSessionAKE.c
Security-55471.14.4.tar.gz
[apple/security.git] / sec / Security / SecOTRSessionAKE.c
1 //
2 // SecOTRSession.c
3 // libsecurity_libSecOTR
4 //
5 // Created by Mitch Adler on 2/22/11.
6 // Copyright 2011 Apple Inc. All rights reserved.
7 //
8
9 #include "SecOTRSession.h"
10
11 #include "SecOTRMath.h"
12 #include "SecOTRIdentityPriv.h"
13 #include "SecOTRSessionPriv.h"
14 #include "SecOTRPackets.h"
15 #include "SecOTRPacketData.h"
16 #include "SecOTRDHKey.h"
17
18 #include <utilities/SecCFWrappers.h>
19
20 #include <CoreFoundation/CFRuntime.h>
21 #include <CoreFoundation/CFString.h>
22
23 #include <Security/SecBase.h>
24 #include <Security/SecRandom.h>
25
26 #include <AssertMacros.h>
27
28 #include <corecrypto/cchmac.h>
29 #include <corecrypto/ccsha2.h>
30
31 #include <string.h>
32
33 static void SecOTRInitMyDHKeys(SecOTRSessionRef session)
34 {
35 CFReleaseNull(session->_myKey);
36 session->_myKey = SecOTRFullDHKCreate(kCFAllocatorDefault);
37 CFReleaseNull(session->_myNextKey);
38 session->_myNextKey = SecOTRFullDHKCreate(kCFAllocatorDefault);
39 session->_keyID = 1;
40
41 bzero(session->_keyCache, sizeof(session->_keyCache));
42 }
43
44 OSStatus SecOTRSAppendStartPacket(SecOTRSessionRef session, CFMutableDataRef appendPacket)
45 {
46 __block OSStatus result = errSecSuccess;
47
48 dispatch_sync(session->_queue, ^{
49 session->_state = kAwaitingDHKey;
50
51 // Generate r and x and calculate gx:
52 SecOTRInitMyDHKeys(session);
53
54 CFMutableDataRef destinationMessage;
55 if (session->_textOutput) {
56 destinationMessage = CFDataCreateMutable(kCFAllocatorDefault, 0);
57 } else {
58 destinationMessage = appendPacket;
59 }
60
61
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);
68 }
69 }
70 });
71
72 return result;
73 }
74
75 OSStatus SecOTRSAppendRestartPacket(SecOTRSessionRef session, CFMutableDataRef appendPacket)
76 {
77 __block OSStatus result = errSecSuccess;
78
79 dispatch_sync(session->_queue, ^{
80 if (!session->_myKey) {
81 secerror("_myKey is NULL, avoiding crash");
82 result = errSecDecode;
83 return;
84 }
85 CFMutableDataRef destinationMessage;
86 if (session->_textOutput) {
87 destinationMessage = CFDataCreateMutable(kCFAllocatorDefault, 0);
88 } else {
89 destinationMessage = appendPacket;
90 }
91
92 session->_state = kAwaitingDHKey;
93 CFReleaseNull(session->_receivedDHMessage);
94 CFReleaseNull(session->_receivedDHKeyMessage);
95
96 SecOTRAppendDHMessage(session, destinationMessage);
97 if (session->_textOutput) {
98 SecOTRPrepareOutgoingBytes(destinationMessage, appendPacket);
99 CFReleaseSafe(destinationMessage);
100 }
101 });
102
103 return result;
104 }
105
106 static const uint8_t* FindGXHash(CFDataRef dhPacket)
107 {
108 const uint8_t* messageBytes = CFDataGetBytePtr(dhPacket);
109 size_t remainingBytes = (size_t)CFDataGetLength(dhPacket);
110
111 OTRMessageType messageType;
112
113 require_noerr(ReadHeader(&messageBytes, &remainingBytes, &messageType), fail);
114 require(messageType == kDHMessage, fail);
115
116 uint32_t egxiLength = 0;
117 require_noerr(ReadLong(&messageBytes, &remainingBytes, & egxiLength), fail);
118 require(egxiLength <= remainingBytes, fail);
119 messageBytes += egxiLength;
120 remainingBytes -= egxiLength;
121
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);
126
127 return messageBytes;
128
129 fail:
130 return NULL;
131 }
132
133 static bool SecOTRMyGXHashIsBigger(SecOTRSessionRef session, CFDataRef dhCommitMessage)
134 {
135 bool mineIsBigger = false;
136
137 CFMutableDataRef myDHCommitMessage = CFDataCreateMutable(kCFAllocatorDefault, 0);
138
139 SecOTRAppendDHMessage(session, myDHCommitMessage);
140
141 const uint8_t* myHash = FindGXHash(myDHCommitMessage);
142 const uint8_t* theirHash = FindGXHash(dhCommitMessage);
143
144 require(myHash, fail);
145 require(theirHash, fail);
146
147 mineIsBigger = 0 < memcmp(myHash, theirHash, CCSHA256_OUTPUT_SIZE);
148
149 fail:
150 CFReleaseNull(myDHCommitMessage);
151 return mineIsBigger;
152 }
153
154 static OSStatus SecOTRSProcessDHMessage(SecOTRSessionRef session,
155 CFDataRef incomingPacket,
156 CFMutableDataRef negotiationResponse)
157 {
158 OSStatus result = errSecParam;
159
160 switch (session->_state) {
161 case kAwaitingDHKey:
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;
168 break;
169 } // Else intentionally fall through to idle
170 case kAwaitingSignature:
171 case kIdle:
172 case kDone:
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);
178
179 // Keep the packet for use later.
180 CFReleaseNull(session->_receivedDHMessage);
181 session->_receivedDHMessage = CFDataCreateCopy(kCFAllocatorDefault, incomingPacket);
182
183 session->_state = kAwaitingRevealSignature;
184 result = errSecSuccess;
185 break;
186 default:
187 result = errSecInteractionNotAllowed;
188 break;
189 }
190
191 return result;
192 }
193
194 static OSStatus SecOTRSetupTheirKeyFrom(SecOTRSessionRef session, const uint8_t**data, size_t*size)
195 {
196 SecOTRPublicDHKeyRef tempKey = SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault, data, size);
197 require(tempKey != NULL, fail);
198 session->_theirKey = tempKey;
199 session->_theirKeyID = 1;
200
201 return errSecSuccess;
202
203 fail:
204 return errSecDecode;
205 }
206
207 static OSStatus SecOTRSExtractTheirPublicDHKey(SecOTRSessionRef session, CFDataRef dhPacket)
208 {
209 OSStatus result = errSecParam;
210
211 const uint8_t *messageBytes = CFDataGetBytePtr(dhPacket);
212 size_t messageSize = (size_t)CFDataGetLength(dhPacket);
213 OTRMessageType messageType = kDHMessage; // Suppress warning.
214
215 ReadHeader(&messageBytes, &messageSize, &messageType);
216 require(messageType == kDHKeyMessage, exit);
217
218 result = SecOTRSetupTheirKeyFrom(session, &messageBytes, &messageSize);
219
220 exit:
221 return result;
222 }
223
224
225 static OSStatus SecOTRSProcessDHKeyMessage(SecOTRSessionRef session,
226 CFDataRef incomingPacket,
227 CFMutableDataRef negotiationResponse)
228 {
229 OSStatus result = errSecUnimplemented;
230
231 result = SecOTRSExtractTheirPublicDHKey(session, incomingPacket);
232 require_noerr(result, exit);
233
234 switch (session->_state) {
235 case kAwaitingDHKey:
236 CFReleaseNull(session->_receivedDHKeyMessage);
237 SecOTRAppendRevealSignatureMessage(session, negotiationResponse);
238 session->_state = kAwaitingSignature;
239 session->_receivedDHKeyMessage = CFDataCreateCopy(kCFAllocatorDefault, incomingPacket);
240 CFRetain(incomingPacket);
241 result = errSecSuccess;
242 break;
243 case kAwaitingSignature:
244 if (CFEqualSafe(incomingPacket, session->_receivedDHKeyMessage))
245 SecOTRAppendRevealSignatureMessage(session, negotiationResponse);
246 result = errSecSuccess;
247 break;
248 case kIdle:
249 case kDone:
250 case kAwaitingRevealSignature:
251 result = errSecSuccess;
252 break;
253 default:
254 result = errSecInteractionNotAllowed;
255 break;
256 }
257
258 exit:
259 return result;
260 }
261
262
263 static OSStatus SecOTRSExtractR(SecOTRSessionRef session,
264 const uint8_t **messageBytes,
265 size_t *messageSize)
266 {
267 OSStatus result = errSecDecode;
268
269 OTRMessageType messageType = kDHMessage; // Suppress warning
270
271 ReadHeader(messageBytes, messageSize, &messageType);
272 require(messageType == kRevealSignatureMessage, exit);
273
274 {
275 uint32_t rSize = 0;
276 ReadLong(messageBytes, messageSize, &rSize);
277 require(rSize == kOTRAuthKeyBytes, exit);
278 }
279
280 memcpy(session->_r, *messageBytes, kOTRAuthKeyBytes);
281
282 *messageBytes += kOTRAuthKeyBytes;
283 *messageSize -= kOTRAuthKeyBytes;
284
285 result = errSecSuccess;
286 exit:
287 return result;
288 }
289
290 static OSStatus FindEncGYInDHPacket(SecOTRSessionRef session,
291 const uint8_t **dhMessageBytesPtr,
292 size_t *messageSizePtr,
293 size_t* encGYBufferSize)
294 {
295 OSStatus result = errSecParam;
296 require_action(*encGYBufferSize >= kExponentiationBytes + 4, exit, result = errSecParam);
297
298 OTRMessageType messageType;
299 result = ReadHeader(dhMessageBytesPtr, messageSizePtr, &messageType);
300 require_noerr(result, exit);
301 require_action(messageType == kDHMessage, exit, result = errSecDecode);
302
303 uint32_t readEncSize;
304 result = ReadLong(dhMessageBytesPtr, messageSizePtr, &readEncSize);
305 require_noerr(result, exit);
306
307 *encGYBufferSize = readEncSize;
308 exit:
309 // Don't bother erasing the public gy decrypted, it's public after all.
310 return result;
311
312 }
313
314 static OSStatus SecOTRSExtractRAndTheirDHKey(SecOTRSessionRef session,
315 const uint8_t **messageBytes,
316 size_t *messageSize)
317 {
318 OSStatus result = errSecDecode;
319
320 require(session->_receivedDHMessage != NULL, exit);
321 result = SecOTRSExtractR(session, messageBytes, messageSize);
322 require_noerr(result, exit);
323
324 uint8_t gxiDecrypted[kExponentiationBytes + 4];
325 const uint8_t *gxiDecryptedBuffer = gxiDecrypted;
326
327 const uint8_t* dhMessageBytes = CFDataGetBytePtr(session->_receivedDHMessage);
328 size_t dhMessageSize = (size_t)CFDataGetLength(session->_receivedDHMessage);
329
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);
334
335 AES_CTR_IV0_Transform(sizeof(session->_r), session->_r, encGYSize, dhMessageBytes, gxiDecrypted);
336
337 result = SecOTRSetupTheirKeyFrom(session, &gxiDecryptedBuffer, &encGYSize);
338
339 exit:
340 // Don't bother erasing the public gy decrypted, it's public after all.
341 return result;
342 }
343
344 static OSStatus SecVerifySignatureAndMac(SecOTRSessionRef session,
345 bool usePrimes,
346 const uint8_t **signatureAndMacBytes,
347 size_t *signatureAndMacSize)
348 {
349 OSStatus result = errSecDecode;
350
351 uint8_t m1[kOTRAuthMACKeyBytes];
352 uint8_t m2[kOTRAuthMACKeyBytes];
353 uint8_t c[kOTRAuthKeyBytes];
354
355 {
356 cc_unit s[kExponentiationUnits];
357
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);
368 bzero(s, sizeof(s));
369 }
370
371 cchmac_di_decl(ccsha256_di(), mBContext);
372
373 cchmac_init(ccsha256_di(), mBContext, sizeof(m1), m1);
374
375 {
376 CFMutableDataRef toHash = CFDataCreateMutable(kCFAllocatorDefault, 0);
377
378 SecPDHKAppendSerialization(session->_theirKey, toHash);
379 SecFDHKAppendPublicSerialization(session->_myKey, toHash);
380
381 cchmac_update(ccsha256_di(), mBContext, (size_t)CFDataGetLength(toHash), CFDataGetBytePtr(toHash));
382
383 CFReleaseNull(toHash);
384 }
385
386 const uint8_t* encSigDataBlobStart = *signatureAndMacBytes;
387
388 uint32_t xbSize = 0;
389 result = ReadLong(signatureAndMacBytes, signatureAndMacSize, &xbSize);
390 require_noerr(result, exit);
391 require(xbSize > 4, exit);
392 require(xbSize <= *signatureAndMacSize, exit);
393
394 uint8_t signatureMac[CCSHA256_OUTPUT_SIZE];
395 cchmac(ccsha256_di(), sizeof(m2), m2, xbSize + 4, encSigDataBlobStart, signatureMac);
396
397 require(xbSize + kSHA256HMAC160Bytes <= *signatureAndMacSize, exit);
398 const uint8_t *macStart = *signatureAndMacBytes + xbSize;
399
400 // check the outer hmac
401 require(0 == memcmp(macStart, signatureMac, kSHA256HMAC160Bytes), exit);
402
403
404 {
405 uint8_t xb[xbSize];
406 // Decrypt and copy the signature block
407 AES_CTR_IV0_Transform(sizeof(c), c, xbSize, *signatureAndMacBytes, xb);
408
409 const uint8_t* signaturePacket = xb;
410 size_t signaturePacketSize = xbSize;
411
412 uint16_t pubKeyType;
413 result = ReadShort(&signaturePacket, &signaturePacketSize, &pubKeyType);
414 require_noerr(result, exit);
415 require_action(pubKeyType == 0xF000, exit, result = errSecUnimplemented);
416
417 uint32_t pubKeySize;
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);
422
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);
427
428 uint8_t mb[CCSHA256_OUTPUT_SIZE];
429 cchmac_final(ccsha256_di(), mBContext, mb);
430
431 // Make reference to the deflated key
432 require_action(SecOTRPIEqualToBytes(session->_them, signaturePacket, (CFIndex)pubKeySize), exit, result = errSecAuthFailed);
433
434 signaturePacket += pubKeySize;
435 signaturePacketSize -= pubKeySize;
436
437 result = ReadLong(&signaturePacket, &signaturePacketSize, &session->_theirKeyID);
438 require_noerr(result, exit);
439
440 uint32_t sigSize;
441 result = ReadLong(&signaturePacket, &signaturePacketSize, &sigSize);
442 require_noerr(result, exit);
443 require_action(sigSize <= signaturePacketSize, exit, result = errSecDecode);
444
445 bool bresult = SecOTRPIVerifySignature(session->_them, mb, sizeof(mb), signaturePacket, sigSize, NULL);
446 result = bresult ? errSecSuccess : errSecDecode;
447 require_noerr(result, exit);
448
449 }
450
451 exit:
452 bzero(m1, sizeof(m1));
453 bzero(m2, sizeof(m2));
454 bzero(c, sizeof(c));
455
456 return result;
457 }
458
459 static OSStatus SecOTRSProcessRevealSignatureMessage(SecOTRSessionRef session,
460 CFDataRef incomingPacket,
461 CFMutableDataRef negotiationResponse)
462 {
463 OSStatus result = errSecParam;
464
465 require_action_quiet(session->_state == kAwaitingRevealSignature, exit, result = errSecSuccess);
466
467 const uint8_t *messageBytes = CFDataGetBytePtr(incomingPacket);
468 size_t messageSize = (size_t)CFDataGetLength(incomingPacket);
469
470 result = SecOTRSExtractRAndTheirDHKey(session, &messageBytes, &messageSize);
471 require_noerr(result, exit);
472
473 result = SecVerifySignatureAndMac(session, false, &messageBytes, &messageSize);
474 require_noerr(result, exit);
475
476 SecOTRAppendSignatureMessage(session, negotiationResponse);
477
478 session->_state = kDone;
479 result = errSecSuccess;
480 exit:
481 return result;
482 }
483
484 static OSStatus SecOTRSProcessSignatureMessage(SecOTRSessionRef session,
485 CFDataRef incomingPacket,
486 CFMutableDataRef negotiationResponse)
487 {
488 OSStatus result = errSecParam;
489
490 require_action_quiet(session->_state == kAwaitingSignature, exit, result = errSecSuccess);
491
492 const uint8_t *messageBytes = CFDataGetBytePtr(incomingPacket);
493 size_t messageSize = (size_t)CFDataGetLength(incomingPacket);
494
495 OTRMessageType messageType;
496 result = ReadHeader(&messageBytes, &messageSize, &messageType);
497 require_noerr(result, exit);
498 require_action(messageType == kSignatureMessage, exit, result = errSecDecode);
499
500 result = SecVerifySignatureAndMac(session, true, &messageBytes, &messageSize);
501 require_noerr(result, exit);
502
503 CFReleaseNull(session->_receivedDHKeyMessage);
504 session->_state = kDone;
505
506 result = errSecSuccess;
507 exit:
508 return result;
509 }
510
511 OSStatus SecOTRSProcessPacket(SecOTRSessionRef session,
512 CFDataRef incomingPacket,
513 CFMutableDataRef negotiationResponse)
514 {
515 __block OSStatus result = errSecParam;
516
517 require(CFDataGetLength(incomingPacket) > 0, fail);
518 dispatch_sync(session->_queue, ^{
519 CFMutableDataRef decodedBytes = CFDataCreateMutable(kCFAllocatorDefault, 0);
520 SecOTRGetIncomingBytes(incomingPacket, decodedBytes);
521
522 const uint8_t* bytes = CFDataGetBytePtr(decodedBytes);
523 size_t size = CFDataGetLength(decodedBytes);
524
525 OTRMessageType packetType = kInvalidMessage;
526 if (ReadHeader(&bytes, &size, &packetType))
527 packetType = kInvalidMessage;
528
529 CFMutableDataRef destinationMessage;
530 if (session->_textOutput) {
531 destinationMessage = CFDataCreateMutable(kCFAllocatorDefault, 0);
532 } else {
533 destinationMessage = negotiationResponse;
534 }
535
536 switch (packetType) {
537 case kDHMessage:
538 result = SecOTRSProcessDHMessage(session, decodedBytes, destinationMessage);
539 break;
540 case kDHKeyMessage:
541 result = SecOTRSProcessDHKeyMessage(session, decodedBytes, destinationMessage);
542 break;
543 case kRevealSignatureMessage:
544 result = SecOTRSProcessRevealSignatureMessage(session, decodedBytes, destinationMessage);
545 break;
546 case kSignatureMessage:
547 result = SecOTRSProcessSignatureMessage(session, decodedBytes, destinationMessage);
548 break;
549 default:
550 result = errSecDecode;
551 break;
552 };
553
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);
556 }
557
558 if (session->_textOutput) {
559 SecOTRPrepareOutgoingBytes(destinationMessage, negotiationResponse);
560 CFReleaseSafe(destinationMessage);
561 }
562 CFReleaseSafe(decodedBytes);
563 });
564
565 fail:
566 return result;
567 }