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>
34 #include <corecrypto/ccrng.h>
36 #define kECKeySize 256
38 static OSStatus
GenerateHashForKey(ccec_pub_ctx_t public_key
, void *output
)
40 size_t size
= ccec_export_pub_size(public_key
);
42 uint8_t *pub_key_bytes_buffer
= malloc(size
);
43 if (pub_key_bytes_buffer
== NULL
) {
44 return errSecMemoryError
;
47 ccec_export_pub(public_key
, pub_key_bytes_buffer
);
49 ccdigest(ccsha1_di(), size
, pub_key_bytes_buffer
, output
);
51 free(pub_key_bytes_buffer
);
57 struct _SecOTRFullDHKey
{
60 ccec_full_ctx_decl(ccn_sizeof(kECKeySize
), _key
);
61 uint8_t keyHash
[CCSHA1_OUTPUT_SIZE
];
65 CFGiblisWithCompareFor(SecOTRFullDHKey
);
67 static size_t AppendECPublicKeyAsDATA(CFMutableDataRef data
, ccec_pub_ctx_t public_key
)
69 size_t size
= ccec_export_pub_size(public_key
);
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
));
77 static size_t AppendECCompactPublicKey(CFMutableDataRef data
, ccec_pub_ctx_t public_key
)
79 size_t size
= ccec_compact_export_size(false, public_key
);
81 ccec_compact_export(false, CFDataIncreaseLengthAndGetMutableBytes(data
, (CFIndex
)size
), (ccec_full_ctx_t
)public_key
);
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
);
91 __block CFStringRef description
= NULL
;
93 BufferPerformWithHexString(byte_array
, sizeof(byte_array
), ^(CFStringRef dataString
) {
94 description
= CFRetainSafe(dataString
);
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
));
104 action(xString
, yString
);
105 CFReleaseNull(xString
);
106 CFReleaseNull(yString
);
108 static CFStringRef
SecOTRFullDHKeyCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
)
110 SecOTRFullDHKeyRef fullDHKey
= (SecOTRFullDHKeyRef
)cf
;
111 __block CFStringRef description
= NULL
;
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
);
122 static Boolean
SecOTRFullDHKeyCompare(CFTypeRef leftCF
, CFTypeRef rightCF
)
124 SecOTRFullDHKeyRef left
= (SecOTRFullDHKeyRef
)leftCF
;
125 SecOTRFullDHKeyRef right
= (SecOTRFullDHKeyRef
)rightCF
;
127 return 0 == memcmp(left
->keyHash
, right
->keyHash
, sizeof(left
->keyHash
));
130 static void SecOTRFullDHKeyDestroy(CFTypeRef cf
)
132 SecOTRFullDHKeyRef fullKey
= (SecOTRFullDHKeyRef
)cf
;
134 bzero(fullKey
->_key
, sizeof(fullKey
->_key
));
137 static inline OSStatus
SecOTRFDHKUpdateHash(SecOTRFullDHKeyRef fullKey
)
139 return GenerateHashForKey(ccec_ctx_pub(fullKey
->_key
), fullKey
->keyHash
);
142 SecOTRFullDHKeyRef
SecOTRFullDHKCreate(CFAllocatorRef allocator
)
144 SecOTRFullDHKeyRef newFDHK
= CFTypeAllocate(SecOTRFullDHKey
, struct _SecOTRFullDHKey
, allocator
);
146 SecFDHKNewKey(newFDHK
);
151 SecOTRFullDHKeyRef
SecOTRFullDHKCreateFromBytes(CFAllocatorRef allocator
, const uint8_t**bytes
, size_t*size
)
153 SecOTRFullDHKeyRef newFDHK
= CFTypeAllocate(SecOTRFullDHKey
, struct _SecOTRFullDHKey
, allocator
);
155 ccec_ctx_init(ccec_cp_256(), newFDHK
->_key
);
157 uint32_t publicKeySize
;
158 require_noerr(ReadLong(bytes
, size
, &publicKeySize
), fail
);
160 require(publicKeySize
<= *size
, fail
);
161 require_noerr(ccec_import_pub(ccec_cp_256(), publicKeySize
, *bytes
, ccec_ctx_pub(newFDHK
->_key
)), fail
);
163 *size
-= publicKeySize
;
164 *bytes
+= publicKeySize
;
166 require_noerr(ReadMPI(bytes
, size
, ccec_ctx_n(newFDHK
->_key
), ccec_ctx_k(newFDHK
->_key
)), fail
);
168 require_noerr(SecOTRFDHKUpdateHash(newFDHK
), fail
);
173 CFReleaseNull(newFDHK
);
177 OSStatus
SecFDHKNewKey(SecOTRFullDHKeyRef fullKey
)
179 struct ccrng_state
*rng
=ccrng(NULL
);
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.
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.
188 ccec_compact_generate_key(ccec_cp_256(), rng
, fullKey
->_key
);
190 return SecOTRFDHKUpdateHash(fullKey
);
193 void SecFDHKAppendSerialization(SecOTRFullDHKeyRef fullKey
, CFMutableDataRef appendTo
)
195 AppendECPublicKeyAsDATA(appendTo
, ccec_ctx_pub(fullKey
->_key
));
196 AppendMPI(appendTo
, ccec_ctx_n(fullKey
->_key
), ccec_ctx_k(fullKey
->_key
));
199 void SecFDHKAppendPublicSerialization(SecOTRFullDHKeyRef fullKey
, CFMutableDataRef appendTo
)
201 if(ccec_ctx_bitlen(fullKey
->_key
) != kECKeySize
) return;
202 AppendECPublicKeyAsDATA(appendTo
, ccec_ctx_pub(fullKey
->_key
));
205 void SecFDHKAppendCompactPublicSerialization(SecOTRFullDHKeyRef fullKey
, CFMutableDataRef appendTo
)
207 if(ccec_ctx_bitlen(fullKey
->_key
) != kECKeySize
) return;
208 AppendECCompactPublicKey(appendTo
, ccec_ctx_pub(fullKey
->_key
));
212 uint8_t* SecFDHKGetHash(SecOTRFullDHKeyRef fullKey
)
214 return fullKey
->keyHash
;
223 struct _SecOTRPublicDHKey
{
226 ccec_pub_ctx_decl(ccn_sizeof(kECKeySize
), _key
);
227 uint8_t keyHash
[CCSHA1_OUTPUT_SIZE
];
230 CFGiblisWithCompareFor(SecOTRPublicDHKey
);
232 static CFStringRef
SecOTRPublicDHKeyCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
233 SecOTRPublicDHKeyRef publicDHKey
= (SecOTRPublicDHKeyRef
)cf
;
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
);
245 static Boolean
SecOTRPublicDHKeyCompare(CFTypeRef leftCF
, CFTypeRef rightCF
)
247 SecOTRPublicDHKeyRef left
= (SecOTRPublicDHKeyRef
)leftCF
;
248 SecOTRPublicDHKeyRef right
= (SecOTRPublicDHKeyRef
)rightCF
;
250 return 0 == memcmp(left
->keyHash
, right
->keyHash
, sizeof(left
->keyHash
));
253 static void SecOTRPublicDHKeyDestroy(CFTypeRef cf
) {
254 SecOTRPublicDHKeyRef pubKey
= (SecOTRPublicDHKeyRef
)cf
;
258 static inline OSStatus
SecOTRPDHKUpdateHash(SecOTRPublicDHKeyRef pubKey
)
260 return GenerateHashForKey(pubKey
->_key
, pubKey
->keyHash
);
263 static void ccec_copy_public(ccec_pub_ctx_t source
, ccec_pub_ctx_t dest
)
265 cc_size sourceKeyN
= ccec_ctx_n(source
);
266 memcpy(dest
, source
, ccec_pub_ctx_size(ccn_sizeof_n(sourceKeyN
)));
269 SecOTRPublicDHKeyRef
SecOTRPublicDHKCreateFromFullKey(CFAllocatorRef allocator
, SecOTRFullDHKeyRef full
)
271 SecOTRPublicDHKeyRef newPDHK
= CFTypeAllocate(SecOTRPublicDHKey
, struct _SecOTRPublicDHKey
, allocator
);
273 ccec_copy_public(ccec_ctx_pub(full
->_key
), newPDHK
->_key
);
274 memcpy(newPDHK
->keyHash
, full
->keyHash
, CCSHA1_OUTPUT_SIZE
);
279 SecOTRPublicDHKeyRef
SecOTRPublicDHKCreateFromSerialization(CFAllocatorRef allocator
, const uint8_t** bytes
, size_t *size
)
281 size_t publicKeySize
;
283 uint32_t readSize
= 0;
284 require_noerr(ReadLong(bytes
, size
, &readSize
), fail
);
285 publicKeySize
= readSize
;
288 require(publicKeySize
<= *size
, fail
);
290 *size
-= publicKeySize
;
292 return SecOTRPublicDHKCreateFromBytes(allocator
, bytes
, &publicKeySize
);
297 SecOTRPublicDHKeyRef
SecOTRPublicDHKCreateFromCompactSerialization(CFAllocatorRef allocator
, const uint8_t** bytes
, size_t *size
)
299 SecOTRPublicDHKeyRef newPDHK
= CFTypeAllocate(SecOTRPublicDHKey
, struct _SecOTRPublicDHKey
, allocator
);
301 size_t publicKeySize
= ccec_cp_prime_size(ccec_cp_256());
303 require_quiet(publicKeySize
<= *size
, fail
);
305 require_noerr_quiet(ccec_compact_import_pub(ccec_cp_256(), publicKeySize
, *bytes
, newPDHK
->_key
), fail
);
307 *size
-= publicKeySize
;
308 *bytes
+= publicKeySize
;
310 require_noerr(SecOTRPDHKUpdateHash(newPDHK
), fail
);
314 CFReleaseNull(newPDHK
);
319 SecOTRPublicDHKeyRef
SecOTRPublicDHKCreateFromBytes(CFAllocatorRef allocator
, const uint8_t** bytes
, size_t *size
)
321 SecOTRPublicDHKeyRef newPDHK
= CFTypeAllocate(SecOTRPublicDHKey
, struct _SecOTRPublicDHKey
, allocator
);
323 require_noerr(ccec_import_pub(ccec_cp_256(), *size
, *bytes
, newPDHK
->_key
), fail
);
328 require_noerr(SecOTRPDHKUpdateHash(newPDHK
), fail
);
332 CFReleaseNull(newPDHK
);
337 void SecPDHKAppendSerialization(SecOTRPublicDHKeyRef pubKey
, CFMutableDataRef appendTo
)
339 AppendECPublicKeyAsDATA(appendTo
, pubKey
->_key
);
342 void SecPDHKAppendCompactSerialization(SecOTRPublicDHKeyRef pubKey
, CFMutableDataRef appendTo
)
344 AppendECPublicKeyAsDATA(appendTo
, pubKey
->_key
);
348 uint8_t* SecPDHKGetHash(SecOTRPublicDHKeyRef pubKey
)
350 return pubKey
->keyHash
;
354 void SecPDHKeyGenerateS(SecOTRFullDHKeyRef myKey
, SecOTRPublicDHKeyRef theirKey
, cc_unit
* s
)
356 ccn_zero(kExponentiationUnits
, s
);
358 size_t keyLen
= ccn_sizeof_n(kExponentiationUnits
);
359 ccec_compute_key(myKey
->_key
, theirKey
->_key
, &keyLen
, (uint8_t*)s
);
362 static int ccec_cmp(ccec_pub_ctx_t l
, ccec_pub_ctx_t r
)
364 size_t lsize
= ccec_export_pub_size(l
);
365 size_t rsize
= ccec_export_pub_size(r
);
369 if (lsize
== rsize
) {
370 // Keys should never be larger than 256.
371 // But if they are, we want to draw attention to it.
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.");
381 ccec_export_pub(l
, lpub
);
382 ccec_export_pub(r
, rpub
);
384 result
= memcmp(lpub
, rpub
, lsize
);
386 result
= rsize
< lsize
? -1 : 1;
392 bool SecDHKIsGreater(SecOTRFullDHKeyRef myKey
, SecOTRPublicDHKeyRef theirKey
)
394 return ccec_cmp(ccec_ctx_pub(myKey
->_key
), theirKey
->_key
) > 0;
397 static void DeriveKeys(CFDataRef dataToHash
,
401 if (messageKey
== NULL
&& macKey
== NULL
)
404 uint8_t hashedSharedKey
[CCSHA1_OUTPUT_SIZE
];
406 ccdigest(ccsha1_di(), CFDataGetLength(dataToHash
), CFDataGetBytePtr(dataToHash
), hashedSharedKey
);
409 memcpy(messageKey
, hashedSharedKey
, kOTRMessageKeyBytes
);
412 ccdigest(ccsha1_di(), kOTRMessageKeyBytes
, messageKey
, macKey
);
415 bzero(hashedSharedKey
, sizeof(hashedSharedKey
));
418 void SecOTRDHKGenerateOTRKeys(SecOTRFullDHKeyRef myKey
, SecOTRPublicDHKeyRef theirKey
,
419 uint8_t* sendMessageKey
, uint8_t* sendMacKey
,
420 uint8_t* receiveMessageKey
, uint8_t* receiveMacKey
)
422 CFMutableDataRef dataToHash
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
425 cc_unit s
[kExponentiationUnits
];
427 SecPDHKeyGenerateS(myKey
, theirKey
, s
);
428 AppendByte(dataToHash
, SecDHKIsGreater(myKey
, theirKey
) ? 0x01 : 0x02);
429 AppendMPI(dataToHash
, kExponentiationUnits
, s
);
431 ccn_zero(kExponentiationUnits
, s
);
434 DeriveKeys(dataToHash
, receiveMessageKey
, receiveMacKey
);
436 uint8_t *messageTypeByte
= CFDataGetMutableBytePtr(dataToHash
);
438 *messageTypeByte
^= 0x03; // Invert the bits since it's either 1 or 2.
440 DeriveKeys(dataToHash
, sendMessageKey
, sendMacKey
);
442 CFReleaseNull(dataToHash
);