]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecOTRDHKey.c
Security-58286.41.2.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);
74
75 ccec_compact_export(false, CFDataIncreaseLengthAndGetMutableBytes(data, (CFIndex)size), (ccec_full_ctx_t)public_key);
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(ccec_ctx_pub(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(ccec_ctx_pub(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, ccec_ctx_pub(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, ccec_ctx_pub(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, ccec_ctx_pub(fullKey->_key));
195 }
196
197 void SecFDHKAppendCompactPublicSerialization(SecOTRFullDHKeyRef fullKey, CFMutableDataRef appendTo)
198 {
199 if(ccec_ctx_bitlen(fullKey->_key) != kECKeySize) return;
200 AppendECCompactPublicKey(appendTo, ccec_ctx_pub(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 cc_size sourceKeyN = ccec_ctx_n(source);
258 memcpy(dest, source, ccec_pub_ctx_size(ccn_sizeof_n(sourceKeyN)));
259 }
260
261 SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromFullKey(CFAllocatorRef allocator, SecOTRFullDHKeyRef full)
262 {
263 SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator);
264
265 ccec_copy_public(ccec_ctx_pub(full->_key), newPDHK->_key);
266 memcpy(newPDHK->keyHash, full->keyHash, CCSHA1_OUTPUT_SIZE);
267
268 return newPDHK;
269 }
270
271 SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromSerialization(CFAllocatorRef allocator, const uint8_t** bytes, size_t *size)
272 {
273 size_t publicKeySize;
274 {
275 uint32_t readSize = 0;
276 require_noerr(ReadLong(bytes, size, &readSize), fail);
277 publicKeySize = readSize;
278 }
279
280 require(publicKeySize <= *size, fail);
281
282 *size -= publicKeySize;
283
284 return SecOTRPublicDHKCreateFromBytes(allocator, bytes, &publicKeySize);
285 fail:
286 return NULL;
287 }
288
289 SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromCompactSerialization(CFAllocatorRef allocator, const uint8_t** bytes, size_t *size)
290 {
291 SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator);
292
293 size_t publicKeySize = ccec_cp_prime_size(ccec_cp_256());
294
295 require_quiet(publicKeySize <= *size, fail);
296
297 require_noerr_quiet(ccec_compact_import_pub(ccec_cp_256(), publicKeySize, *bytes, newPDHK->_key), fail);
298
299 *size -= publicKeySize;
300 *bytes += publicKeySize;
301
302 SecOTRPDHKUpdateHash(newPDHK);
303
304 return newPDHK;
305 fail:
306 CFReleaseNull(newPDHK);
307 return NULL;
308 }
309
310
311 SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromBytes(CFAllocatorRef allocator, const uint8_t** bytes, size_t *size)
312 {
313 SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator);
314
315 require_noerr(ccec_import_pub(ccec_cp_256(), *size, *bytes, newPDHK->_key), fail);
316
317 *bytes += *size;
318 *size = 0;
319
320 SecOTRPDHKUpdateHash(newPDHK);
321
322 return newPDHK;
323 fail:
324 CFReleaseNull(newPDHK);
325 return NULL;
326 }
327
328
329 void SecPDHKAppendSerialization(SecOTRPublicDHKeyRef pubKey, CFMutableDataRef appendTo)
330 {
331 AppendECPublicKeyAsDATA(appendTo, pubKey->_key);
332 }
333
334 void SecPDHKAppendCompactSerialization(SecOTRPublicDHKeyRef pubKey, CFMutableDataRef appendTo)
335 {
336 AppendECPublicKeyAsDATA(appendTo, pubKey->_key);
337 }
338
339
340 uint8_t* SecPDHKGetHash(SecOTRPublicDHKeyRef pubKey)
341 {
342 return pubKey->keyHash;
343 }
344
345
346 void SecPDHKeyGenerateS(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey, cc_unit* s)
347 {
348 ccn_zero(kExponentiationUnits, s);
349
350 size_t keyLen = ccn_sizeof_n(kExponentiationUnits);
351 ccec_compute_key(myKey->_key, theirKey->_key, &keyLen, (uint8_t*)s);
352 }
353
354 static int ccec_cmp(ccec_pub_ctx_t l, ccec_pub_ctx_t r)
355 {
356 size_t lsize = ccec_export_pub_size(l);
357 size_t rsize = ccec_export_pub_size(r);
358
359 int result = 0;
360
361 if (lsize == rsize) {
362 uint8_t lpub[lsize];
363 uint8_t rpub[rsize];
364
365 ccec_export_pub(l, lpub);
366 ccec_export_pub(r, rpub);
367
368 result = memcmp(lpub, rpub, lsize);
369 } else {
370 result = rsize < lsize ? -1 : 1;
371 }
372
373 return result;
374 }
375
376 bool SecDHKIsGreater(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey)
377 {
378 return ccec_cmp(ccec_ctx_pub(myKey->_key), theirKey->_key) > 0;
379 }
380
381 static void DeriveKeys(CFDataRef dataToHash,
382 uint8_t* messageKey,
383 uint8_t* macKey)
384 {
385 if (messageKey == NULL && macKey == NULL)
386 return;
387
388 uint8_t hashedSharedKey[CCSHA1_OUTPUT_SIZE];
389
390 ccdigest(ccsha1_di(), CFDataGetLength(dataToHash), CFDataGetBytePtr(dataToHash), hashedSharedKey);
391
392 if (messageKey)
393 memcpy(messageKey, hashedSharedKey, kOTRMessageKeyBytes);
394
395 if (macKey) {
396 ccdigest(ccsha1_di(), kOTRMessageKeyBytes, messageKey, macKey);
397 }
398
399 bzero(hashedSharedKey, sizeof(hashedSharedKey));
400 }
401
402 void SecOTRDHKGenerateOTRKeys(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey,
403 uint8_t* sendMessageKey, uint8_t* sendMacKey,
404 uint8_t* receiveMessageKey, uint8_t* receiveMacKey)
405 {
406 CFMutableDataRef dataToHash = CFDataCreateMutable(kCFAllocatorDefault, 0);
407
408 {
409 cc_unit s[kExponentiationUnits];
410
411 SecPDHKeyGenerateS(myKey, theirKey, s);
412 AppendByte(dataToHash, SecDHKIsGreater(myKey, theirKey) ? 0x01 : 0x02);
413 AppendMPI(dataToHash, kExponentiationUnits, s);
414
415 ccn_zero(kExponentiationUnits, s);
416 }
417
418 DeriveKeys(dataToHash, receiveMessageKey, receiveMacKey);
419
420 uint8_t *messageTypeByte = CFDataGetMutableBytePtr(dataToHash);
421
422 *messageTypeByte ^= 0x03; // Invert the bits since it's either 1 or 2.
423
424 DeriveKeys(dataToHash, sendMessageKey, sendMacKey);
425
426 CFReleaseNull(dataToHash);
427 }