OLD | NEW |
| (Empty) |
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 <memory> | |
11 #include <vector> | |
12 | |
13 // TODO(wtc): replace scoped_array by std::vector. | |
14 #include "base/sys_byteorder.h" | |
15 | |
16 namespace crypto { | |
17 | |
18 namespace { | |
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. | |
23 struct 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). | |
31 ALG_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. | |
53 bool 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|. | |
102 bool 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. | |
140 bool 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|. | |
170 bool GenerateHMACKey(size_t key_size_in_bits, | |
171 ALG_ID alg, | |
172 ScopedHCRYPTPROV* provider, | |
173 ScopedHCRYPTKEY* key, | |
174 std::unique_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 std::unique_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. | |
213 bool 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. | |
241 bool 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 | |
310 SymmetricKey::~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 | |
318 SymmetricKey* 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 std::unique_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 | |
358 SymmetricKey* 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 | |
467 SymmetricKey* 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 | |
501 bool 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 | |
529 SymmetricKey::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 | |
OLD | NEW |