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