1//
2//  SecOTRDHKey.c
3//  libsecurity_libSecOTR
4//
5//  Created by Mitch Adler on 3/2/11.
6//  Copyright 2011 Apple Inc. All rights reserved.
7//
8#include "SecOTRDHKey.h"
9#include <utilities/SecCFWrappers.h>
10
11#include "SecOTRMath.h"
12#include "SecOTRPacketData.h"
13
14#include <corecrypto/ccn.h>
15#include <corecrypto/ccsha1.h>
16#include <corecrypto/ccec_priv.h>
17#include <corecrypto/ccec.h>
18
19#include <CommonCrypto/CommonRandomSPI.h>
20
21#ifdef USECOMMONCRYPTO
22#include <CommonCrypto/CommonDigest.h>
23#endif
24
25#define kECKeySize 256
26
27struct _SecOTRFullDHKey {
28    CFRuntimeBase _base;
29
30    ccec_full_ctx_decl(ccn_sizeof(kECKeySize), _key);
31    uint8_t keyHash[CCSHA1_OUTPUT_SIZE];
32
33};
34
35CFGiblisFor(SecOTRFullDHKey);
36
37static size_t AppendECPublicKeyAsDATA(CFMutableDataRef data, ccec_pub_ctx_t public_key)
38{
39    size_t size = ccec_export_pub_size(public_key);
40
41    AppendLong(data, (uint32_t)size); /* cast: no overflow, pub size always fit in 32 bits */
42    ccec_export_pub(public_key, CFDataIncreaseLengthAndGetMutableBytes(data, (CFIndex)size));
43
44    return size;
45}
46
47
48static CF_RETURNS_RETAINED CFStringRef SecOTRFullDHKeyCopyDescription(CFTypeRef cf)
49{
50    SecOTRFullDHKeyRef session = (SecOTRFullDHKeyRef)cf;
51    return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTRFullDHKeyRef: %p>"), session);
52}
53
54static void SecOTRFullDHKeyDestroy(CFTypeRef cf)
55{
56    SecOTRFullDHKeyRef fullKey = (SecOTRFullDHKeyRef)cf;
57
58    bzero(fullKey->_key, sizeof(fullKey->_key));
59}
60
61SecOTRFullDHKeyRef SecOTRFullDHKCreate(CFAllocatorRef allocator)
62{
63    SecOTRFullDHKeyRef newFDHK = CFTypeAllocate(SecOTRFullDHKey, struct _SecOTRFullDHKey, allocator);
64
65    SecFDHKNewKey(newFDHK);
66
67    return newFDHK;
68}
69
70SecOTRFullDHKeyRef SecOTRFullDHKCreateFromBytes(CFAllocatorRef allocator, const uint8_t**bytes, size_t*size)
71{
72    SecOTRFullDHKeyRef newFDHK = CFTypeAllocate(SecOTRFullDHKey, struct _SecOTRFullDHKey, allocator);
73
74    ccec_ctx_init(ccec_cp_256(), newFDHK->_key);
75
76    uint32_t publicKeySize;
77    require_noerr(ReadLong(bytes, size, &publicKeySize), fail);
78
79    require(publicKeySize <= *size, fail);
80    require_noerr(ccec_import_pub(ccec_cp_256(), publicKeySize, *bytes, newFDHK->_key), fail);
81    ccdigest(ccsha1_di(), publicKeySize, *bytes, newFDHK->keyHash);
82
83    *size -= publicKeySize;
84    *bytes += publicKeySize;
85
86    require_noerr(ReadMPI(bytes, size, ccec_ctx_n(newFDHK->_key), ccec_ctx_k(newFDHK->_key)), fail);
87
88    return newFDHK;
89
90fail:
91    CFReleaseNull(newFDHK);
92    return NULL;
93}
94
95void SecFDHKNewKey(SecOTRFullDHKeyRef fullKey)
96{
97    struct ccrng_state *rng=ccDRBGGetRngState();
98
99#if defined(CCECDH_AVAILABLE)
100    ccecdh_generate_key(ccec_cp_256(), rng, fullKey->_key);
101#else
102    ccec_generate_key(ccec_cp_256(), rng, fullKey->_key);
103#endif
104
105    size_t size = ccec_export_pub_size(fullKey->_key);
106    uint8_t publicKey[size];
107
108    ccec_export_pub(fullKey->_key, publicKey);
109    ccdigest(ccsha1_di(), size, publicKey, fullKey->keyHash);
110}
111
112void SecFDHKAppendSerialization(SecOTRFullDHKeyRef fullKey, CFMutableDataRef appendTo)
113{
114    AppendECPublicKeyAsDATA(appendTo, fullKey->_key);
115    AppendMPI(appendTo, ccec_ctx_n(fullKey->_key), ccec_ctx_k(fullKey->_key));
116}
117
118
119void SecFDHKAppendPublicSerialization(SecOTRFullDHKeyRef fullKey, CFMutableDataRef appendTo)
120{
121    if(ccec_ctx_bitlen(fullKey->_key) != kECKeySize) return;
122    AppendECPublicKeyAsDATA(appendTo, fullKey->_key);
123}
124
125
126uint8_t* SecFDHKGetHash(SecOTRFullDHKeyRef fullKey)
127{
128    return fullKey->keyHash;
129}
130
131
132
133
134//
135//
136//
137struct _SecOTRPublicDHKey {
138    CFRuntimeBase _base;
139
140    ccec_pub_ctx_decl(ccn_sizeof(kECKeySize), _key);
141    uint8_t keyHash[CCSHA1_OUTPUT_SIZE];
142
143};
144
145CFGiblisFor(SecOTRPublicDHKey);
146
147static CF_RETURNS_RETAINED CFStringRef SecOTRPublicDHKeyCopyDescription(CFTypeRef cf) {
148    SecOTRPublicDHKeyRef session = (SecOTRPublicDHKeyRef)cf;
149    return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTRPublicDHKeyRef: %p>"), session);
150}
151
152static void SecOTRPublicDHKeyDestroy(CFTypeRef cf) {
153    SecOTRPublicDHKeyRef pubKey = (SecOTRPublicDHKeyRef)cf;
154    (void) pubKey;
155}
156
157static void ccec_copy_public(ccec_pub_ctx_t source, ccec_pub_ctx_t dest)
158{
159    ccec_ctx_cp(dest) = ccec_ctx_cp(source);
160    // TODO: +1?!
161    ccn_set(3*ccec_ctx_n(source), (cc_unit*) ccec_ctx_point(dest)._p, (cc_unit*) ccec_ctx_point(source)._p);
162}
163
164SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromFullKey(CFAllocatorRef allocator, SecOTRFullDHKeyRef full)
165{
166    SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator);
167
168    ccec_copy_public(full->_key, newPDHK->_key);
169    memcpy(newPDHK->keyHash, full->keyHash, CCSHA1_OUTPUT_SIZE);
170
171    return newPDHK;
172}
173
174SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromSerialization(CFAllocatorRef allocator, const uint8_t** bytes, size_t *size)
175{
176    SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator);
177
178    uint32_t publicKeySize;
179    require_noerr(ReadLong(bytes, size, &publicKeySize), fail);
180
181    require(publicKeySize <= *size, fail);
182
183    require_noerr(ccec_import_pub(ccec_cp_256(), publicKeySize, *bytes, newPDHK->_key), fail);
184    ccdigest(ccsha1_di(), publicKeySize, *bytes, newPDHK->keyHash);
185
186    *size -= publicKeySize;
187    *bytes += publicKeySize;
188
189    return newPDHK;
190fail:
191    CFReleaseNull(newPDHK);
192    return NULL;
193}
194
195SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromBytes(CFAllocatorRef allocator, const uint8_t** bytes, size_t *size)
196{
197    SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator);
198
199    require_noerr(ccec_import_pub(ccec_cp_256(), *size, *bytes, newPDHK->_key), fail);
200    ccdigest(ccsha1_di(), *size, *bytes, newPDHK->keyHash);
201
202    return newPDHK;
203fail:
204    CFReleaseNull(newPDHK);
205    return NULL;
206}
207
208void SecPDHKAppendSerialization(SecOTRPublicDHKeyRef pubKey, CFMutableDataRef appendTo)
209{
210    AppendECPublicKeyAsDATA(appendTo, pubKey->_key);
211}
212
213
214uint8_t* SecPDHKGetHash(SecOTRPublicDHKeyRef pubKey)
215{
216    return pubKey->keyHash;
217}
218
219
220void SecPDHKeyGenerateS(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey, cc_unit* s)
221{
222    ccn_zero(kExponentiationUnits, s);
223
224    size_t keyLen = ccn_sizeof_n(kExponentiationUnits);
225    ccec_compute_key(myKey->_key, theirKey->_key, &keyLen, (uint8_t*)s);
226}
227
228static int ccec_cmp(ccec_pub_ctx_t l, ccec_pub_ctx_t r)
229{
230    size_t lsize = ccec_export_pub_size(l);
231    size_t rsize = ccec_export_pub_size(r);
232
233    int result = 0;
234
235    if (lsize == rsize) {
236        uint8_t lpub[lsize];
237        uint8_t rpub[rsize];
238
239        ccec_export_pub(l, lpub);
240        ccec_export_pub(r, rpub);
241
242        result = memcmp(lpub, rpub, lsize);
243    } else {
244        result = rsize < lsize ? -1 : 1;
245    }
246
247    return result;
248}
249
250bool SecDHKIsGreater(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey)
251{
252    return ccec_cmp(myKey->_key, theirKey->_key) > 0;
253}
254
255static void DeriveKeys(CFDataRef dataToHash,
256                       uint8_t* messageKey,
257                       uint8_t* macKey)
258{
259    if (messageKey == NULL && macKey == NULL)
260        return;
261
262    uint8_t hashedSharedKey[CCSHA1_OUTPUT_SIZE];
263
264#ifdef USECOMMONCRYPTO
265    (void) CCDigest(kCCDigestSHA1, CFDataGetBytePtr(dataToHash), (uint32_t)CFDataGetLength(dataToHash), hashedSharedKey);
266#else
267    ccdigest(ccsha1_di(), CFDataGetLength(dataToHash), CFDataGetBytePtr(dataToHash), hashedSharedKey);
268#endif
269
270    if (messageKey)
271        memcpy(messageKey, hashedSharedKey, kOTRMessageKeyBytes);
272
273    if (macKey) {
274#ifdef USECOMMONCRYPTO
275        (void) CCDigest(kCCDigestSHA1, messageKey, kOTRMessageKeyBytes, macKey);
276#else
277        ccdigest(ccsha1_di(), kOTRMessageKeyBytes, messageKey, macKey);
278#endif
279    }
280
281    bzero(hashedSharedKey, sizeof(hashedSharedKey));
282}
283
284void SecOTRDHKGenerateOTRKeys(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey,
285                           uint8_t* sendMessageKey, uint8_t* sendMacKey,
286                           uint8_t* receiveMessageKey, uint8_t* receiveMacKey)
287{
288    CFMutableDataRef dataToHash = CFDataCreateMutable(kCFAllocatorDefault, 0);
289
290    {
291        cc_unit s[kExponentiationUnits];
292
293        SecPDHKeyGenerateS(myKey, theirKey, s);
294        AppendByte(dataToHash, SecDHKIsGreater(myKey, theirKey) ? 0x01 : 0x02);
295        AppendMPI(dataToHash, kExponentiationUnits, s);
296
297        ccn_zero(kExponentiationUnits, s);
298    }
299
300    DeriveKeys(dataToHash, receiveMessageKey, receiveMacKey);
301
302    uint8_t *messageTypeByte = CFDataGetMutableBytePtr(dataToHash);
303
304    *messageTypeByte ^= 0x03; // Invert the bits since it's either 1 or 2.
305
306    DeriveKeys(dataToHash, sendMessageKey, sendMacKey);
307
308    CFReleaseNull(dataToHash);
309}
310