1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "crypto/symmetric_key.h" 6 7 #include <vector> 8 9 // TODO(wtc): replace scoped_array by std::vector. 10 #include "base/memory/scoped_ptr.h" 11 #include "base/sys_byteorder.h" 12 13 namespace crypto { 14 15 namespace { 16 17 // The following is a non-public Microsoft header documented in MSDN under 18 // CryptImportKey / CryptExportKey. Following the header is the byte array of 19 // the actual plaintext key. 20 struct PlaintextBlobHeader { 21 BLOBHEADER hdr; 22 DWORD cbKeySize; 23 }; 24 25 // CryptoAPI makes use of three distinct ALG_IDs for AES, rather than just 26 // CALG_AES (which exists, but depending on the functions you are calling, may 27 // result in function failure, whereas the subtype would succeed). 28 ALG_ID GetAESAlgIDForKeySize(size_t key_size_in_bits) { 29 // Only AES-128/-192/-256 is supported in CryptoAPI. 30 switch (key_size_in_bits) { 31 case 128: 32 return CALG_AES_128; 33 case 192: 34 return CALG_AES_192; 35 case 256: 36 return CALG_AES_256; 37 default: 38 NOTREACHED(); 39 return 0; 40 } 41 }; 42 43 // Imports a raw/plaintext key of |key_size| stored in |*key_data| into a new 44 // key created for the specified |provider|. |alg| contains the algorithm of 45 // the key being imported. 46 // If |key_data| is intended to be used as an HMAC key, then |alg| should be 47 // CALG_HMAC. 48 // If successful, returns true and stores the imported key in |*key|. 49 // TODO(wtc): use this function in hmac_win.cc. 50 bool ImportRawKey(HCRYPTPROV provider, 51 ALG_ID alg, 52 const void* key_data, size_t key_size, 53 ScopedHCRYPTKEY* key) { 54 DCHECK_GT(key_size, 0); 55 56 DWORD actual_size = 57 static_cast<DWORD>(sizeof(PlaintextBlobHeader) + key_size); 58 std::vector<BYTE> tmp_data(actual_size); 59 BYTE* actual_key = &tmp_data[0]; 60 memcpy(actual_key + sizeof(PlaintextBlobHeader), key_data, key_size); 61 PlaintextBlobHeader* key_header = 62 reinterpret_cast<PlaintextBlobHeader*>(actual_key); 63 memset(key_header, 0, sizeof(PlaintextBlobHeader)); 64 65 key_header->hdr.bType = PLAINTEXTKEYBLOB; 66 key_header->hdr.bVersion = CUR_BLOB_VERSION; 67 key_header->hdr.aiKeyAlg = alg; 68 69 key_header->cbKeySize = static_cast<DWORD>(key_size); 70 71 HCRYPTKEY unsafe_key = NULL; 72 DWORD flags = CRYPT_EXPORTABLE; 73 if (alg == CALG_HMAC) { 74 // Though it may appear odd that IPSEC and RC2 are being used, this is 75 // done in accordance with Microsoft's FIPS 140-2 Security Policy for the 76 // RSA Enhanced Provider, as the approved means of using arbitrary HMAC 77 // key material. 78 key_header->hdr.aiKeyAlg = CALG_RC2; 79 flags |= CRYPT_IPSEC_HMAC_KEY; 80 } 81 82 BOOL ok = 83 CryptImportKey(provider, actual_key, actual_size, 0, flags, &unsafe_key); 84 85 // Clean up the temporary copy of key, regardless of whether it was imported 86 // sucessfully or not. 87 SecureZeroMemory(actual_key, actual_size); 88 89 if (!ok) 90 return false; 91 92 key->reset(unsafe_key); 93 return true; 94 } 95 96 // Attempts to generate a random AES key of |key_size_in_bits|. Returns true 97 // if generation is successful, storing the generated key in |*key| and the 98 // key provider (CSP) in |*provider|. 99 bool GenerateAESKey(size_t key_size_in_bits, 100 ScopedHCRYPTPROV* provider, 101 ScopedHCRYPTKEY* key) { 102 DCHECK(provider); 103 DCHECK(key); 104 105 ALG_ID alg = GetAESAlgIDForKeySize(key_size_in_bits); 106 if (alg == 0) 107 return false; 108 109 ScopedHCRYPTPROV safe_provider; 110 // Note: The only time NULL is safe to be passed as pszContainer is when 111 // dwFlags contains CRYPT_VERIFYCONTEXT, as all keys generated and/or used 112 // will be treated as ephemeral keys and not persisted. 113 BOOL ok = CryptAcquireContext(safe_provider.receive(), NULL, NULL, 114 PROV_RSA_AES, CRYPT_VERIFYCONTEXT); 115 if (!ok) 116 return false; 117 118 ScopedHCRYPTKEY safe_key; 119 // In the FIPS 140-2 Security Policy for CAPI on XP/Vista+, Microsoft notes 120 // that CryptGenKey makes use of the same functionality exposed via 121 // CryptGenRandom. The reason this is being used, as opposed to 122 // CryptGenRandom and CryptImportKey is for compliance with the security 123 // policy 124 ok = CryptGenKey(safe_provider.get(), alg, CRYPT_EXPORTABLE, 125 safe_key.receive()); 126 if (!ok) 127 return false; 128 129 key->swap(safe_key); 130 provider->swap(safe_provider); 131 132 return true; 133 } 134 135 // Returns true if the HMAC key size meets the requirement of FIPS 198 136 // Section 3. |alg| is the hash function used in the HMAC. 137 bool CheckHMACKeySize(size_t key_size_in_bits, ALG_ID alg) { 138 DWORD hash_size = 0; 139 switch (alg) { 140 case CALG_SHA1: 141 hash_size = 20; 142 break; 143 case CALG_SHA_256: 144 hash_size = 32; 145 break; 146 case CALG_SHA_384: 147 hash_size = 48; 148 break; 149 case CALG_SHA_512: 150 hash_size = 64; 151 break; 152 } 153 if (hash_size == 0) 154 return false; 155 156 // An HMAC key must be >= L/2, where L is the output size of the hash 157 // function being used. 158 return (key_size_in_bits >= (hash_size / 2 * 8) && 159 (key_size_in_bits % 8) == 0); 160 } 161 162 // Attempts to generate a random, |key_size_in_bits|-long HMAC key, for use 163 // with the hash function |alg|. 164 // |key_size_in_bits| must be >= 1/2 the hash size of |alg| for security. 165 // Returns true if generation is successful, storing the generated key in 166 // |*key| and the key provider (CSP) in |*provider|. 167 bool GenerateHMACKey(size_t key_size_in_bits, 168 ALG_ID alg, 169 ScopedHCRYPTPROV* provider, 170 ScopedHCRYPTKEY* key, 171 scoped_ptr<BYTE[]>* raw_key) { 172 DCHECK(provider); 173 DCHECK(key); 174 DCHECK(raw_key); 175 176 if (!CheckHMACKeySize(key_size_in_bits, alg)) 177 return false; 178 179 ScopedHCRYPTPROV safe_provider; 180 // See comment in GenerateAESKey as to why NULL is acceptable for the 181 // container name. 182 BOOL ok = CryptAcquireContext(safe_provider.receive(), NULL, NULL, 183 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); 184 if (!ok) 185 return false; 186 187 DWORD key_size_in_bytes = static_cast<DWORD>(key_size_in_bits / 8); 188 scoped_ptr<BYTE[]> random(new BYTE[key_size_in_bytes]); 189 ok = CryptGenRandom(safe_provider, key_size_in_bytes, random.get()); 190 if (!ok) 191 return false; 192 193 ScopedHCRYPTKEY safe_key; 194 bool rv = ImportRawKey(safe_provider, CALG_HMAC, random.get(), 195 key_size_in_bytes, &safe_key); 196 if (rv) { 197 key->swap(safe_key); 198 provider->swap(safe_provider); 199 raw_key->swap(random); 200 } 201 202 SecureZeroMemory(random.get(), key_size_in_bytes); 203 return rv; 204 } 205 206 // Attempts to create an HMAC hash instance using the specified |provider| 207 // and |key|. The inner hash function will be |hash_alg|. If successful, 208 // returns true and stores the hash in |*hash|. 209 // TODO(wtc): use this function in hmac_win.cc. 210 bool CreateHMACHash(HCRYPTPROV provider, 211 HCRYPTKEY key, 212 ALG_ID hash_alg, 213 ScopedHCRYPTHASH* hash) { 214 ScopedHCRYPTHASH safe_hash; 215 BOOL ok = CryptCreateHash(provider, CALG_HMAC, key, 0, safe_hash.receive()); 216 if (!ok) 217 return false; 218 219 HMAC_INFO hmac_info; 220 memset(&hmac_info, 0, sizeof(hmac_info)); 221 hmac_info.HashAlgid = hash_alg; 222 223 ok = CryptSetHashParam(safe_hash, HP_HMAC_INFO, 224 reinterpret_cast<const BYTE*>(&hmac_info), 0); 225 if (!ok) 226 return false; 227 228 hash->swap(safe_hash); 229 return true; 230 } 231 232 // Computes a block of the derived key using the PBKDF2 function F for the 233 // specified |block_index| using the PRF |hash|, writing the output to 234 // |output_buf|. 235 // |output_buf| must have enough space to accomodate the output of the PRF 236 // specified by |hash|. 237 // Returns true if the block was successfully computed. 238 bool ComputePBKDF2Block(HCRYPTHASH hash, 239 DWORD hash_size, 240 const std::string& salt, 241 size_t iterations, 242 uint32 block_index, 243 BYTE* output_buf) { 244 // From RFC 2898: 245 // 3. <snip> The function F is defined as the exclusive-or sum of the first 246 // c iterates of the underlying pseudorandom function PRF applied to the 247 // password P and the concatenation of the salt S and the block index i: 248 // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c 249 // where 250 // U_1 = PRF(P, S || INT (i)) 251 // U_2 = PRF(P, U_1) 252 // ... 253 // U_c = PRF(P, U_{c-1}) 254 ScopedHCRYPTHASH safe_hash; 255 BOOL ok = CryptDuplicateHash(hash, NULL, 0, safe_hash.receive()); 256 if (!ok) 257 return false; 258 259 // Iteration U_1: Compute PRF for S. 260 ok = CryptHashData(safe_hash, reinterpret_cast<const BYTE*>(salt.data()), 261 static_cast<DWORD>(salt.size()), 0); 262 if (!ok) 263 return false; 264 265 // Iteration U_1: and append (big-endian) INT (i). 266 uint32 big_endian_block_index = base::HostToNet32(block_index); 267 ok = CryptHashData(safe_hash, 268 reinterpret_cast<BYTE*>(&big_endian_block_index), 269 sizeof(big_endian_block_index), 0); 270 271 std::vector<BYTE> hash_value(hash_size); 272 273 DWORD size = hash_size; 274 ok = CryptGetHashParam(safe_hash, HP_HASHVAL, &hash_value[0], &size, 0); 275 if (!ok || size != hash_size) 276 return false; 277 278 memcpy(output_buf, &hash_value[0], hash_size); 279 280 // Iteration 2 - c: Compute U_{iteration} by applying the PRF to 281 // U_{iteration - 1}, then xor the resultant hash with |output|, which 282 // contains U_1 ^ U_2 ^ ... ^ U_{iteration - 1}. 283 for (size_t iteration = 2; iteration <= iterations; ++iteration) { 284 safe_hash.reset(); 285 ok = CryptDuplicateHash(hash, NULL, 0, safe_hash.receive()); 286 if (!ok) 287 return false; 288 289 ok = CryptHashData(safe_hash, &hash_value[0], hash_size, 0); 290 if (!ok) 291 return false; 292 293 size = hash_size; 294 ok = CryptGetHashParam(safe_hash, HP_HASHVAL, &hash_value[0], &size, 0); 295 if (!ok || size != hash_size) 296 return false; 297 298 for (int i = 0; i < hash_size; ++i) 299 output_buf[i] ^= hash_value[i]; 300 } 301 302 return true; 303 } 304 305 } // namespace 306 307 SymmetricKey::~SymmetricKey() { 308 // TODO(wtc): create a "secure" string type that zeroes itself in the 309 // destructor. 310 if (!raw_key_.empty()) 311 SecureZeroMemory(const_cast<char *>(raw_key_.data()), raw_key_.size()); 312 } 313 314 // static 315 SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, 316 size_t key_size_in_bits) { 317 DCHECK_GE(key_size_in_bits, 8); 318 319 ScopedHCRYPTPROV provider; 320 ScopedHCRYPTKEY key; 321 322 bool ok = false; 323 scoped_ptr<BYTE[]> raw_key; 324 325 switch (algorithm) { 326 case AES: 327 ok = GenerateAESKey(key_size_in_bits, &provider, &key); 328 break; 329 case HMAC_SHA1: 330 ok = GenerateHMACKey(key_size_in_bits, CALG_SHA1, &provider, 331 &key, &raw_key); 332 break; 333 } 334 335 if (!ok) { 336 NOTREACHED(); 337 return NULL; 338 } 339 340 size_t key_size_in_bytes = key_size_in_bits / 8; 341 if (raw_key == NULL) 342 key_size_in_bytes = 0; 343 344 SymmetricKey* result = new SymmetricKey(provider.release(), 345 key.release(), 346 raw_key.get(), 347 key_size_in_bytes); 348 if (raw_key != NULL) 349 SecureZeroMemory(raw_key.get(), key_size_in_bytes); 350 351 return result; 352 } 353 354 // static 355 SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, 356 const std::string& password, 357 const std::string& salt, 358 size_t iterations, 359 size_t key_size_in_bits) { 360 // CryptoAPI lacks routines to perform PBKDF2 derivation as specified 361 // in RFC 2898, so it must be manually implemented. Only HMAC-SHA1 is 362 // supported as the PRF. 363 364 // While not used until the end, sanity-check the input before proceeding 365 // with the expensive computation. 366 DWORD provider_type = 0; 367 ALG_ID alg = 0; 368 switch (algorithm) { 369 case AES: 370 provider_type = PROV_RSA_AES; 371 alg = GetAESAlgIDForKeySize(key_size_in_bits); 372 break; 373 case HMAC_SHA1: 374 provider_type = PROV_RSA_FULL; 375 alg = CALG_HMAC; 376 break; 377 default: 378 NOTREACHED(); 379 break; 380 } 381 if (provider_type == 0 || alg == 0) 382 return NULL; 383 384 ScopedHCRYPTPROV provider; 385 BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type, 386 CRYPT_VERIFYCONTEXT); 387 if (!ok) 388 return NULL; 389 390 // Convert the user password into a key suitable to be fed into the PRF 391 // function. 392 ScopedHCRYPTKEY password_as_key; 393 BYTE* password_as_bytes = 394 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(password.data())); 395 if (!ImportRawKey(provider, CALG_HMAC, password_as_bytes, 396 password.size(), &password_as_key)) 397 return NULL; 398 399 // Configure the PRF function. Only HMAC variants are supported, with the 400 // only hash function supported being SHA1. 401 // TODO(rsleevi): Support SHA-256 on XP SP3+. 402 ScopedHCRYPTHASH prf; 403 if (!CreateHMACHash(provider, password_as_key, CALG_SHA1, &prf)) 404 return NULL; 405 406 DWORD hLen = 0; 407 DWORD param_size = sizeof(hLen); 408 ok = CryptGetHashParam(prf, HP_HASHSIZE, 409 reinterpret_cast<BYTE*>(&hLen), ¶m_size, 0); 410 if (!ok || hLen == 0) 411 return NULL; 412 413 // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and stop. 414 size_t dkLen = key_size_in_bits / 8; 415 DCHECK_GT(dkLen, 0); 416 417 if ((dkLen / hLen) > 0xFFFFFFFF) { 418 DLOG(ERROR) << "Derived key too long."; 419 return NULL; 420 } 421 422 // 2. Let l be the number of hLen-octet blocks in the derived key, 423 // rounding up, and let r be the number of octets in the last 424 // block: 425 size_t L = (dkLen + hLen - 1) / hLen; 426 DCHECK_GT(L, 0); 427 428 size_t total_generated_size = L * hLen; 429 std::vector<BYTE> generated_key(total_generated_size); 430 BYTE* block_offset = &generated_key[0]; 431 432 // 3. For each block of the derived key apply the function F defined below 433 // to the password P, the salt S, the iteration count c, and the block 434 // index to compute the block: 435 // T_1 = F (P, S, c, 1) 436 // T_2 = F (P, S, c, 2) 437 // ... 438 // T_l = F (P, S, c, l) 439 // <snip> 440 // 4. Concatenate the blocks and extract the first dkLen octets to produce 441 // a derived key DK: 442 // DK = T_1 || T_2 || ... || T_l<0..r-1> 443 for (uint32 block_index = 1; block_index <= L; ++block_index) { 444 if (!ComputePBKDF2Block(prf, hLen, salt, iterations, block_index, 445 block_offset)) 446 return NULL; 447 block_offset += hLen; 448 } 449 450 // Convert the derived key bytes into a key handle for the desired algorithm. 451 ScopedHCRYPTKEY key; 452 if (!ImportRawKey(provider, alg, &generated_key[0], dkLen, &key)) 453 return NULL; 454 455 SymmetricKey* result = new SymmetricKey(provider.release(), key.release(), 456 &generated_key[0], dkLen); 457 458 SecureZeroMemory(&generated_key[0], total_generated_size); 459 460 return result; 461 } 462 463 // static 464 SymmetricKey* SymmetricKey::Import(Algorithm algorithm, 465 const std::string& raw_key) { 466 DWORD provider_type = 0; 467 ALG_ID alg = 0; 468 switch (algorithm) { 469 case AES: 470 provider_type = PROV_RSA_AES; 471 alg = GetAESAlgIDForKeySize(raw_key.size() * 8); 472 break; 473 case HMAC_SHA1: 474 provider_type = PROV_RSA_FULL; 475 alg = CALG_HMAC; 476 break; 477 default: 478 NOTREACHED(); 479 break; 480 } 481 if (provider_type == 0 || alg == 0) 482 return NULL; 483 484 ScopedHCRYPTPROV provider; 485 BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type, 486 CRYPT_VERIFYCONTEXT); 487 if (!ok) 488 return NULL; 489 490 ScopedHCRYPTKEY key; 491 if (!ImportRawKey(provider, alg, raw_key.data(), raw_key.size(), &key)) 492 return NULL; 493 494 return new SymmetricKey(provider.release(), key.release(), 495 raw_key.data(), raw_key.size()); 496 } 497 498 bool SymmetricKey::GetRawKey(std::string* raw_key) { 499 // Short circuit for when the key was supplied to the constructor. 500 if (!raw_key_.empty()) { 501 *raw_key = raw_key_; 502 return true; 503 } 504 505 DWORD size = 0; 506 BOOL ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, NULL, &size); 507 if (!ok) 508 return false; 509 510 std::vector<BYTE> result(size); 511 512 ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, &result[0], &size); 513 if (!ok) 514 return false; 515 516 PlaintextBlobHeader* header = 517 reinterpret_cast<PlaintextBlobHeader*>(&result[0]); 518 raw_key->assign(reinterpret_cast<char*>(&result[sizeof(*header)]), 519 header->cbKeySize); 520 521 SecureZeroMemory(&result[0], size); 522 523 return true; 524 } 525 526 SymmetricKey::SymmetricKey(HCRYPTPROV provider, 527 HCRYPTKEY key, 528 const void* key_data, size_t key_size_in_bytes) 529 : provider_(provider), key_(key) { 530 if (key_data) { 531 raw_key_.assign(reinterpret_cast<const char*>(key_data), 532 key_size_in_bytes); 533 } 534 } 535 536 } // namespace crypto 537