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 void 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
[size
]; 
  45     ccec_export_pub(public_key
, pub_key_bytes_buffer
); 
  47     ccdigest(ccsha1_di(), size
, pub_key_bytes_buffer
, output
); 
  51 struct _SecOTRFullDHKey 
{ 
  54     ccec_full_ctx_decl(ccn_sizeof(kECKeySize
), _key
); 
  55     uint8_t keyHash
[CCSHA1_OUTPUT_SIZE
]; 
  59 CFGiblisWithCompareFor(SecOTRFullDHKey
); 
  61 static size_t AppendECPublicKeyAsDATA(CFMutableDataRef data
, ccec_pub_ctx_t public_key
) 
  63     size_t size 
= ccec_export_pub_size(public_key
); 
  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
)); 
  71 static size_t AppendECCompactPublicKey(CFMutableDataRef data
, ccec_pub_ctx_t public_key
) 
  73     size_t size 
= ccec_compact_export_size(false, public_key
); 
  75     ccec_compact_export(false, CFDataIncreaseLengthAndGetMutableBytes(data
, (CFIndex
)size
), (ccec_full_ctx_t
)public_key
); 
  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
); 
  85     __block CFStringRef description 
= NULL
; 
  87     BufferPerformWithHexString(byte_array
, sizeof(byte_array
), ^(CFStringRef dataString
) { 
  88         description 
= CFRetainSafe(dataString
); 
  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
)); 
  98     action(xString
, yString
); 
  99     CFReleaseNull(xString
); 
 100     CFReleaseNull(yString
); 
 102 static CFStringRef 
SecOTRFullDHKeyCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) 
 104     SecOTRFullDHKeyRef fullDHKey 
= (SecOTRFullDHKeyRef
)cf
; 
 105     __block CFStringRef description 
= NULL
; 
 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
); 
 116 static Boolean 
SecOTRFullDHKeyCompare(CFTypeRef leftCF
, CFTypeRef rightCF
) 
 118     SecOTRFullDHKeyRef left 
= (SecOTRFullDHKeyRef
)leftCF
; 
 119     SecOTRFullDHKeyRef right 
= (SecOTRFullDHKeyRef
)rightCF
; 
 121     return 0 == memcmp(left
->keyHash
, right
->keyHash
, sizeof(left
->keyHash
)); 
 124 static void SecOTRFullDHKeyDestroy(CFTypeRef cf
) 
 126     SecOTRFullDHKeyRef fullKey 
= (SecOTRFullDHKeyRef
)cf
; 
 128     bzero(fullKey
->_key
, sizeof(fullKey
->_key
)); 
 131 static inline void SecOTRFDHKUpdateHash(SecOTRFullDHKeyRef fullKey
) 
 133     GenerateHashForKey(ccec_ctx_pub(fullKey
->_key
), fullKey
->keyHash
); 
 136 SecOTRFullDHKeyRef 
SecOTRFullDHKCreate(CFAllocatorRef allocator
) 
 138     SecOTRFullDHKeyRef newFDHK 
= CFTypeAllocate(SecOTRFullDHKey
, struct _SecOTRFullDHKey
, allocator
); 
 140     SecFDHKNewKey(newFDHK
); 
 145 SecOTRFullDHKeyRef 
SecOTRFullDHKCreateFromBytes(CFAllocatorRef allocator
, const uint8_t**bytes
, size_t*size
) 
 147     SecOTRFullDHKeyRef newFDHK 
= CFTypeAllocate(SecOTRFullDHKey
, struct _SecOTRFullDHKey
, allocator
); 
 149     ccec_ctx_init(ccec_cp_256(), newFDHK
->_key
); 
 151     uint32_t publicKeySize
; 
 152     require_noerr(ReadLong(bytes
, size
, &publicKeySize
), fail
); 
 154     require(publicKeySize 
<= *size
, fail
); 
 155     require_noerr(ccec_import_pub(ccec_cp_256(), publicKeySize
, *bytes
, ccec_ctx_pub(newFDHK
->_key
)), fail
); 
 157     *size 
-= publicKeySize
; 
 158     *bytes 
+= publicKeySize
; 
 160     require_noerr(ReadMPI(bytes
, size
, ccec_ctx_n(newFDHK
->_key
), ccec_ctx_k(newFDHK
->_key
)), fail
); 
 162     SecOTRFDHKUpdateHash(newFDHK
); 
 167     CFReleaseNull(newFDHK
); 
 171 void SecFDHKNewKey(SecOTRFullDHKeyRef fullKey
) 
 173     struct ccrng_state 
*rng
=ccDRBGGetRngState(); 
 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. 
 179     ccec_compact_generate_key(ccec_cp_256(), rng
, fullKey
->_key
); 
 181     SecOTRFDHKUpdateHash(fullKey
); 
 185 void SecFDHKAppendSerialization(SecOTRFullDHKeyRef fullKey
, CFMutableDataRef appendTo
) 
 187     AppendECPublicKeyAsDATA(appendTo
, ccec_ctx_pub(fullKey
->_key
)); 
 188     AppendMPI(appendTo
, ccec_ctx_n(fullKey
->_key
), ccec_ctx_k(fullKey
->_key
)); 
 191 void SecFDHKAppendPublicSerialization(SecOTRFullDHKeyRef fullKey
, CFMutableDataRef appendTo
) 
 193     if(ccec_ctx_bitlen(fullKey
->_key
) != kECKeySize
) return; 
 194     AppendECPublicKeyAsDATA(appendTo
, ccec_ctx_pub(fullKey
->_key
)); 
 197 void SecFDHKAppendCompactPublicSerialization(SecOTRFullDHKeyRef fullKey
, CFMutableDataRef appendTo
) 
 199     if(ccec_ctx_bitlen(fullKey
->_key
) != kECKeySize
) return; 
 200     AppendECCompactPublicKey(appendTo
, ccec_ctx_pub(fullKey
->_key
)); 
 204 uint8_t* SecFDHKGetHash(SecOTRFullDHKeyRef fullKey
) 
 206     return fullKey
->keyHash
; 
 215 struct _SecOTRPublicDHKey 
{ 
 218     ccec_pub_ctx_decl(ccn_sizeof(kECKeySize
), _key
); 
 219     uint8_t keyHash
[CCSHA1_OUTPUT_SIZE
]; 
 222 CFGiblisWithCompareFor(SecOTRPublicDHKey
); 
 224 static CFStringRef 
SecOTRPublicDHKeyCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) { 
 225     SecOTRPublicDHKeyRef publicDHKey 
= (SecOTRPublicDHKeyRef
)cf
; 
 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
); 
 237 static Boolean 
SecOTRPublicDHKeyCompare(CFTypeRef leftCF
, CFTypeRef rightCF
) 
 239     SecOTRPublicDHKeyRef left 
= (SecOTRPublicDHKeyRef
)leftCF
; 
 240     SecOTRPublicDHKeyRef right 
= (SecOTRPublicDHKeyRef
)rightCF
; 
 242     return 0 == memcmp(left
->keyHash
, right
->keyHash
, sizeof(left
->keyHash
)); 
 245 static void SecOTRPublicDHKeyDestroy(CFTypeRef cf
) { 
 246     SecOTRPublicDHKeyRef pubKey 
= (SecOTRPublicDHKeyRef
)cf
; 
 250 static inline void SecOTRPDHKUpdateHash(SecOTRPublicDHKeyRef pubKey
) 
 252     GenerateHashForKey(pubKey
->_key
, pubKey
->keyHash
); 
 255 static void ccec_copy_public(ccec_pub_ctx_t source
, ccec_pub_ctx_t dest
) 
 257     cc_size sourceKeyN 
= ccec_ctx_n(source
); 
 258     memcpy(dest
, source
, ccec_pub_ctx_size(ccn_sizeof_n(sourceKeyN
))); 
 261 SecOTRPublicDHKeyRef 
SecOTRPublicDHKCreateFromFullKey(CFAllocatorRef allocator
, SecOTRFullDHKeyRef full
) 
 263     SecOTRPublicDHKeyRef newPDHK 
= CFTypeAllocate(SecOTRPublicDHKey
, struct _SecOTRPublicDHKey
, allocator
); 
 265     ccec_copy_public(ccec_ctx_pub(full
->_key
), newPDHK
->_key
); 
 266     memcpy(newPDHK
->keyHash
, full
->keyHash
, CCSHA1_OUTPUT_SIZE
); 
 271 SecOTRPublicDHKeyRef 
SecOTRPublicDHKCreateFromSerialization(CFAllocatorRef allocator
, const uint8_t** bytes
, size_t *size
) 
 273     size_t publicKeySize
; 
 275         uint32_t readSize 
= 0; 
 276         require_noerr(ReadLong(bytes
, size
, &readSize
), fail
); 
 277         publicKeySize 
= readSize
; 
 280     require(publicKeySize 
<= *size
, fail
); 
 282     *size 
-= publicKeySize
; 
 284     return SecOTRPublicDHKCreateFromBytes(allocator
, bytes
, &publicKeySize
); 
 289 SecOTRPublicDHKeyRef 
SecOTRPublicDHKCreateFromCompactSerialization(CFAllocatorRef allocator
, const uint8_t** bytes
, size_t *size
) 
 291     SecOTRPublicDHKeyRef newPDHK 
= CFTypeAllocate(SecOTRPublicDHKey
, struct _SecOTRPublicDHKey
, allocator
); 
 293     size_t publicKeySize 
= ccec_cp_prime_size(ccec_cp_256()); 
 295     require_quiet(publicKeySize 
<= *size
, fail
); 
 297     require_noerr_quiet(ccec_compact_import_pub(ccec_cp_256(), publicKeySize
, *bytes
, newPDHK
->_key
), fail
); 
 299     *size 
-= publicKeySize
; 
 300     *bytes 
+= publicKeySize
; 
 302     SecOTRPDHKUpdateHash(newPDHK
); 
 306     CFReleaseNull(newPDHK
); 
 311 SecOTRPublicDHKeyRef 
SecOTRPublicDHKCreateFromBytes(CFAllocatorRef allocator
, const uint8_t** bytes
, size_t *size
) 
 313     SecOTRPublicDHKeyRef newPDHK 
= CFTypeAllocate(SecOTRPublicDHKey
, struct _SecOTRPublicDHKey
, allocator
); 
 315     require_noerr(ccec_import_pub(ccec_cp_256(), *size
, *bytes
, newPDHK
->_key
), fail
); 
 320     SecOTRPDHKUpdateHash(newPDHK
); 
 324     CFReleaseNull(newPDHK
); 
 329 void SecPDHKAppendSerialization(SecOTRPublicDHKeyRef pubKey
, CFMutableDataRef appendTo
) 
 331     AppendECPublicKeyAsDATA(appendTo
, pubKey
->_key
); 
 334 void SecPDHKAppendCompactSerialization(SecOTRPublicDHKeyRef pubKey
, CFMutableDataRef appendTo
) 
 336     AppendECPublicKeyAsDATA(appendTo
, pubKey
->_key
); 
 340 uint8_t* SecPDHKGetHash(SecOTRPublicDHKeyRef pubKey
) 
 342     return pubKey
->keyHash
; 
 346 void SecPDHKeyGenerateS(SecOTRFullDHKeyRef myKey
, SecOTRPublicDHKeyRef theirKey
, cc_unit
* s
) 
 348     ccn_zero(kExponentiationUnits
, s
); 
 350     size_t keyLen 
= ccn_sizeof_n(kExponentiationUnits
); 
 351     ccec_compute_key(myKey
->_key
, theirKey
->_key
, &keyLen
, (uint8_t*)s
); 
 354 static int ccec_cmp(ccec_pub_ctx_t l
, ccec_pub_ctx_t r
) 
 356     size_t lsize 
= ccec_export_pub_size(l
); 
 357     size_t rsize 
= ccec_export_pub_size(r
); 
 361     if (lsize 
== rsize
) { 
 365         ccec_export_pub(l
, lpub
); 
 366         ccec_export_pub(r
, rpub
); 
 368         result 
= memcmp(lpub
, rpub
, lsize
); 
 370         result 
= rsize 
< lsize 
? -1 : 1; 
 376 bool SecDHKIsGreater(SecOTRFullDHKeyRef myKey
, SecOTRPublicDHKeyRef theirKey
) 
 378     return ccec_cmp(ccec_ctx_pub(myKey
->_key
), theirKey
->_key
) > 0; 
 381 static void DeriveKeys(CFDataRef dataToHash
, 
 385     if (messageKey 
== NULL 
&& macKey 
== NULL
) 
 388     uint8_t hashedSharedKey
[CCSHA1_OUTPUT_SIZE
]; 
 390     ccdigest(ccsha1_di(), CFDataGetLength(dataToHash
), CFDataGetBytePtr(dataToHash
), hashedSharedKey
); 
 393         memcpy(messageKey
, hashedSharedKey
, kOTRMessageKeyBytes
); 
 396         ccdigest(ccsha1_di(), kOTRMessageKeyBytes
, messageKey
, macKey
); 
 399     bzero(hashedSharedKey
, sizeof(hashedSharedKey
)); 
 402 void SecOTRDHKGenerateOTRKeys(SecOTRFullDHKeyRef myKey
, SecOTRPublicDHKeyRef theirKey
, 
 403                            uint8_t* sendMessageKey
, uint8_t* sendMacKey
, 
 404                            uint8_t* receiveMessageKey
, uint8_t* receiveMacKey
) 
 406     CFMutableDataRef dataToHash 
= CFDataCreateMutable(kCFAllocatorDefault
, 0); 
 409         cc_unit s
[kExponentiationUnits
]; 
 411         SecPDHKeyGenerateS(myKey
, theirKey
, s
); 
 412         AppendByte(dataToHash
, SecDHKIsGreater(myKey
, theirKey
) ? 0x01 : 0x02); 
 413         AppendMPI(dataToHash
, kExponentiationUnits
, s
); 
 415         ccn_zero(kExponentiationUnits
, s
); 
 418     DeriveKeys(dataToHash
, receiveMessageKey
, receiveMacKey
); 
 420     uint8_t *messageTypeByte 
= CFDataGetMutableBytePtr(dataToHash
); 
 422     *messageTypeByte 
^= 0x03; // Invert the bits since it's either 1 or 2. 
 424     DeriveKeys(dataToHash
, sendMessageKey
, sendMacKey
); 
 426     CFReleaseNull(dataToHash
);