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