]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecOTRDHKey.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / sec / Security / SecOTRDHKey.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 #include "SecOTRDHKey.h"
25 #include <utilities/SecCFWrappers.h>
26
27 #include "SecOTRMath.h"
28 #include "SecOTRPacketData.h"
29
30 #include <corecrypto/ccn.h>
31 #include <corecrypto/ccsha1.h>
32 #include <corecrypto/ccec_priv.h>
33 #include <corecrypto/ccec.h>
34
35 #include <CommonCrypto/CommonRandomSPI.h>
36
37 #define kECKeySize 256
38
39 static void GenerateHashForKey(ccec_pub_ctx_t public_key, void *output)
40 {
41 size_t size = ccec_export_pub_size(public_key);
42
43 uint8_t pub_key_bytes_buffer[size];
44
45 ccec_export_pub(public_key, pub_key_bytes_buffer);
46
47 ccdigest(ccsha1_di(), size, pub_key_bytes_buffer, output);
48 }
49
50
51 struct _SecOTRFullDHKey {
52 CFRuntimeBase _base;
53
54 ccec_full_ctx_decl(ccn_sizeof(kECKeySize), _key);
55 uint8_t keyHash[CCSHA1_OUTPUT_SIZE];
56
57 };
58
59 CFGiblisWithCompareFor(SecOTRFullDHKey);
60
61 static size_t AppendECPublicKeyAsDATA(CFMutableDataRef data, ccec_pub_ctx_t public_key)
62 {
63 size_t size = ccec_export_pub_size(public_key);
64
65 AppendLong(data, (uint32_t)size); /* cast: no overflow, pub size always fit in 32 bits */
66 ccec_export_pub(public_key, CFDataIncreaseLengthAndGetMutableBytes(data, (CFIndex)size));
67
68 return size;
69 }
70
71 static size_t AppendECCompactPublicKey(CFMutableDataRef data, ccec_pub_ctx_t public_key)
72 {
73 size_t size = ccec_compact_export_size(false, public_key._full);
74
75 ccec_compact_export(false, CFDataIncreaseLengthAndGetMutableBytes(data, (CFIndex)size), public_key._full);
76
77 return size;
78 }
79
80 static CFStringRef CCNCopyAsHex(size_t n, cc_unit *value){
81 size_t bytes = ccn_write_uint_size(n, value);
82 uint8_t byte_array [bytes];
83 ccn_write_uint(n, value, bytes, byte_array);
84
85 __block CFStringRef description = NULL;
86
87 BufferPerformWithHexString(byte_array, sizeof(byte_array), ^(CFStringRef dataString) {
88 description = CFRetainSafe(dataString);
89 });
90 return description;
91 }
92 static void withXandY(ccec_pub_ctx_t pubKey, void (^action)(CFStringRef x, CFStringRef y)){
93 CFStringRef xString = NULL;
94 CFStringRef yString = NULL;
95 xString = CCNCopyAsHex(ccec_ctx_n(pubKey), ccec_ctx_x(pubKey));
96 yString = CCNCopyAsHex(ccec_ctx_n(pubKey), ccec_ctx_y(pubKey));
97
98 action(xString, yString);
99 CFReleaseNull(xString);
100 CFReleaseNull(yString);
101 }
102 static CFStringRef SecOTRFullDHKeyCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions)
103 {
104 SecOTRFullDHKeyRef fullDHKey = (SecOTRFullDHKeyRef)cf;
105 __block CFStringRef description = NULL;
106
107 withXandY(fullDHKey->_key, ^(CFStringRef x, CFStringRef y) {
108 BufferPerformWithHexString(fullDHKey->keyHash, sizeof(fullDHKey->keyHash), ^(CFStringRef dataString) {
109 description = CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTRFullDHKeyRef@%p: x: %@ y: %@ [%@]>"), fullDHKey, x, y, dataString);
110 });
111 });
112
113 return description;
114 }
115
116 static Boolean SecOTRFullDHKeyCompare(CFTypeRef leftCF, CFTypeRef rightCF)
117 {
118 SecOTRFullDHKeyRef left = (SecOTRFullDHKeyRef)leftCF;
119 SecOTRFullDHKeyRef right = (SecOTRFullDHKeyRef)rightCF;
120
121 return 0 == memcmp(left->keyHash, right->keyHash, sizeof(left->keyHash));
122 }
123
124 static void SecOTRFullDHKeyDestroy(CFTypeRef cf)
125 {
126 SecOTRFullDHKeyRef fullKey = (SecOTRFullDHKeyRef)cf;
127
128 bzero(fullKey->_key, sizeof(fullKey->_key));
129 }
130
131 static inline void SecOTRFDHKUpdateHash(SecOTRFullDHKeyRef fullKey)
132 {
133 GenerateHashForKey(fullKey->_key, fullKey->keyHash);
134 }
135
136 SecOTRFullDHKeyRef SecOTRFullDHKCreate(CFAllocatorRef allocator)
137 {
138 SecOTRFullDHKeyRef newFDHK = CFTypeAllocate(SecOTRFullDHKey, struct _SecOTRFullDHKey, allocator);
139
140 SecFDHKNewKey(newFDHK);
141
142 return newFDHK;
143 }
144
145 SecOTRFullDHKeyRef SecOTRFullDHKCreateFromBytes(CFAllocatorRef allocator, const uint8_t**bytes, size_t*size)
146 {
147 SecOTRFullDHKeyRef newFDHK = CFTypeAllocate(SecOTRFullDHKey, struct _SecOTRFullDHKey, allocator);
148
149 ccec_ctx_init(ccec_cp_256(), newFDHK->_key);
150
151 uint32_t publicKeySize;
152 require_noerr(ReadLong(bytes, size, &publicKeySize), fail);
153
154 require(publicKeySize <= *size, fail);
155 require_noerr(ccec_import_pub(ccec_cp_256(), publicKeySize, *bytes, newFDHK->_key), fail);
156
157 *size -= publicKeySize;
158 *bytes += publicKeySize;
159
160 require_noerr(ReadMPI(bytes, size, ccec_ctx_n(newFDHK->_key), ccec_ctx_k(newFDHK->_key)), fail);
161
162 SecOTRFDHKUpdateHash(newFDHK);
163
164 return newFDHK;
165
166 fail:
167 CFReleaseNull(newFDHK);
168 return NULL;
169 }
170
171 void SecFDHKNewKey(SecOTRFullDHKeyRef fullKey)
172 {
173 struct ccrng_state *rng=ccDRBGGetRngState();
174
175 // We need compact keys, maybe we should be using
176 // ccecdh_generate_key or ccechd_generate_compact_key, but for now ecdh are fine for compact use IFF we don't
177 // use the non-compact pub part.
178
179 ccec_compact_generate_key(ccec_cp_256(), rng, fullKey->_key);
180
181 SecOTRFDHKUpdateHash(fullKey);
182
183 }
184
185 void SecFDHKAppendSerialization(SecOTRFullDHKeyRef fullKey, CFMutableDataRef appendTo)
186 {
187 AppendECPublicKeyAsDATA(appendTo, fullKey->_key);
188 AppendMPI(appendTo, ccec_ctx_n(fullKey->_key), ccec_ctx_k(fullKey->_key));
189 }
190
191 void SecFDHKAppendPublicSerialization(SecOTRFullDHKeyRef fullKey, CFMutableDataRef appendTo)
192 {
193 if(ccec_ctx_bitlen(fullKey->_key) != kECKeySize) return;
194 AppendECPublicKeyAsDATA(appendTo, fullKey->_key);
195 }
196
197 void SecFDHKAppendCompactPublicSerialization(SecOTRFullDHKeyRef fullKey, CFMutableDataRef appendTo)
198 {
199 if(ccec_ctx_bitlen(fullKey->_key) != kECKeySize) return;
200 AppendECCompactPublicKey(appendTo, fullKey->_key);
201 }
202
203
204 uint8_t* SecFDHKGetHash(SecOTRFullDHKeyRef fullKey)
205 {
206 return fullKey->keyHash;
207 }
208
209
210
211
212 //
213 //
214 //
215 struct _SecOTRPublicDHKey {
216 CFRuntimeBase _base;
217
218 ccec_pub_ctx_decl(ccn_sizeof(kECKeySize), _key);
219 uint8_t keyHash[CCSHA1_OUTPUT_SIZE];
220 };
221
222 CFGiblisWithCompareFor(SecOTRPublicDHKey);
223
224 static CFStringRef SecOTRPublicDHKeyCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
225 SecOTRPublicDHKeyRef publicDHKey = (SecOTRPublicDHKeyRef)cf;
226
227 __block CFStringRef description = NULL;
228 withXandY(publicDHKey->_key, ^(CFStringRef x, CFStringRef y) {
229 BufferPerformWithHexString(publicDHKey->keyHash, sizeof(publicDHKey->keyHash), ^(CFStringRef dataString) {
230 description = CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTRPublicDHKeyRef@%p: x: %@ y: %@ [%@]>"), publicDHKey, x, y, dataString);
231 });
232
233 });
234 return description;
235 }
236
237 static Boolean SecOTRPublicDHKeyCompare(CFTypeRef leftCF, CFTypeRef rightCF)
238 {
239 SecOTRPublicDHKeyRef left = (SecOTRPublicDHKeyRef)leftCF;
240 SecOTRPublicDHKeyRef right = (SecOTRPublicDHKeyRef)rightCF;
241
242 return 0 == memcmp(left->keyHash, right->keyHash, sizeof(left->keyHash));
243 }
244
245 static void SecOTRPublicDHKeyDestroy(CFTypeRef cf) {
246 SecOTRPublicDHKeyRef pubKey = (SecOTRPublicDHKeyRef)cf;
247 (void) pubKey;
248 }
249
250 static inline void SecOTRPDHKUpdateHash(SecOTRPublicDHKeyRef pubKey)
251 {
252 GenerateHashForKey(pubKey->_key, pubKey->keyHash);
253 }
254
255 static void ccec_copy_public(ccec_pub_ctx_t source, ccec_pub_ctx_t dest)
256 {
257 ccec_ctx_cp(dest) = ccec_ctx_cp(source);
258 // TODO: +1?!
259 ccn_set(3*ccec_ctx_n(source), (cc_unit*) ccec_ctx_point(dest)._p, (cc_unit*) ccec_ctx_point(source)._p);
260 }
261
262 SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromFullKey(CFAllocatorRef allocator, SecOTRFullDHKeyRef full)
263 {
264 SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator);
265
266 ccec_copy_public(full->_key, newPDHK->_key);
267 memcpy(newPDHK->keyHash, full->keyHash, CCSHA1_OUTPUT_SIZE);
268
269 return newPDHK;
270 }
271
272 SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromSerialization(CFAllocatorRef allocator, const uint8_t** bytes, size_t *size)
273 {
274 size_t publicKeySize;
275 {
276 uint32_t readSize = 0;
277 require_noerr(ReadLong(bytes, size, &readSize), fail);
278 publicKeySize = readSize;
279 }
280
281 require(publicKeySize <= *size, fail);
282
283 *size -= publicKeySize;
284
285 return SecOTRPublicDHKCreateFromBytes(allocator, bytes, &publicKeySize);
286 fail:
287 return NULL;
288 }
289
290 SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromCompactSerialization(CFAllocatorRef allocator, const uint8_t** bytes, size_t *size)
291 {
292 SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator);
293
294 size_t publicKeySize = ccec_cp_prime_size(ccec_cp_256());
295
296 require_quiet(publicKeySize <= *size, fail);
297
298 require_noerr_quiet(ccec_compact_import_pub(ccec_cp_256(), publicKeySize, *bytes, newPDHK->_key), fail);
299
300 *size -= publicKeySize;
301 *bytes += publicKeySize;
302
303 SecOTRPDHKUpdateHash(newPDHK);
304
305 return newPDHK;
306 fail:
307 CFReleaseNull(newPDHK);
308 return NULL;
309 }
310
311
312 SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromBytes(CFAllocatorRef allocator, const uint8_t** bytes, size_t *size)
313 {
314 SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator);
315
316 require_noerr(ccec_import_pub(ccec_cp_256(), *size, *bytes, newPDHK->_key), fail);
317
318 *bytes += *size;
319 *size = 0;
320
321 SecOTRPDHKUpdateHash(newPDHK);
322
323 return newPDHK;
324 fail:
325 CFReleaseNull(newPDHK);
326 return NULL;
327 }
328
329
330 void SecPDHKAppendSerialization(SecOTRPublicDHKeyRef pubKey, CFMutableDataRef appendTo)
331 {
332 AppendECPublicKeyAsDATA(appendTo, pubKey->_key);
333 }
334
335 void SecPDHKAppendCompactSerialization(SecOTRPublicDHKeyRef pubKey, CFMutableDataRef appendTo)
336 {
337 AppendECPublicKeyAsDATA(appendTo, pubKey->_key);
338 }
339
340
341 uint8_t* SecPDHKGetHash(SecOTRPublicDHKeyRef pubKey)
342 {
343 return pubKey->keyHash;
344 }
345
346
347 void SecPDHKeyGenerateS(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey, cc_unit* s)
348 {
349 ccn_zero(kExponentiationUnits, s);
350
351 size_t keyLen = ccn_sizeof_n(kExponentiationUnits);
352 ccec_compute_key(myKey->_key, theirKey->_key, &keyLen, (uint8_t*)s);
353 }
354
355 static int ccec_cmp(ccec_pub_ctx_t l, ccec_pub_ctx_t r)
356 {
357 size_t lsize = ccec_export_pub_size(l);
358 size_t rsize = ccec_export_pub_size(r);
359
360 int result = 0;
361
362 if (lsize == rsize) {
363 uint8_t lpub[lsize];
364 uint8_t rpub[rsize];
365
366 ccec_export_pub(l, lpub);
367 ccec_export_pub(r, rpub);
368
369 result = memcmp(lpub, rpub, lsize);
370 } else {
371 result = rsize < lsize ? -1 : 1;
372 }
373
374 return result;
375 }
376
377 bool SecDHKIsGreater(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey)
378 {
379 return ccec_cmp(myKey->_key, theirKey->_key) > 0;
380 }
381
382 static void DeriveKeys(CFDataRef dataToHash,
383 uint8_t* messageKey,
384 uint8_t* macKey)
385 {
386 if (messageKey == NULL && macKey == NULL)
387 return;
388
389 uint8_t hashedSharedKey[CCSHA1_OUTPUT_SIZE];
390
391 ccdigest(ccsha1_di(), CFDataGetLength(dataToHash), CFDataGetBytePtr(dataToHash), hashedSharedKey);
392
393 if (messageKey)
394 memcpy(messageKey, hashedSharedKey, kOTRMessageKeyBytes);
395
396 if (macKey) {
397 ccdigest(ccsha1_di(), kOTRMessageKeyBytes, messageKey, macKey);
398 }
399
400 bzero(hashedSharedKey, sizeof(hashedSharedKey));
401 }
402
403 void SecOTRDHKGenerateOTRKeys(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey,
404 uint8_t* sendMessageKey, uint8_t* sendMacKey,
405 uint8_t* receiveMessageKey, uint8_t* receiveMacKey)
406 {
407 CFMutableDataRef dataToHash = CFDataCreateMutable(kCFAllocatorDefault, 0);
408
409 {
410 cc_unit s[kExponentiationUnits];
411
412 SecPDHKeyGenerateS(myKey, theirKey, s);
413 AppendByte(dataToHash, SecDHKIsGreater(myKey, theirKey) ? 0x01 : 0x02);
414 AppendMPI(dataToHash, kExponentiationUnits, s);
415
416 ccn_zero(kExponentiationUnits, s);
417 }
418
419 DeriveKeys(dataToHash, receiveMessageKey, receiveMacKey);
420
421 uint8_t *messageTypeByte = CFDataGetMutableBytePtr(dataToHash);
422
423 *messageTypeByte ^= 0x03; // Invert the bits since it's either 1 or 2.
424
425 DeriveKeys(dataToHash, sendMessageKey, sendMacKey);
426
427 CFReleaseNull(dataToHash);
428 }