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
49static 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
57    bzero(session->_keyCache, sizeof(session->_keyCache));
58}
59
60OSStatus SecOTRSAppendStartPacket(SecOTRSessionRef session, CFMutableDataRef appendPacket)
61{
62    __block OSStatus result = errSecSuccess;
63
64    dispatch_sync(session->_queue, ^{
65        session->_state = kAwaitingDHKey;
66
67        // Generate r and x and calculate gx:
68        SecOTRInitMyDHKeys(session);
69
70        CFMutableDataRef destinationMessage = NULL;
71        if (session->_textOutput) {
72            destinationMessage = CFDataCreateMutable(kCFAllocatorDefault, 0);
73        } else {
74            destinationMessage = CFRetainSafe(appendPacket);
75        }
76
77
78        result = SecRandomCopyBytes(kSecRandomDefault, sizeof(session->_r), session->_r);
79        if (result == errSecSuccess) {
80            SecOTRAppendDHMessage(session, destinationMessage);
81            if (session->_textOutput) {
82                SecOTRPrepareOutgoingBytes(destinationMessage, appendPacket);
83            }
84        }
85        CFReleaseSafe(destinationMessage);
86    });
87
88    return result;
89}
90
91OSStatus SecOTRSAppendRestartPacket(SecOTRSessionRef session, CFMutableDataRef appendPacket)
92{
93    __block OSStatus result = errSecSuccess;
94
95    dispatch_sync(session->_queue, ^{
96        if (!session->_myKey) {
97            secerror("_myKey is NULL, avoiding crash");
98            result = errSecDecode;
99            return;
100        }
101        CFMutableDataRef destinationMessage;
102        if (session->_textOutput) {
103            destinationMessage = CFDataCreateMutable(kCFAllocatorDefault, 0);
104        } else {
105            destinationMessage = CFRetainSafe(appendPacket);
106        }
107
108        session->_state = kAwaitingDHKey;
109        CFReleaseNull(session->_receivedDHMessage);
110        CFReleaseNull(session->_receivedDHKeyMessage);
111
112        SecOTRAppendDHMessage(session, destinationMessage);
113        if (session->_textOutput) {
114            SecOTRPrepareOutgoingBytes(destinationMessage, appendPacket);
115        }
116        CFReleaseSafe(destinationMessage);
117    });
118
119    return result;
120}
121
122static const uint8_t* FindGXHash(CFDataRef dhPacket)
123{
124    const uint8_t* messageBytes = CFDataGetBytePtr(dhPacket);
125    size_t remainingBytes = (size_t)CFDataGetLength(dhPacket);
126
127    OTRMessageType messageType;
128
129    require_noerr(ReadHeader(&messageBytes, &remainingBytes, &messageType), fail);
130    require(messageType == kDHMessage, fail);
131
132    uint32_t egxiLength = 0;
133    require_noerr(ReadLong(&messageBytes, &remainingBytes, & egxiLength), fail);
134    require(egxiLength <= remainingBytes, fail);
135    messageBytes += egxiLength;
136    remainingBytes -= egxiLength;
137
138    uint32_t dataLength = 0;
139    require_noerr(ReadLong(&messageBytes, &remainingBytes, &dataLength), fail);
140    require(dataLength <= remainingBytes, fail);
141    require(dataLength == CCSHA256_OUTPUT_SIZE, fail);
142
143    return messageBytes;
144
145fail:
146    return NULL;
147}
148
149static bool SecOTRMyGXHashIsBigger(SecOTRSessionRef session, CFDataRef dhCommitMessage)
150{
151    bool mineIsBigger = false;
152
153    CFMutableDataRef myDHCommitMessage = CFDataCreateMutable(kCFAllocatorDefault, 0);
154
155    SecOTRAppendDHMessage(session, myDHCommitMessage);
156
157    const uint8_t* myHash = FindGXHash(myDHCommitMessage);
158    const uint8_t* theirHash = FindGXHash(dhCommitMessage);
159
160    require(myHash, fail);
161    require(theirHash, fail);
162
163    mineIsBigger = 0 < memcmp(myHash, theirHash, CCSHA256_OUTPUT_SIZE);
164
165fail:
166    CFReleaseNull(myDHCommitMessage);
167    return mineIsBigger;
168}
169
170static OSStatus SecOTRSProcessDHMessage(SecOTRSessionRef session,
171                                        CFDataRef incomingPacket,
172                                        CFMutableDataRef negotiationResponse)
173{
174    OSStatus result = errSecParam;
175
176    switch (session->_state) {
177        case kAwaitingDHKey:
178            // Compare hash values.
179            if (SecOTRMyGXHashIsBigger(session, incomingPacket)) {
180                // If we're bigger we resend to force them to deal.
181                CFReleaseNull(session->_receivedDHMessage);
182                SecOTRAppendDHMessage(session, negotiationResponse);
183                result = errSecSuccess;
184                break;
185            } // Else intentionally fall through to idle
186        case kAwaitingSignature:
187        case kIdle:
188        case kDone:
189            // Generate a new X and GX..
190            SecOTRInitMyDHKeys(session);
191            // If we were already waiting on reveal, then just send the packet again
192        case kAwaitingRevealSignature:
193            SecOTRAppendDHKeyMessage(session, negotiationResponse);
194
195            // Keep the packet for use later.
196            CFReleaseNull(session->_receivedDHMessage);
197            session->_receivedDHMessage = CFDataCreateCopy(kCFAllocatorDefault, incomingPacket);
198
199            session->_state = kAwaitingRevealSignature;
200            result = errSecSuccess;
201            break;
202        default:
203            result = errSecInteractionNotAllowed;
204            break;
205    }
206
207    return result;
208}
209
210static OSStatus SecOTRSetupTheirKeyFrom(SecOTRSessionRef session, const uint8_t**data, size_t*size)
211{
212    SecOTRPublicDHKeyRef tempKey = SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault, data, size);
213    require(tempKey != NULL, fail);
214    session->_theirKey = tempKey;
215    session->_theirKeyID = 1;
216
217    return errSecSuccess;
218
219fail:
220    return errSecDecode;
221}
222
223static OSStatus SecOTRSExtractTheirPublicDHKey(SecOTRSessionRef session, CFDataRef dhPacket)
224{
225    OSStatus result = errSecParam;
226
227    const uint8_t *messageBytes = CFDataGetBytePtr(dhPacket);
228    size_t messageSize = (size_t)CFDataGetLength(dhPacket);
229    OTRMessageType messageType = kDHMessage; // Suppress warning.
230
231    ReadHeader(&messageBytes, &messageSize, &messageType);
232    require(messageType == kDHKeyMessage, exit);
233
234    result = SecOTRSetupTheirKeyFrom(session, &messageBytes, &messageSize);
235
236exit:
237    return result;
238}
239
240
241static OSStatus SecOTRSProcessDHKeyMessage(SecOTRSessionRef session,
242                                        CFDataRef incomingPacket,
243                                        CFMutableDataRef negotiationResponse)
244{
245    OSStatus result = errSecUnimplemented;
246
247    result = SecOTRSExtractTheirPublicDHKey(session, incomingPacket);
248    require_noerr(result, exit);
249
250    switch (session->_state) {
251        case kAwaitingDHKey:
252            CFReleaseNull(session->_receivedDHKeyMessage);
253            SecOTRAppendRevealSignatureMessage(session, negotiationResponse);
254            session->_state = kAwaitingSignature;
255            session->_receivedDHKeyMessage = CFDataCreateCopy(kCFAllocatorDefault, incomingPacket);
256            result = errSecSuccess;
257            break;
258        case kAwaitingSignature:
259            if (CFEqualSafe(incomingPacket, session->_receivedDHKeyMessage))
260                SecOTRAppendRevealSignatureMessage(session, negotiationResponse);
261            result = errSecSuccess;
262            break;
263        case kIdle:
264        case kDone:
265        case kAwaitingRevealSignature:
266            result = errSecSuccess;
267            break;
268        default:
269            result = errSecInteractionNotAllowed;
270            break;
271    }
272
273exit:
274    return result;
275}
276
277
278static OSStatus SecOTRSExtractR(SecOTRSessionRef session,
279                                const uint8_t **messageBytes,
280                                size_t *messageSize)
281{
282    OSStatus result = errSecDecode;
283
284    OTRMessageType messageType = kDHMessage; // Suppress warning
285
286    ReadHeader(messageBytes, messageSize, &messageType);
287    require(messageType == kRevealSignatureMessage, exit);
288
289    {
290        uint32_t rSize = 0;
291        ReadLong(messageBytes, messageSize, &rSize);
292        require(rSize == kOTRAuthKeyBytes, exit);
293    }
294
295    memcpy(session->_r, *messageBytes, kOTRAuthKeyBytes);
296
297    *messageBytes += kOTRAuthKeyBytes;
298    *messageSize -= kOTRAuthKeyBytes;
299
300    result = errSecSuccess;
301exit:
302    return result;
303}
304
305static OSStatus FindEncGYInDHPacket(SecOTRSessionRef session,
306                                      const uint8_t **dhMessageBytesPtr,
307                                      size_t *messageSizePtr,
308                                      size_t* encGYBufferSize)
309{
310    OSStatus result = errSecParam;
311    require_action(*encGYBufferSize >= kExponentiationBytes + 4, exit, result = errSecParam);
312
313    OTRMessageType messageType;
314    result = ReadHeader(dhMessageBytesPtr, messageSizePtr, &messageType);
315    require_noerr(result, exit);
316    require_action(messageType == kDHMessage, exit, result = errSecDecode);
317
318    uint32_t readEncSize;
319    result = ReadLong(dhMessageBytesPtr, messageSizePtr, &readEncSize);
320    require_noerr(result, exit);
321
322    *encGYBufferSize = readEncSize;
323exit:
324    // Don't bother erasing the public gy decrypted, it's public after all.
325    return result;
326
327}
328
329static OSStatus SecOTRSExtractRAndTheirDHKey(SecOTRSessionRef session,
330                                     const uint8_t **messageBytes,
331                                     size_t *messageSize)
332{
333    OSStatus result = errSecDecode;
334
335    require(session->_receivedDHMessage != NULL, exit);
336    result = SecOTRSExtractR(session, messageBytes, messageSize);
337    require_noerr(result, exit);
338
339    uint8_t gxiDecrypted[kExponentiationBytes + 4];
340    const uint8_t *gxiDecryptedBuffer = gxiDecrypted;
341
342    const uint8_t* dhMessageBytes = CFDataGetBytePtr(session->_receivedDHMessage);
343    size_t dhMessageSize = (size_t)CFDataGetLength(session->_receivedDHMessage);
344
345    size_t encGYSize = sizeof(gxiDecrypted);
346    result = FindEncGYInDHPacket(session, &dhMessageBytes, &dhMessageSize, &encGYSize);
347    require_noerr(result, exit);
348    require_action(encGYSize <= kExponentiationBytes + 4, exit, result = errSecDecode);
349
350    AES_CTR_IV0_Transform(sizeof(session->_r), session->_r, encGYSize, dhMessageBytes, gxiDecrypted);
351
352    result = SecOTRSetupTheirKeyFrom(session, &gxiDecryptedBuffer, &encGYSize);
353
354exit:
355    // Don't bother erasing the public gy decrypted, it's public after all.
356    return result;
357}
358
359static OSStatus SecVerifySignatureAndMac(SecOTRSessionRef session,
360                                         bool usePrimes,
361                                         const uint8_t **signatureAndMacBytes,
362                                         size_t *signatureAndMacSize)
363{
364    OSStatus result = errSecDecode;
365
366    uint8_t m1[kOTRAuthMACKeyBytes];
367    uint8_t m2[kOTRAuthMACKeyBytes];
368    uint8_t c[kOTRAuthKeyBytes];
369
370    {
371        cc_unit s[kExponentiationUnits];
372
373        SecPDHKeyGenerateS(session->_myKey, session->_theirKey, s);
374        // Derive M1, M2 and C, either prime or normal versions.
375        DeriveOTR256BitsFromS(usePrimes ? kM1Prime : kM1,
376                              kExponentiationUnits, s, sizeof(m1), m1);
377        DeriveOTR256BitsFromS(usePrimes ? kM2Prime : kM2,
378                              kExponentiationUnits, s, sizeof(m2), m2);
379        DeriveOTR128BitPairFromS(kCs,
380                                 kExponentiationUnits, s,
381                                 sizeof(c),usePrimes ? NULL : c,
382                                 sizeof(c), usePrimes ? c : NULL);
383        bzero(s, sizeof(s));
384    }
385
386    cchmac_di_decl(ccsha256_di(), mBContext);
387
388    cchmac_init(ccsha256_di(), mBContext, sizeof(m1), m1);
389
390    {
391        CFMutableDataRef toHash = CFDataCreateMutable(kCFAllocatorDefault, 0);
392
393        SecPDHKAppendSerialization(session->_theirKey, toHash);
394        SecFDHKAppendPublicSerialization(session->_myKey, toHash);
395
396        cchmac_update(ccsha256_di(), mBContext, (size_t)CFDataGetLength(toHash), CFDataGetBytePtr(toHash));
397
398        CFReleaseNull(toHash);
399    }
400
401    const uint8_t* encSigDataBlobStart = *signatureAndMacBytes;
402
403    uint32_t xbSize = 0;
404    result = ReadLong(signatureAndMacBytes, signatureAndMacSize, &xbSize);
405    require_noerr(result, exit);
406    require_action(xbSize > 4, exit, result = errSecDecode);
407    require_action(xbSize <= *signatureAndMacSize, exit, result = errSecDecode);
408
409    uint8_t signatureMac[CCSHA256_OUTPUT_SIZE];
410    cchmac(ccsha256_di(), sizeof(m2), m2, xbSize + 4, encSigDataBlobStart, signatureMac);
411
412    require(xbSize + kSHA256HMAC160Bytes <= *signatureAndMacSize, exit);
413    const uint8_t *macStart = *signatureAndMacBytes + xbSize;
414
415    // check the outer hmac
416    require_action(0 == memcmp(macStart, signatureMac, kSHA256HMAC160Bytes), exit, result = errSecDecode);
417
418
419    {
420        uint8_t xb[xbSize];
421        // Decrypt and copy the signature block
422        AES_CTR_IV0_Transform(sizeof(c), c, xbSize, *signatureAndMacBytes, xb);
423
424        const uint8_t* signaturePacket = xb;
425        size_t signaturePacketSize = xbSize;
426
427        uint16_t pubKeyType;
428        result = ReadShort(&signaturePacket, &signaturePacketSize, &pubKeyType);
429        require_noerr(result, exit);
430        require_action(pubKeyType == 0xF000, exit, result = errSecUnimplemented);
431
432        uint32_t pubKeySize;
433        result = ReadLong(&signaturePacket, &signaturePacketSize, &pubKeySize);
434        require_noerr(result, exit);
435        require_action(pubKeySize <= signaturePacketSize, exit, result = errSecDecode);
436        require(((CFIndex)pubKeySize) >= 0, exit);
437
438        // Add the signature and keyid to the hash.
439        // PUBKEY of our type is 2 bytes of type, 2 bytes of size and size bytes.
440        // Key ID is 4 bytes.
441        cchmac_update(ccsha256_di(), mBContext, 2 + 4 + pubKeySize + 4, xb);
442
443        uint8_t mb[CCSHA256_OUTPUT_SIZE];
444        cchmac_final(ccsha256_di(), mBContext, mb);
445
446        // Make reference to the deflated key
447        require_action(SecOTRPIEqualToBytes(session->_them, signaturePacket, (CFIndex)pubKeySize), exit, result = errSecAuthFailed);
448
449        signaturePacket += pubKeySize;
450        signaturePacketSize -= pubKeySize;
451
452        result = ReadLong(&signaturePacket, &signaturePacketSize, &session->_theirKeyID);
453        require_noerr(result, exit);
454
455        uint32_t sigSize;
456        result = ReadLong(&signaturePacket, &signaturePacketSize, &sigSize);
457        require_noerr(result, exit);
458        require_action(sigSize <= signaturePacketSize, exit, result = errSecDecode);
459
460        bool bresult = SecOTRPIVerifySignature(session->_them, mb, sizeof(mb), signaturePacket, sigSize, NULL);
461        result = bresult ? errSecSuccess : errSecDecode;
462        require_noerr(result, exit);
463
464    }
465
466exit:
467    bzero(m1, sizeof(m1));
468    bzero(m2, sizeof(m2));
469    bzero(c, sizeof(c));
470
471    return result;
472}
473
474static OSStatus SecOTRSProcessRevealSignatureMessage(SecOTRSessionRef session,
475                                        CFDataRef incomingPacket,
476                                        CFMutableDataRef negotiationResponse)
477{
478    OSStatus result = errSecParam;
479
480    require_action_quiet(session->_state == kAwaitingRevealSignature, exit, result = errSecSuccess);
481
482    const uint8_t *messageBytes = CFDataGetBytePtr(incomingPacket);
483    size_t messageSize = (size_t)CFDataGetLength(incomingPacket);
484
485    result = SecOTRSExtractRAndTheirDHKey(session, &messageBytes, &messageSize);
486    require_noerr(result, exit);
487
488    result = SecVerifySignatureAndMac(session, false, &messageBytes, &messageSize);
489    require_noerr(result, exit);
490
491    SecOTRAppendSignatureMessage(session, negotiationResponse);
492
493    session->_state = kDone;
494    result = errSecSuccess;
495exit:
496    return result;
497}
498
499static OSStatus SecOTRSProcessSignatureMessage(SecOTRSessionRef session,
500                                        CFDataRef incomingPacket,
501                                        CFMutableDataRef negotiationResponse)
502{
503    OSStatus result = errSecParam;
504
505    require_action_quiet(session->_state == kAwaitingSignature, exit, result = errSecSuccess);
506
507    const uint8_t *messageBytes = CFDataGetBytePtr(incomingPacket);
508    size_t messageSize = (size_t)CFDataGetLength(incomingPacket);
509
510    OTRMessageType messageType;
511    result = ReadHeader(&messageBytes, &messageSize, &messageType);
512    require_noerr(result, exit);
513    require_action(messageType == kSignatureMessage, exit, result = errSecDecode);
514
515    result = SecVerifySignatureAndMac(session, true, &messageBytes, &messageSize);
516    require_noerr(result, exit);
517
518    CFReleaseNull(session->_receivedDHKeyMessage);
519    session->_state = kDone;
520
521    result = errSecSuccess;
522exit:
523    return result;
524}
525
526OSStatus SecOTRSProcessPacket(SecOTRSessionRef session,
527                              CFDataRef incomingPacket,
528                              CFMutableDataRef negotiationResponse)
529{
530    __block OSStatus result = errSecParam;
531
532    require(CFDataGetLength(incomingPacket) > 0, fail);
533    dispatch_sync(session->_queue, ^{
534        CFDataRef decodedBytes = SecOTRCopyIncomingBytes(incomingPacket);
535
536        const uint8_t* bytes = CFDataGetBytePtr(decodedBytes);
537        size_t size = CFDataGetLength(decodedBytes);
538
539        OTRMessageType packetType = kInvalidMessage;
540        if (ReadHeader(&bytes, &size, &packetType))
541            packetType = kInvalidMessage;
542
543        CFMutableDataRef destinationMessage;
544        if (session->_textOutput) {
545            destinationMessage = CFDataCreateMutable(kCFAllocatorDefault, 0);
546        } else {
547            destinationMessage = CFRetainSafe(negotiationResponse);
548        }
549
550        switch (packetType) {
551            case kDHMessage:
552                result = SecOTRSProcessDHMessage(session, decodedBytes, destinationMessage);
553                break;
554            case kDHKeyMessage:
555                result = SecOTRSProcessDHKeyMessage(session, decodedBytes, destinationMessage);
556                break;
557            case kRevealSignatureMessage:
558                result = SecOTRSProcessRevealSignatureMessage(session, decodedBytes, destinationMessage);
559                break;
560            case kSignatureMessage:
561                result = SecOTRSProcessSignatureMessage(session, decodedBytes, destinationMessage);
562                break;
563            default:
564                result = errSecDecode;
565                break;
566        };
567
568        if (result != errSecSuccess) {
569            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);
570        }
571
572        if (session->_textOutput) {
573            SecOTRPrepareOutgoingBytes(destinationMessage, negotiationResponse);
574        }
575        CFReleaseSafe(destinationMessage);
576        CFReleaseSafe(decodedBytes);
577    });
578
579fail:
580    return result;
581}
582