OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 "base/crypto/symmetric_key.h" | |
6 | |
7 #include <winsock2.h> // For htonl. | |
8 | |
9 #include <vector> | |
10 | |
11 // TODO(wtc): replace scoped_array by std::vector. | |
12 #include "base/memory/scoped_ptr.h" | |
13 | |
14 namespace base { | |
15 | |
16 namespace { | |
17 | |
18 // The following is a non-public Microsoft header documented in MSDN under | |
19 // CryptImportKey / CryptExportKey. Following the header is the byte array of | |
20 // the actual plaintext key. | |
21 struct PlaintextBlobHeader { | |
22 BLOBHEADER hdr; | |
23 DWORD cbKeySize; | |
24 }; | |
25 | |
26 // CryptoAPI makes use of three distinct ALG_IDs for AES, rather than just | |
27 // CALG_AES (which exists, but depending on the functions you are calling, may | |
28 // result in function failure, whereas the subtype would succeed). | |
29 ALG_ID GetAESAlgIDForKeySize(size_t key_size_in_bits) { | |
30 // Only AES-128/-192/-256 is supported in CryptoAPI. | |
31 switch (key_size_in_bits) { | |
32 case 128: | |
33 return CALG_AES_128; | |
34 case 192: | |
35 return CALG_AES_192; | |
36 case 256: | |
37 return CALG_AES_256; | |
38 default: | |
39 NOTREACHED(); | |
40 return 0; | |
41 } | |
42 }; | |
43 | |
44 // Imports a raw/plaintext key of |key_size| stored in |*key_data| into a new | |
45 // key created for the specified |provider|. |alg| contains the algorithm of | |
46 // the key being imported. | |
47 // If |key_data| is intended to be used as an HMAC key, then |alg| should be | |
48 // CALG_HMAC. | |
49 // If successful, returns true and stores the imported key in |*key|. | |
50 // TODO(wtc): use this function in hmac_win.cc. | |
51 bool ImportRawKey(HCRYPTPROV provider, | |
52 ALG_ID alg, | |
53 const void* key_data, DWORD key_size, | |
54 ScopedHCRYPTKEY* key) { | |
55 DCHECK_GT(key_size, 0); | |
56 | |
57 DWORD actual_size = 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 = 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_array<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 = key_size_in_bits / 8; | |
188 scoped_array<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 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 = htonl(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_array<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 base | |
OLD | NEW |