1//
2//  OTRMath.c
3//  libsecurity_libSecOTR
4//
5//  Created by Mitch Adler on 1/28/11.
6//  Copyright 2011 Apple Inc. All rights reserved.
7//
8
9#include "SecOTRMath.h"
10#include "SecOTRMathPrivate.h"
11
12#include "SecOTRPacketData.h"
13
14#include <AssertMacros.h>
15
16#include <pthread.h>
17
18#include <Security/SecRandom.h>
19
20#include <corecrypto/ccsha2.h>
21#include <corecrypto/cczp.h>
22
23#include <limits.h>
24
25static inline
26void cczp_init_from_bytes(cc_size n, cczp_t zp, size_t pSize, const void *p, size_t rSize, const void* r)
27{
28    CCZP_N(zp) = n;
29    zp.zp->mod_prime = cczp_mod;
30    ccn_read_uint(n, CCZP_PRIME(zp), pSize, p);
31    ccn_read_uint(n+1, CCZP_RECIP(zp), rSize, r);
32
33}
34
35static pthread_once_t   kOTRImportGroupData = PTHREAD_ONCE_INIT;
36
37static cc_unit sOTRGenerator[kOTRDHGroupUnits];
38static cczp_decl_n(kOTRDHGroupUnits, sOTRGroup_zp);
39
40static void setupGroupValues()
41{
42    __Check( kExponentiationUnits == kOTRDHGroupUnits );
43
44    cczp_init_from_bytes(kOTRDHGroupUnits, sOTRGroup_zp,
45                         sizeof(kOTRDHGroup), kOTRDHGroup,
46                         sizeof(kOTRDHGroup_Recip), kOTRDHGroup_Recip);
47
48    ccn_seti(kOTRDHGroupUnits, sOTRGenerator, kOTRGeneratorValue);
49}
50
51void OTRExponentiate(cc_unit* res, const cc_unit* base, const cc_unit* exponent)
52{
53    pthread_once(&kOTRImportGroupData, setupGroupValues);
54
55    cczp_power(sOTRGroup_zp, res, base, exponent);
56}
57
58void OTRGroupExponentiate(cc_unit* res, const cc_unit* exponent)
59{
60    pthread_once(&kOTRImportGroupData, setupGroupValues);
61
62    OTRExponentiate(res, sOTRGenerator, exponent);
63}
64
65//
66// Random Number Generation
67//
68
69OSStatus GetRandomBytesInLSBs(size_t bytesOfRandomness, size_t n, cc_unit* place)
70{
71    OSStatus result = errSecParam;
72    require(bytesOfRandomness * 8 <= ccn_bitsof_n(n), fail);
73    {
74        uint8_t randomBytes[bytesOfRandomness];
75
76        result = SecRandomCopyBytes(kSecRandomDefault, sizeof(randomBytes), randomBytes);
77
78        require_noerr(result, fail);
79
80        ccn_read_uint(n, place, sizeof(randomBytes), randomBytes);
81
82        bzero(randomBytes, bytesOfRandomness);
83    }
84fail:
85    return result;
86}
87
88OSStatus FillWithRandomBytes(size_t n, cc_unit* place)
89{
90    return GetRandomBytesInLSBs(ccn_sizeof(n), n, place);
91}
92
93
94static const uint8_t kIVZero[16] = { };
95
96static void AES_CTR_Transform(size_t keySize, const uint8_t* key,
97                       const uint8_t iv[16],
98                       size_t howMuch, const uint8_t* from, uint8_t* to)
99{
100    const struct ccmode_ctr* ctr_encrypt = ccaes_ctr_crypt_mode();
101    ccctr_ctx_decl(ctr_encrypt->size, ctr_ctx);
102    ctr_encrypt->init(ctr_encrypt, ctr_ctx, keySize, key, iv);
103
104    ctr_encrypt->ctr(ctr_ctx, howMuch, from, to);
105}
106
107void AES_CTR_HighHalf_Transform(size_t keySize, const uint8_t* key,
108                                uint64_t highHalf,
109                                size_t howMuch, const uint8_t* from, uint8_t* to)
110{
111    uint8_t iv[16] = { highHalf >> 56, highHalf >> 48, highHalf >> 40, highHalf >> 32,
112                       highHalf >> 24, highHalf >> 16, highHalf >> 8 , highHalf >> 0,
113                                    0,              0,             0,              0,
114                                    0,              0,             0,              0 };
115    AES_CTR_Transform(keySize, key, iv, howMuch, from, to);
116}
117
118void AES_CTR_IV0_Transform(size_t keySize, const uint8_t* key,
119                           size_t howMuch, const uint8_t* from, uint8_t* to)
120{
121    AES_CTR_Transform(keySize, key, kIVZero, howMuch, from, to);
122}
123
124
125//
126// Key Derivation
127//
128
129static void HashMPIWithPrefix(uint8_t byte, cc_size sN, const cc_unit* s, uint8_t* buffer)
130{
131    CFMutableDataRef dataToHash = CFDataCreateMutable(kCFAllocatorDefault, 0);
132    CFDataAppendBytes(dataToHash, &byte, 1);
133
134    AppendMPI(dataToHash, sN, s);
135
136    uint8_t *bytesToHash = CFDataGetMutableBytePtr(dataToHash);
137    CFIndex  amountToHash = CFDataGetLength(dataToHash);
138
139    /* 64 bits cast: amountToHash is the size of an identity +1 , which is currently hardcoded and never more than 2^32 bytes */
140    assert((unsigned long)amountToHash<UINT32_MAX); /* Debug check, Correct as long as CFIndex is a signed long and CC_LONG is a uint32_t */
141
142    (void) CC_SHA256(bytesToHash, (CC_LONG)amountToHash, buffer);
143
144    bzero(bytesToHash, (size_t)amountToHash);
145    CFReleaseNull(dataToHash);
146}
147
148void DeriveOTR256BitsFromS(KeyType whichKey, cc_size sN, const cc_unit* s, size_t keySize, uint8_t* key)
149{
150    HashMPIWithPrefix(whichKey, sN, s, key);
151}
152
153void DeriveOTR128BitPairFromS(KeyType whichKey, size_t sSize, const cc_unit* s,
154                              size_t firstKeySize, uint8_t* firstKey,
155                              size_t secondKeySize, uint8_t* secondKey)
156{
157    uint8_t hashBuffer[CCSHA256_OUTPUT_SIZE];
158
159    HashMPIWithPrefix(whichKey, sSize, s, hashBuffer);
160
161    if (firstKey) {
162        firstKeySize = firstKeySize > CCSHA256_OUTPUT_SIZE/2 ? CCSHA256_OUTPUT_SIZE/2 : firstKeySize;
163        memcpy(firstKey, hashBuffer, firstKeySize);
164    }
165    if (secondKey) {
166        secondKeySize = secondKeySize > CCSHA256_OUTPUT_SIZE/2 ? CCSHA256_OUTPUT_SIZE/2 : secondKeySize;
167        memcpy(secondKey, hashBuffer, secondKeySize);
168    }
169
170    bzero(hashBuffer, CCSHA256_OUTPUT_SIZE);
171
172}
173
174void DeriveOTR64BitsFromS(KeyType whichKey, size_t sn, const cc_unit* s,
175                          size_t topKeySize, uint8_t* topKey)
176{
177    uint8_t hashBuffer[CCSHA256_OUTPUT_SIZE];
178
179    HashMPIWithPrefix(whichKey, sn, s, hashBuffer);
180
181    topKeySize = topKeySize > CCSHA256_OUTPUT_SIZE/2 ? CCSHA256_OUTPUT_SIZE/2 : topKeySize;
182    memcpy(topKey, hashBuffer, topKeySize);
183
184    bzero(hashBuffer, CCSHA256_OUTPUT_SIZE);
185}
186
187