1// 2// SecOTRDHKey.c 3// libsecurity_libSecOTR 4// 5// Created by Mitch Adler on 3/2/11. 6// Copyright 2011 Apple Inc. All rights reserved. 7// 8#include "SecOTRDHKey.h" 9#include <utilities/SecCFWrappers.h> 10 11#include "SecOTRMath.h" 12#include "SecOTRPacketData.h" 13 14#include <corecrypto/ccn.h> 15#include <corecrypto/ccsha1.h> 16#include <corecrypto/ccec_priv.h> 17#include <corecrypto/ccec.h> 18 19#include <CommonCrypto/CommonRandomSPI.h> 20 21#ifdef USECOMMONCRYPTO 22#include <CommonCrypto/CommonDigest.h> 23#endif 24 25#define kECKeySize 256 26 27struct _SecOTRFullDHKey { 28 CFRuntimeBase _base; 29 30 ccec_full_ctx_decl(ccn_sizeof(kECKeySize), _key); 31 uint8_t keyHash[CCSHA1_OUTPUT_SIZE]; 32 33}; 34 35CFGiblisFor(SecOTRFullDHKey); 36 37static size_t AppendECPublicKeyAsDATA(CFMutableDataRef data, ccec_pub_ctx_t public_key) 38{ 39 size_t size = ccec_export_pub_size(public_key); 40 41 AppendLong(data, (uint32_t)size); /* cast: no overflow, pub size always fit in 32 bits */ 42 ccec_export_pub(public_key, CFDataIncreaseLengthAndGetMutableBytes(data, (CFIndex)size)); 43 44 return size; 45} 46 47 48static CF_RETURNS_RETAINED CFStringRef SecOTRFullDHKeyCopyDescription(CFTypeRef cf) 49{ 50 SecOTRFullDHKeyRef session = (SecOTRFullDHKeyRef)cf; 51 return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTRFullDHKeyRef: %p>"), session); 52} 53 54static void SecOTRFullDHKeyDestroy(CFTypeRef cf) 55{ 56 SecOTRFullDHKeyRef fullKey = (SecOTRFullDHKeyRef)cf; 57 58 bzero(fullKey->_key, sizeof(fullKey->_key)); 59} 60 61SecOTRFullDHKeyRef SecOTRFullDHKCreate(CFAllocatorRef allocator) 62{ 63 SecOTRFullDHKeyRef newFDHK = CFTypeAllocate(SecOTRFullDHKey, struct _SecOTRFullDHKey, allocator); 64 65 SecFDHKNewKey(newFDHK); 66 67 return newFDHK; 68} 69 70SecOTRFullDHKeyRef SecOTRFullDHKCreateFromBytes(CFAllocatorRef allocator, const uint8_t**bytes, size_t*size) 71{ 72 SecOTRFullDHKeyRef newFDHK = CFTypeAllocate(SecOTRFullDHKey, struct _SecOTRFullDHKey, allocator); 73 74 ccec_ctx_init(ccec_cp_256(), newFDHK->_key); 75 76 uint32_t publicKeySize; 77 require_noerr(ReadLong(bytes, size, &publicKeySize), fail); 78 79 require(publicKeySize <= *size, fail); 80 require_noerr(ccec_import_pub(ccec_cp_256(), publicKeySize, *bytes, newFDHK->_key), fail); 81 ccdigest(ccsha1_di(), publicKeySize, *bytes, newFDHK->keyHash); 82 83 *size -= publicKeySize; 84 *bytes += publicKeySize; 85 86 require_noerr(ReadMPI(bytes, size, ccec_ctx_n(newFDHK->_key), ccec_ctx_k(newFDHK->_key)), fail); 87 88 return newFDHK; 89 90fail: 91 CFReleaseNull(newFDHK); 92 return NULL; 93} 94 95void SecFDHKNewKey(SecOTRFullDHKeyRef fullKey) 96{ 97 struct ccrng_state *rng=ccDRBGGetRngState(); 98 99#if defined(CCECDH_AVAILABLE) 100 ccecdh_generate_key(ccec_cp_256(), rng, fullKey->_key); 101#else 102 ccec_generate_key(ccec_cp_256(), rng, fullKey->_key); 103#endif 104 105 size_t size = ccec_export_pub_size(fullKey->_key); 106 uint8_t publicKey[size]; 107 108 ccec_export_pub(fullKey->_key, publicKey); 109 ccdigest(ccsha1_di(), size, publicKey, fullKey->keyHash); 110} 111 112void SecFDHKAppendSerialization(SecOTRFullDHKeyRef fullKey, CFMutableDataRef appendTo) 113{ 114 AppendECPublicKeyAsDATA(appendTo, fullKey->_key); 115 AppendMPI(appendTo, ccec_ctx_n(fullKey->_key), ccec_ctx_k(fullKey->_key)); 116} 117 118 119void SecFDHKAppendPublicSerialization(SecOTRFullDHKeyRef fullKey, CFMutableDataRef appendTo) 120{ 121 if(ccec_ctx_bitlen(fullKey->_key) != kECKeySize) return; 122 AppendECPublicKeyAsDATA(appendTo, fullKey->_key); 123} 124 125 126uint8_t* SecFDHKGetHash(SecOTRFullDHKeyRef fullKey) 127{ 128 return fullKey->keyHash; 129} 130 131 132 133 134// 135// 136// 137struct _SecOTRPublicDHKey { 138 CFRuntimeBase _base; 139 140 ccec_pub_ctx_decl(ccn_sizeof(kECKeySize), _key); 141 uint8_t keyHash[CCSHA1_OUTPUT_SIZE]; 142 143}; 144 145CFGiblisFor(SecOTRPublicDHKey); 146 147static CF_RETURNS_RETAINED CFStringRef SecOTRPublicDHKeyCopyDescription(CFTypeRef cf) { 148 SecOTRPublicDHKeyRef session = (SecOTRPublicDHKeyRef)cf; 149 return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTRPublicDHKeyRef: %p>"), session); 150} 151 152static void SecOTRPublicDHKeyDestroy(CFTypeRef cf) { 153 SecOTRPublicDHKeyRef pubKey = (SecOTRPublicDHKeyRef)cf; 154 (void) pubKey; 155} 156 157static void ccec_copy_public(ccec_pub_ctx_t source, ccec_pub_ctx_t dest) 158{ 159 ccec_ctx_cp(dest) = ccec_ctx_cp(source); 160 // TODO: +1?! 161 ccn_set(3*ccec_ctx_n(source), (cc_unit*) ccec_ctx_point(dest)._p, (cc_unit*) ccec_ctx_point(source)._p); 162} 163 164SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromFullKey(CFAllocatorRef allocator, SecOTRFullDHKeyRef full) 165{ 166 SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator); 167 168 ccec_copy_public(full->_key, newPDHK->_key); 169 memcpy(newPDHK->keyHash, full->keyHash, CCSHA1_OUTPUT_SIZE); 170 171 return newPDHK; 172} 173 174SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromSerialization(CFAllocatorRef allocator, const uint8_t** bytes, size_t *size) 175{ 176 SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator); 177 178 uint32_t publicKeySize; 179 require_noerr(ReadLong(bytes, size, &publicKeySize), fail); 180 181 require(publicKeySize <= *size, fail); 182 183 require_noerr(ccec_import_pub(ccec_cp_256(), publicKeySize, *bytes, newPDHK->_key), fail); 184 ccdigest(ccsha1_di(), publicKeySize, *bytes, newPDHK->keyHash); 185 186 *size -= publicKeySize; 187 *bytes += publicKeySize; 188 189 return newPDHK; 190fail: 191 CFReleaseNull(newPDHK); 192 return NULL; 193} 194 195SecOTRPublicDHKeyRef SecOTRPublicDHKCreateFromBytes(CFAllocatorRef allocator, const uint8_t** bytes, size_t *size) 196{ 197 SecOTRPublicDHKeyRef newPDHK = CFTypeAllocate(SecOTRPublicDHKey, struct _SecOTRPublicDHKey, allocator); 198 199 require_noerr(ccec_import_pub(ccec_cp_256(), *size, *bytes, newPDHK->_key), fail); 200 ccdigest(ccsha1_di(), *size, *bytes, newPDHK->keyHash); 201 202 return newPDHK; 203fail: 204 CFReleaseNull(newPDHK); 205 return NULL; 206} 207 208void SecPDHKAppendSerialization(SecOTRPublicDHKeyRef pubKey, CFMutableDataRef appendTo) 209{ 210 AppendECPublicKeyAsDATA(appendTo, pubKey->_key); 211} 212 213 214uint8_t* SecPDHKGetHash(SecOTRPublicDHKeyRef pubKey) 215{ 216 return pubKey->keyHash; 217} 218 219 220void SecPDHKeyGenerateS(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey, cc_unit* s) 221{ 222 ccn_zero(kExponentiationUnits, s); 223 224 size_t keyLen = ccn_sizeof_n(kExponentiationUnits); 225 ccec_compute_key(myKey->_key, theirKey->_key, &keyLen, (uint8_t*)s); 226} 227 228static int ccec_cmp(ccec_pub_ctx_t l, ccec_pub_ctx_t r) 229{ 230 size_t lsize = ccec_export_pub_size(l); 231 size_t rsize = ccec_export_pub_size(r); 232 233 int result = 0; 234 235 if (lsize == rsize) { 236 uint8_t lpub[lsize]; 237 uint8_t rpub[rsize]; 238 239 ccec_export_pub(l, lpub); 240 ccec_export_pub(r, rpub); 241 242 result = memcmp(lpub, rpub, lsize); 243 } else { 244 result = rsize < lsize ? -1 : 1; 245 } 246 247 return result; 248} 249 250bool SecDHKIsGreater(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey) 251{ 252 return ccec_cmp(myKey->_key, theirKey->_key) > 0; 253} 254 255static void DeriveKeys(CFDataRef dataToHash, 256 uint8_t* messageKey, 257 uint8_t* macKey) 258{ 259 if (messageKey == NULL && macKey == NULL) 260 return; 261 262 uint8_t hashedSharedKey[CCSHA1_OUTPUT_SIZE]; 263 264#ifdef USECOMMONCRYPTO 265 (void) CCDigest(kCCDigestSHA1, CFDataGetBytePtr(dataToHash), (uint32_t)CFDataGetLength(dataToHash), hashedSharedKey); 266#else 267 ccdigest(ccsha1_di(), CFDataGetLength(dataToHash), CFDataGetBytePtr(dataToHash), hashedSharedKey); 268#endif 269 270 if (messageKey) 271 memcpy(messageKey, hashedSharedKey, kOTRMessageKeyBytes); 272 273 if (macKey) { 274#ifdef USECOMMONCRYPTO 275 (void) CCDigest(kCCDigestSHA1, messageKey, kOTRMessageKeyBytes, macKey); 276#else 277 ccdigest(ccsha1_di(), kOTRMessageKeyBytes, messageKey, macKey); 278#endif 279 } 280 281 bzero(hashedSharedKey, sizeof(hashedSharedKey)); 282} 283 284void SecOTRDHKGenerateOTRKeys(SecOTRFullDHKeyRef myKey, SecOTRPublicDHKeyRef theirKey, 285 uint8_t* sendMessageKey, uint8_t* sendMacKey, 286 uint8_t* receiveMessageKey, uint8_t* receiveMacKey) 287{ 288 CFMutableDataRef dataToHash = CFDataCreateMutable(kCFAllocatorDefault, 0); 289 290 { 291 cc_unit s[kExponentiationUnits]; 292 293 SecPDHKeyGenerateS(myKey, theirKey, s); 294 AppendByte(dataToHash, SecDHKIsGreater(myKey, theirKey) ? 0x01 : 0x02); 295 AppendMPI(dataToHash, kExponentiationUnits, s); 296 297 ccn_zero(kExponentiationUnits, s); 298 } 299 300 DeriveKeys(dataToHash, receiveMessageKey, receiveMacKey); 301 302 uint8_t *messageTypeByte = CFDataGetMutableBytePtr(dataToHash); 303 304 *messageTypeByte ^= 0x03; // Invert the bits since it's either 1 or 2. 305 306 DeriveKeys(dataToHash, sendMessageKey, sendMacKey); 307 308 CFReleaseNull(dataToHash); 309} 310