[email protected] | 4a0141b | 2012-03-27 01:15:30 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 41c78fa | 2010-03-22 20:08:41 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 4b559b4d | 2011-04-14 17:37:14 | [diff] [blame] | 5 | #include "crypto/symmetric_key.h" |
[email protected] | 41c78fa | 2010-03-22 20:08:41 | [diff] [blame] | 6 | |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 7 | #include <vector> |
| 8 | |
| 9 | // TODO(wtc): replace scoped_array by std::vector. |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 10 | #include "base/memory/scoped_ptr.h" |
[email protected] | 9eb7b11b | 2012-03-28 20:19:31 | [diff] [blame] | 11 | #include "base/sys_byteorder.h" |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 12 | |
[email protected] | 4b559b4d | 2011-04-14 17:37:14 | [diff] [blame] | 13 | namespace crypto { |
[email protected] | 41c78fa | 2010-03-22 20:08:41 | [diff] [blame] | 14 | |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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. |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 50 | bool ImportRawKey(HCRYPTPROV provider, |
| 51 | ALG_ID alg, |
[email protected] | 4a0141b | 2012-03-27 01:15:30 | [diff] [blame] | 52 | const void* key_data, size_t key_size, |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 53 | ScopedHCRYPTKEY* key) { |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 54 | DCHECK_GT(key_size, 0); |
| 55 | |
[email protected] | 4a0141b | 2012-03-27 01:15:30 | [diff] [blame] | 56 | DWORD actual_size = |
| 57 | static_cast<DWORD>(sizeof(PlaintextBlobHeader) + key_size); |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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 | |
[email protected] | 4a0141b | 2012-03-27 01:15:30 | [diff] [blame] | 69 | key_header->cbKeySize = static_cast<DWORD>(key_size); |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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 | |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 82 | BOOL ok = |
| 83 | CryptImportKey(provider, actual_key, actual_size, 0, flags, &unsafe_key); |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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|. |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 99 | bool GenerateAESKey(size_t key_size_in_bits, |
| 100 | ScopedHCRYPTPROV* provider, |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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|. |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 167 | bool GenerateHMACKey(size_t key_size_in_bits, |
| 168 | ALG_ID alg, |
| 169 | ScopedHCRYPTPROV* provider, |
| 170 | ScopedHCRYPTKEY* key, |
[email protected] | bbd87929 | 2013-04-11 16:46:51 | [diff] [blame] | 171 | scoped_ptr<BYTE[]>* raw_key) { |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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 | |
[email protected] | 4a0141b | 2012-03-27 01:15:30 | [diff] [blame] | 187 | DWORD key_size_in_bytes = static_cast<DWORD>(key_size_in_bits / 8); |
[email protected] | bbd87929 | 2013-04-11 16:46:51 | [diff] [blame] | 188 | scoped_ptr<BYTE[]> random(new BYTE[key_size_in_bytes]); |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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. |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 210 | bool CreateHMACHash(HCRYPTPROV provider, |
| 211 | HCRYPTKEY key, |
| 212 | ALG_ID hash_alg, |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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. |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 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) { |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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. |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 260 | ok = CryptHashData(safe_hash, reinterpret_cast<const BYTE*>(salt.data()), |
[email protected] | 4a0141b | 2012-03-27 01:15:30 | [diff] [blame] | 261 | static_cast<DWORD>(salt.size()), 0); |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 262 | if (!ok) |
| 263 | return false; |
| 264 | |
| 265 | // Iteration U_1: and append (big-endian) INT (i). |
[email protected] | 9eb7b11b | 2012-03-28 20:19:31 | [diff] [blame] | 266 | uint32 big_endian_block_index = base::HostToNet32(block_index); |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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 | } |
[email protected] | 39422e3 | 2010-03-25 19:13:00 | [diff] [blame] | 313 | |
| 314 | // static |
[email protected] | 10811823 | 2010-03-29 18:22:24 | [diff] [blame] | 315 | SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, |
| 316 | size_t key_size_in_bits) { |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 317 | DCHECK_GE(key_size_in_bits, 8); |
| 318 | |
| 319 | ScopedHCRYPTPROV provider; |
| 320 | ScopedHCRYPTKEY key; |
| 321 | |
| 322 | bool ok = false; |
[email protected] | bbd87929 | 2013-04-11 16:46:51 | [diff] [blame] | 323 | scoped_ptr<BYTE[]> raw_key; |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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; |
[email protected] | 39422e3 | 2010-03-25 19:13:00 | [diff] [blame] | 352 | } |
| 353 | |
| 354 | // static |
| 355 | SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, |
| 356 | const std::string& password, |
| 357 | const std::string& salt, |
| 358 | size_t iterations, |
[email protected] | 10811823 | 2010-03-29 18:22:24 | [diff] [blame] | 359 | size_t key_size_in_bits) { |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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) { |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 444 | if (!ComputePBKDF2Block(prf, hLen, salt, iterations, block_index, |
| 445 | block_offset)) |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 446 | return NULL; |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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, |
[email protected] | 896200b3 | 2010-07-20 19:21:18 | [diff] [blame] | 465 | const std::string& raw_key) { |
[email protected] | c6e86ae | 2010-09-14 03:40:40 | [diff] [blame] | 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; |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 483 | |
| 484 | ScopedHCRYPTPROV provider; |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 485 | BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type, |
| 486 | CRYPT_VERIFYCONTEXT); |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 487 | if (!ok) |
| 488 | return NULL; |
| 489 | |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 490 | ScopedHCRYPTKEY key; |
[email protected] | 896200b3 | 2010-07-20 19:21:18 | [diff] [blame] | 491 | if (!ImportRawKey(provider, alg, raw_key.data(), raw_key.size(), &key)) |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 492 | return NULL; |
| 493 | |
| 494 | return new SymmetricKey(provider.release(), key.release(), |
[email protected] | 896200b3 | 2010-07-20 19:21:18 | [diff] [blame] | 495 | raw_key.data(), raw_key.size()); |
[email protected] | 39422e3 | 2010-03-25 19:13:00 | [diff] [blame] | 496 | } |
| 497 | |
[email protected] | 41c78fa | 2010-03-22 20:08:41 | [diff] [blame] | 498 | bool SymmetricKey::GetRawKey(std::string* raw_key) { |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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; |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 506 | BOOL ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, NULL, &size); |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 507 | if (!ok) |
| 508 | return false; |
| 509 | |
| 510 | std::vector<BYTE> result(size); |
| 511 | |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 512 | ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, &result[0], &size); |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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 | |
[email protected] | 08ce4d4 | 2010-10-21 00:31:19 | [diff] [blame] | 526 | SymmetricKey::SymmetricKey(HCRYPTPROV provider, |
| 527 | HCRYPTKEY key, |
[email protected] | 692033a | 2010-04-09 18:40:50 | [diff] [blame] | 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 | } |
[email protected] | 41c78fa | 2010-03-22 20:08:41 | [diff] [blame] | 534 | } |
| 535 | |
[email protected] | 4b559b4d | 2011-04-14 17:37:14 | [diff] [blame] | 536 | } // namespace crypto |