Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(27)

Side by Side Diff: crypto/symmetric_key_win.cc

Issue 1924093006: Removing crypto/third_party/nss/ and removing crypto _win files (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove unused sources. Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « crypto/symmetric_key_openssl.cc ('k') | crypto/third_party/nss/LICENSE » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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), &param_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
OLDNEW
« no previous file with comments | « crypto/symmetric_key_openssl.cc ('k') | crypto/third_party/nss/LICENSE » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698