2 * Copyright (c) 2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "SecOTRDHKey.h"
25 #include <utilities/SecCFWrappers.h>
27 #include "SecOTRMath.h"
28 #include "SecOTRPacketData.h"
30 #include <corecrypto/ccn.h>
31 #include <corecrypto/ccsha1.h>
32 #include <corecrypto/ccec_priv.h>
33 #include <corecrypto/ccec.h>
35 #include <CommonCrypto/CommonRandomSPI.h>
37 #define kECKeySize 256
39 static OSStatus
GenerateHashForKey(ccec_pub_ctx_t public_key
, void *output
)
41 size_t size
= ccec_export_pub_size(public_key
);
43 uint8_t *pub_key_bytes_buffer
= malloc(size
);
44 if (pub_key_bytes_buffer
== NULL
) {
45 return errSecMemoryError
;
48 ccec_export_pub(public_key
, pub_key_bytes_buffer
);
50 ccdigest(ccsha1_di(), size
, pub_key_bytes_buffer
, output
);
52 free(pub_key_bytes_buffer
);
58 struct _SecOTRFullDHKey
{
61 ccec_full_ctx_decl(ccn_sizeof(kECKeySize
), _key
);
62 uint8_t keyHash
[CCSHA1_OUTPUT_SIZE
];
66 CFGiblisWithCompareFor(SecOTRFullDHKey
);
68 static size_t AppendECPublicKeyAsDATA(CFMutableDataRef data
, ccec_pub_ctx_t public_key
)
70 size_t size
= ccec_export_pub_size(public_key
);
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
));
78 static size_t AppendECCompactPublicKey(CFMutableDataRef data
, ccec_pub_ctx_t public_key
)
80 size_t size
= ccec_compact_export_size(false, public_key
);
82 ccec_compact_export(false, CFDataIncreaseLengthAndGetMutableBytes(data
, (CFIndex
)size
), (ccec_full_ctx_t
)public_key
);
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
);
92 __block CFStringRef description
= NULL
;
94 BufferPerformWithHexString(byte_array
, sizeof(byte_array
), ^(CFStringRef dataString
) {
95 description
= CFRetainSafe(dataString
);
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
));
105 action(xString
, yString
);
106 CFReleaseNull(xString
);
107 CFReleaseNull(yString
);
109 static CFStringRef
SecOTRFullDHKeyCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
)
111 SecOTRFullDHKeyRef fullDHKey
= (SecOTRFullDHKeyRef
)cf
;
112 __block CFStringRef description
= NULL
;
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
);
123 static Boolean
SecOTRFullDHKeyCompare(CFTypeRef leftCF
, CFTypeRef rightCF
)
125 SecOTRFullDHKeyRef left
= (SecOTRFullDHKeyRef
)leftCF
;
126 SecOTRFullDHKeyRef right
= (SecOTRFullDHKeyRef
)rightCF
;
128 return 0 == memcmp(left
->keyHash
, right
->keyHash
, sizeof(left
->keyHash
));
131 static void SecOTRFullDHKeyDestroy(CFTypeRef cf
)
133 SecOTRFullDHKeyRef fullKey
= (SecOTRFullDHKeyRef
)cf
;
135 bzero(fullKey
->_key
, sizeof(fullKey
->_key
));
138 static inline OSStatus
SecOTRFDHKUpdateHash(SecOTRFullDHKeyRef fullKey
)
140 return GenerateHashForKey(ccec_ctx_pub(fullKey
->_key
), fullKey
->keyHash
);
143 SecOTRFullDHKeyRef
SecOTRFullDHKCreate(CFAllocatorRef allocator
)
145 SecOTRFullDHKeyRef newFDHK
= CFTypeAllocate(SecOTRFullDHKey
, struct _SecOTRFullDHKey
, allocator
);
147 SecFDHKNewKey(newFDHK
);
152 SecOTRFullDHKeyRef
SecOTRFullDHKCreateFromBytes(CFAllocatorRef allocator
, const uint8_t**bytes
, size_t*size
)
154 SecOTRFullDHKeyRef newFDHK
= CFTypeAllocate(SecOTRFullDHKey
, struct _SecOTRFullDHKey
, allocator
);
156 ccec_ctx_init(ccec_cp_256(), newFDHK
->_key
);
158 uint32_t publicKeySize
;
159 require_noerr(ReadLong(bytes
, size
, &publicKeySize
), fail
);
161 require(publicKeySize
<= *size
, fail
);
162 require_noerr(ccec_import_pub(ccec_cp_256(), publicKeySize
, *bytes
, ccec_ctx_pub(newFDHK
->_key
)), fail
);
164 *size
-= publicKeySize
;
165 *bytes
+= publicKeySize
;
167 require_noerr(ReadMPI(bytes
, size
, ccec_ctx_n(newFDHK
->_key
), ccec_ctx_k(newFDHK
->_key
)), fail
);
169 require_noerr(SecOTRFDHKUpdateHash(newFDHK
), fail
);
174 CFReleaseNull(newFDHK
);
178 OSStatus
SecFDHKNewKey(SecOTRFullDHKeyRef fullKey
)
180 struct ccrng_state
*rng
=ccDRBGGetRngState();
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.
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.
189 ccec_compact_generate_key(ccec_cp_256(), rng
, fullKey
->_key
);
191 return SecOTRFDHKUpdateHash(fullKey
);
194 void SecFDHKAppendSerialization(SecOTRFullDHKeyRef fullKey
, CFMutableDataRef appendTo
)
196 AppendECPublicKeyAsDATA(appendTo
, ccec_ctx_pub(fullKey
->_key
));
197 AppendMPI(appendTo
, ccec_ctx_n(fullKey
->_key
), ccec_ctx_k(fullKey
->_key
));
200 void SecFDHKAppendPublicSerialization(SecOTRFullDHKeyRef fullKey
, CFMutableDataRef appendTo
)
202 if(ccec_ctx_bitlen(fullKey
->_key
) != kECKeySize
) return;
203 AppendECPublicKeyAsDATA(appendTo
, ccec_ctx_pub(fullKey
->_key
));
206 void SecFDHKAppendCompactPublicSerialization(SecOTRFullDHKeyRef fullKey
, CFMutableDataRef appendTo
)
208 if(ccec_ctx_bitlen(fullKey
->_key
) != kECKeySize
) return;
209 AppendECCompactPublicKey(appendTo
, ccec_ctx_pub(fullKey
->_key
));
213 uint8_t* SecFDHKGetHash(SecOTRFullDHKeyRef fullKey
)
215 return fullKey
->keyHash
;
224 struct _SecOTRPublicDHKey
{
227 ccec_pub_ctx_decl(ccn_sizeof(kECKeySize
), _key
);
228 uint8_t keyHash
[CCSHA1_OUTPUT_SIZE
];
231 CFGiblisWithCompareFor(SecOTRPublicDHKey
);
233 static CFStringRef
SecOTRPublicDHKeyCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
234 SecOTRPublicDHKeyRef publicDHKey
= (SecOTRPublicDHKeyRef
)cf
;
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
);
246 static Boolean
SecOTRPublicDHKeyCompare(CFTypeRef leftCF
, CFTypeRef rightCF
)
248 SecOTRPublicDHKeyRef left
= (SecOTRPublicDHKeyRef
)leftCF
;
249 SecOTRPublicDHKeyRef right
= (SecOTRPublicDHKeyRef
)rightCF
;
251 return 0 == memcmp(left
->keyHash
, right
->keyHash
, sizeof(left
->keyHash
));
254 static void SecOTRPublicDHKeyDestroy(CFTypeRef cf
) {
255 SecOTRPublicDHKeyRef pubKey
= (SecOTRPublicDHKeyRef
)cf
;
259 static inline OSStatus
SecOTRPDHKUpdateHash(SecOTRPublicDHKeyRef pubKey
)
261 return GenerateHashForKey(pubKey
->_key
, pubKey
->keyHash
);
264 static void ccec_copy_public(ccec_pub_ctx_t source
, ccec_pub_ctx_t dest
)
266 cc_size sourceKeyN
= ccec_ctx_n(source
);
267 memcpy(dest
, source
, ccec_pub_ctx_size(ccn_sizeof_n(sourceKeyN
)));
270 SecOTRPublicDHKeyRef
SecOTRPublicDHKCreateFromFullKey(CFAllocatorRef allocator
, SecOTRFullDHKeyRef full
)
272 SecOTRPublicDHKeyRef newPDHK
= CFTypeAllocate(SecOTRPublicDHKey
, struct _SecOTRPublicDHKey
, allocator
);
274 ccec_copy_public(ccec_ctx_pub(full
->_key
), newPDHK
->_key
);
275 memcpy(newPDHK
->keyHash
, full
->keyHash
, CCSHA1_OUTPUT_SIZE
);
280 SecOTRPublicDHKeyRef
SecOTRPublicDHKCreateFromSerialization(CFAllocatorRef allocator
, const uint8_t** bytes
, size_t *size
)
282 size_t publicKeySize
;
284 uint32_t readSize
= 0;
285 require_noerr(ReadLong(bytes
, size
, &readSize
), fail
);
286 publicKeySize
= readSize
;
289 require(publicKeySize
<= *size
, fail
);
291 *size
-= publicKeySize
;
293 return SecOTRPublicDHKCreateFromBytes(allocator
, bytes
, &publicKeySize
);
298 SecOTRPublicDHKeyRef
SecOTRPublicDHKCreateFromCompactSerialization(CFAllocatorRef allocator
, const uint8_t** bytes
, size_t *size
)
300 SecOTRPublicDHKeyRef newPDHK
= CFTypeAllocate(SecOTRPublicDHKey
, struct _SecOTRPublicDHKey
, allocator
);
302 size_t publicKeySize
= ccec_cp_prime_size(ccec_cp_256());
304 require_quiet(publicKeySize
<= *size
, fail
);
306 require_noerr_quiet(ccec_compact_import_pub(ccec_cp_256(), publicKeySize
, *bytes
, newPDHK
->_key
), fail
);
308 *size
-= publicKeySize
;
309 *bytes
+= publicKeySize
;
311 require_noerr(SecOTRPDHKUpdateHash(newPDHK
), fail
);
315 CFReleaseNull(newPDHK
);
320 SecOTRPublicDHKeyRef
SecOTRPublicDHKCreateFromBytes(CFAllocatorRef allocator
, const uint8_t** bytes
, size_t *size
)
322 SecOTRPublicDHKeyRef newPDHK
= CFTypeAllocate(SecOTRPublicDHKey
, struct _SecOTRPublicDHKey
, allocator
);
324 require_noerr(ccec_import_pub(ccec_cp_256(), *size
, *bytes
, newPDHK
->_key
), fail
);
329 require_noerr(SecOTRPDHKUpdateHash(newPDHK
), fail
);
333 CFReleaseNull(newPDHK
);
338 void SecPDHKAppendSerialization(SecOTRPublicDHKeyRef pubKey
, CFMutableDataRef appendTo
)
340 AppendECPublicKeyAsDATA(appendTo
, pubKey
->_key
);
343 void SecPDHKAppendCompactSerialization(SecOTRPublicDHKeyRef pubKey
, CFMutableDataRef appendTo
)
345 AppendECPublicKeyAsDATA(appendTo
, pubKey
->_key
);
349 uint8_t* SecPDHKGetHash(SecOTRPublicDHKeyRef pubKey
)
351 return pubKey
->keyHash
;
355 void SecPDHKeyGenerateS(SecOTRFullDHKeyRef myKey
, SecOTRPublicDHKeyRef theirKey
, cc_unit
* s
)
357 ccn_zero(kExponentiationUnits
, s
);
359 size_t keyLen
= ccn_sizeof_n(kExponentiationUnits
);
360 ccec_compute_key(myKey
->_key
, theirKey
->_key
, &keyLen
, (uint8_t*)s
);
363 static int ccec_cmp(ccec_pub_ctx_t l
, ccec_pub_ctx_t r
)
365 size_t lsize
= ccec_export_pub_size(l
);
366 size_t rsize
= ccec_export_pub_size(r
);
370 if (lsize
== rsize
) {
371 // Keys should never be larger than 256.
372 // But if they are, we want to draw attention to it.
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.");
382 ccec_export_pub(l
, lpub
);
383 ccec_export_pub(r
, rpub
);
385 result
= memcmp(lpub
, rpub
, lsize
);
387 result
= rsize
< lsize
? -1 : 1;
393 bool SecDHKIsGreater(SecOTRFullDHKeyRef myKey
, SecOTRPublicDHKeyRef theirKey
)
395 return ccec_cmp(ccec_ctx_pub(myKey
->_key
), theirKey
->_key
) > 0;
398 static void DeriveKeys(CFDataRef dataToHash
,
402 if (messageKey
== NULL
&& macKey
== NULL
)
405 uint8_t hashedSharedKey
[CCSHA1_OUTPUT_SIZE
];
407 ccdigest(ccsha1_di(), CFDataGetLength(dataToHash
), CFDataGetBytePtr(dataToHash
), hashedSharedKey
);
410 memcpy(messageKey
, hashedSharedKey
, kOTRMessageKeyBytes
);
413 ccdigest(ccsha1_di(), kOTRMessageKeyBytes
, messageKey
, macKey
);
416 bzero(hashedSharedKey
, sizeof(hashedSharedKey
));
419 void SecOTRDHKGenerateOTRKeys(SecOTRFullDHKeyRef myKey
, SecOTRPublicDHKeyRef theirKey
,
420 uint8_t* sendMessageKey
, uint8_t* sendMacKey
,
421 uint8_t* receiveMessageKey
, uint8_t* receiveMacKey
)
423 CFMutableDataRef dataToHash
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
426 cc_unit s
[kExponentiationUnits
];
428 SecPDHKeyGenerateS(myKey
, theirKey
, s
);
429 AppendByte(dataToHash
, SecDHKIsGreater(myKey
, theirKey
) ? 0x01 : 0x02);
430 AppendMPI(dataToHash
, kExponentiationUnits
, s
);
432 ccn_zero(kExponentiationUnits
, s
);
435 DeriveKeys(dataToHash
, receiveMessageKey
, receiveMacKey
);
437 uint8_t *messageTypeByte
= CFDataGetMutableBytePtr(dataToHash
);
439 *messageTypeByte
^= 0x03; // Invert the bits since it's either 1 or 2.
441 DeriveKeys(dataToHash
, sendMessageKey
, sendMacKey
);
443 CFReleaseNull(dataToHash
);