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