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