blob: 87e0bc3a4597f66b8d9bc1956b52cebf53916774 [file] [log] [blame]
[email protected]4a0141b2012-03-27 01:15:301// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]41c78fa2010-03-22 20:08:412// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]4b559b4d2011-04-14 17:37:145#include "crypto/symmetric_key.h"
[email protected]41c78fa2010-03-22 20:08:416
[email protected]692033a2010-04-09 18:40:507#include <winsock2.h> // For htonl.
8
9#include <vector>
10
11// TODO(wtc): replace scoped_array by std::vector.
[email protected]3b63f8f42011-03-28 01:54:1512#include "base/memory/scoped_ptr.h"
[email protected]692033a2010-04-09 18:40:5013
[email protected]4b559b4d2011-04-14 17:37:1414namespace crypto {
[email protected]41c78fa2010-03-22 20:08:4115
[email protected]692033a2010-04-09 18:40:5016namespace {
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.
21struct 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).
29ALG_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.
[email protected]08ce4d42010-10-21 00:31:1951bool ImportRawKey(HCRYPTPROV provider,
52 ALG_ID alg,
[email protected]4a0141b2012-03-27 01:15:3053 const void* key_data, size_t key_size,
[email protected]08ce4d42010-10-21 00:31:1954 ScopedHCRYPTKEY* key) {
[email protected]692033a2010-04-09 18:40:5055 DCHECK_GT(key_size, 0);
56
[email protected]4a0141b2012-03-27 01:15:3057 DWORD actual_size =
58 static_cast<DWORD>(sizeof(PlaintextBlobHeader) + key_size);
[email protected]692033a2010-04-09 18:40:5059 std::vector<BYTE> tmp_data(actual_size);
60 BYTE* actual_key = &tmp_data[0];
61 memcpy(actual_key + sizeof(PlaintextBlobHeader), key_data, key_size);
62 PlaintextBlobHeader* key_header =
63 reinterpret_cast<PlaintextBlobHeader*>(actual_key);
64 memset(key_header, 0, sizeof(PlaintextBlobHeader));
65
66 key_header->hdr.bType = PLAINTEXTKEYBLOB;
67 key_header->hdr.bVersion = CUR_BLOB_VERSION;
68 key_header->hdr.aiKeyAlg = alg;
69
[email protected]4a0141b2012-03-27 01:15:3070 key_header->cbKeySize = static_cast<DWORD>(key_size);
[email protected]692033a2010-04-09 18:40:5071
72 HCRYPTKEY unsafe_key = NULL;
73 DWORD flags = CRYPT_EXPORTABLE;
74 if (alg == CALG_HMAC) {
75 // Though it may appear odd that IPSEC and RC2 are being used, this is
76 // done in accordance with Microsoft's FIPS 140-2 Security Policy for the
77 // RSA Enhanced Provider, as the approved means of using arbitrary HMAC
78 // key material.
79 key_header->hdr.aiKeyAlg = CALG_RC2;
80 flags |= CRYPT_IPSEC_HMAC_KEY;
81 }
82
[email protected]08ce4d42010-10-21 00:31:1983 BOOL ok =
84 CryptImportKey(provider, actual_key, actual_size, 0, flags, &unsafe_key);
[email protected]692033a2010-04-09 18:40:5085
86 // Clean up the temporary copy of key, regardless of whether it was imported
87 // sucessfully or not.
88 SecureZeroMemory(actual_key, actual_size);
89
90 if (!ok)
91 return false;
92
93 key->reset(unsafe_key);
94 return true;
95}
96
97// Attempts to generate a random AES key of |key_size_in_bits|. Returns true
98// if generation is successful, storing the generated key in |*key| and the
99// key provider (CSP) in |*provider|.
[email protected]08ce4d42010-10-21 00:31:19100bool GenerateAESKey(size_t key_size_in_bits,
101 ScopedHCRYPTPROV* provider,
[email protected]692033a2010-04-09 18:40:50102 ScopedHCRYPTKEY* key) {
103 DCHECK(provider);
104 DCHECK(key);
105
106 ALG_ID alg = GetAESAlgIDForKeySize(key_size_in_bits);
107 if (alg == 0)
108 return false;
109
110 ScopedHCRYPTPROV safe_provider;
111 // Note: The only time NULL is safe to be passed as pszContainer is when
112 // dwFlags contains CRYPT_VERIFYCONTEXT, as all keys generated and/or used
113 // will be treated as ephemeral keys and not persisted.
114 BOOL ok = CryptAcquireContext(safe_provider.receive(), NULL, NULL,
115 PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
116 if (!ok)
117 return false;
118
119 ScopedHCRYPTKEY safe_key;
120 // In the FIPS 140-2 Security Policy for CAPI on XP/Vista+, Microsoft notes
121 // that CryptGenKey makes use of the same functionality exposed via
122 // CryptGenRandom. The reason this is being used, as opposed to
123 // CryptGenRandom and CryptImportKey is for compliance with the security
124 // policy
125 ok = CryptGenKey(safe_provider.get(), alg, CRYPT_EXPORTABLE,
126 safe_key.receive());
127 if (!ok)
128 return false;
129
130 key->swap(safe_key);
131 provider->swap(safe_provider);
132
133 return true;
134}
135
136// Returns true if the HMAC key size meets the requirement of FIPS 198
137// Section 3. |alg| is the hash function used in the HMAC.
138bool CheckHMACKeySize(size_t key_size_in_bits, ALG_ID alg) {
139 DWORD hash_size = 0;
140 switch (alg) {
141 case CALG_SHA1:
142 hash_size = 20;
143 break;
144 case CALG_SHA_256:
145 hash_size = 32;
146 break;
147 case CALG_SHA_384:
148 hash_size = 48;
149 break;
150 case CALG_SHA_512:
151 hash_size = 64;
152 break;
153 }
154 if (hash_size == 0)
155 return false;
156
157 // An HMAC key must be >= L/2, where L is the output size of the hash
158 // function being used.
159 return (key_size_in_bits >= (hash_size / 2 * 8) &&
160 (key_size_in_bits % 8) == 0);
161}
162
163// Attempts to generate a random, |key_size_in_bits|-long HMAC key, for use
164// with the hash function |alg|.
165// |key_size_in_bits| must be >= 1/2 the hash size of |alg| for security.
166// Returns true if generation is successful, storing the generated key in
167// |*key| and the key provider (CSP) in |*provider|.
[email protected]08ce4d42010-10-21 00:31:19168bool GenerateHMACKey(size_t key_size_in_bits,
169 ALG_ID alg,
170 ScopedHCRYPTPROV* provider,
171 ScopedHCRYPTKEY* key,
[email protected]692033a2010-04-09 18:40:50172 scoped_array<BYTE>* raw_key) {
173 DCHECK(provider);
174 DCHECK(key);
175 DCHECK(raw_key);
176
177 if (!CheckHMACKeySize(key_size_in_bits, alg))
178 return false;
179
180 ScopedHCRYPTPROV safe_provider;
181 // See comment in GenerateAESKey as to why NULL is acceptable for the
182 // container name.
183 BOOL ok = CryptAcquireContext(safe_provider.receive(), NULL, NULL,
184 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
185 if (!ok)
186 return false;
187
[email protected]4a0141b2012-03-27 01:15:30188 DWORD key_size_in_bytes = static_cast<DWORD>(key_size_in_bits / 8);
[email protected]692033a2010-04-09 18:40:50189 scoped_array<BYTE> random(new BYTE[key_size_in_bytes]);
190 ok = CryptGenRandom(safe_provider, key_size_in_bytes, random.get());
191 if (!ok)
192 return false;
193
194 ScopedHCRYPTKEY safe_key;
195 bool rv = ImportRawKey(safe_provider, CALG_HMAC, random.get(),
196 key_size_in_bytes, &safe_key);
197 if (rv) {
198 key->swap(safe_key);
199 provider->swap(safe_provider);
200 raw_key->swap(random);
201 }
202
203 SecureZeroMemory(random.get(), key_size_in_bytes);
204 return rv;
205}
206
207// Attempts to create an HMAC hash instance using the specified |provider|
208// and |key|. The inner hash function will be |hash_alg|. If successful,
209// returns true and stores the hash in |*hash|.
210// TODO(wtc): use this function in hmac_win.cc.
[email protected]08ce4d42010-10-21 00:31:19211bool CreateHMACHash(HCRYPTPROV provider,
212 HCRYPTKEY key,
213 ALG_ID hash_alg,
[email protected]692033a2010-04-09 18:40:50214 ScopedHCRYPTHASH* hash) {
215 ScopedHCRYPTHASH safe_hash;
216 BOOL ok = CryptCreateHash(provider, CALG_HMAC, key, 0, safe_hash.receive());
217 if (!ok)
218 return false;
219
220 HMAC_INFO hmac_info;
221 memset(&hmac_info, 0, sizeof(hmac_info));
222 hmac_info.HashAlgid = hash_alg;
223
224 ok = CryptSetHashParam(safe_hash, HP_HMAC_INFO,
225 reinterpret_cast<const BYTE*>(&hmac_info), 0);
226 if (!ok)
227 return false;
228
229 hash->swap(safe_hash);
230 return true;
231}
232
233// Computes a block of the derived key using the PBKDF2 function F for the
234// specified |block_index| using the PRF |hash|, writing the output to
235// |output_buf|.
236// |output_buf| must have enough space to accomodate the output of the PRF
237// specified by |hash|.
238// Returns true if the block was successfully computed.
[email protected]08ce4d42010-10-21 00:31:19239bool ComputePBKDF2Block(HCRYPTHASH hash,
240 DWORD hash_size,
241 const std::string& salt,
242 size_t iterations,
243 uint32 block_index,
244 BYTE* output_buf) {
[email protected]692033a2010-04-09 18:40:50245 // From RFC 2898:
246 // 3. <snip> The function F is defined as the exclusive-or sum of the first
247 // c iterates of the underlying pseudorandom function PRF applied to the
248 // password P and the concatenation of the salt S and the block index i:
249 // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
250 // where
251 // U_1 = PRF(P, S || INT (i))
252 // U_2 = PRF(P, U_1)
253 // ...
254 // U_c = PRF(P, U_{c-1})
255 ScopedHCRYPTHASH safe_hash;
256 BOOL ok = CryptDuplicateHash(hash, NULL, 0, safe_hash.receive());
257 if (!ok)
258 return false;
259
260 // Iteration U_1: Compute PRF for S.
[email protected]08ce4d42010-10-21 00:31:19261 ok = CryptHashData(safe_hash, reinterpret_cast<const BYTE*>(salt.data()),
[email protected]4a0141b2012-03-27 01:15:30262 static_cast<DWORD>(salt.size()), 0);
[email protected]692033a2010-04-09 18:40:50263 if (!ok)
264 return false;
265
266 // Iteration U_1: and append (big-endian) INT (i).
267 uint32 big_endian_block_index = htonl(block_index);
268 ok = CryptHashData(safe_hash,
269 reinterpret_cast<BYTE*>(&big_endian_block_index),
270 sizeof(big_endian_block_index), 0);
271
272 std::vector<BYTE> hash_value(hash_size);
273
274 DWORD size = hash_size;
275 ok = CryptGetHashParam(safe_hash, HP_HASHVAL, &hash_value[0], &size, 0);
276 if (!ok || size != hash_size)
277 return false;
278
279 memcpy(output_buf, &hash_value[0], hash_size);
280
281 // Iteration 2 - c: Compute U_{iteration} by applying the PRF to
282 // U_{iteration - 1}, then xor the resultant hash with |output|, which
283 // contains U_1 ^ U_2 ^ ... ^ U_{iteration - 1}.
284 for (size_t iteration = 2; iteration <= iterations; ++iteration) {
285 safe_hash.reset();
286 ok = CryptDuplicateHash(hash, NULL, 0, safe_hash.receive());
287 if (!ok)
288 return false;
289
290 ok = CryptHashData(safe_hash, &hash_value[0], hash_size, 0);
291 if (!ok)
292 return false;
293
294 size = hash_size;
295 ok = CryptGetHashParam(safe_hash, HP_HASHVAL, &hash_value[0], &size, 0);
296 if (!ok || size != hash_size)
297 return false;
298
299 for (int i = 0; i < hash_size; ++i)
300 output_buf[i] ^= hash_value[i];
301 }
302
303 return true;
304}
305
306} // namespace
307
308SymmetricKey::~SymmetricKey() {
309 // TODO(wtc): create a "secure" string type that zeroes itself in the
310 // destructor.
311 if (!raw_key_.empty())
312 SecureZeroMemory(const_cast<char *>(raw_key_.data()), raw_key_.size());
313}
[email protected]39422e32010-03-25 19:13:00314
315// static
[email protected]108118232010-03-29 18:22:24316SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm,
317 size_t key_size_in_bits) {
[email protected]692033a2010-04-09 18:40:50318 DCHECK_GE(key_size_in_bits, 8);
319
320 ScopedHCRYPTPROV provider;
321 ScopedHCRYPTKEY key;
322
323 bool ok = false;
324 scoped_array<BYTE> raw_key;
325
326 switch (algorithm) {
327 case AES:
328 ok = GenerateAESKey(key_size_in_bits, &provider, &key);
329 break;
330 case HMAC_SHA1:
331 ok = GenerateHMACKey(key_size_in_bits, CALG_SHA1, &provider,
332 &key, &raw_key);
333 break;
334 }
335
336 if (!ok) {
337 NOTREACHED();
338 return NULL;
339 }
340
341 size_t key_size_in_bytes = key_size_in_bits / 8;
342 if (raw_key == NULL)
343 key_size_in_bytes = 0;
344
345 SymmetricKey* result = new SymmetricKey(provider.release(),
346 key.release(),
347 raw_key.get(),
348 key_size_in_bytes);
349 if (raw_key != NULL)
350 SecureZeroMemory(raw_key.get(), key_size_in_bytes);
351
352 return result;
[email protected]39422e32010-03-25 19:13:00353}
354
355// static
356SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm,
357 const std::string& password,
358 const std::string& salt,
359 size_t iterations,
[email protected]108118232010-03-29 18:22:24360 size_t key_size_in_bits) {
[email protected]692033a2010-04-09 18:40:50361 // CryptoAPI lacks routines to perform PBKDF2 derivation as specified
362 // in RFC 2898, so it must be manually implemented. Only HMAC-SHA1 is
363 // supported as the PRF.
364
365 // While not used until the end, sanity-check the input before proceeding
366 // with the expensive computation.
367 DWORD provider_type = 0;
368 ALG_ID alg = 0;
369 switch (algorithm) {
370 case AES:
371 provider_type = PROV_RSA_AES;
372 alg = GetAESAlgIDForKeySize(key_size_in_bits);
373 break;
374 case HMAC_SHA1:
375 provider_type = PROV_RSA_FULL;
376 alg = CALG_HMAC;
377 break;
378 default:
379 NOTREACHED();
380 break;
381 }
382 if (provider_type == 0 || alg == 0)
383 return NULL;
384
385 ScopedHCRYPTPROV provider;
386 BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type,
387 CRYPT_VERIFYCONTEXT);
388 if (!ok)
389 return NULL;
390
391 // Convert the user password into a key suitable to be fed into the PRF
392 // function.
393 ScopedHCRYPTKEY password_as_key;
394 BYTE* password_as_bytes =
395 const_cast<BYTE*>(reinterpret_cast<const BYTE*>(password.data()));
396 if (!ImportRawKey(provider, CALG_HMAC, password_as_bytes,
397 password.size(), &password_as_key))
398 return NULL;
399
400 // Configure the PRF function. Only HMAC variants are supported, with the
401 // only hash function supported being SHA1.
402 // TODO(rsleevi): Support SHA-256 on XP SP3+.
403 ScopedHCRYPTHASH prf;
404 if (!CreateHMACHash(provider, password_as_key, CALG_SHA1, &prf))
405 return NULL;
406
407 DWORD hLen = 0;
408 DWORD param_size = sizeof(hLen);
409 ok = CryptGetHashParam(prf, HP_HASHSIZE,
410 reinterpret_cast<BYTE*>(&hLen), &param_size, 0);
411 if (!ok || hLen == 0)
412 return NULL;
413
414 // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and stop.
415 size_t dkLen = key_size_in_bits / 8;
416 DCHECK_GT(dkLen, 0);
417
418 if ((dkLen / hLen) > 0xFFFFFFFF) {
419 DLOG(ERROR) << "Derived key too long.";
420 return NULL;
421 }
422
423 // 2. Let l be the number of hLen-octet blocks in the derived key,
424 // rounding up, and let r be the number of octets in the last
425 // block:
426 size_t L = (dkLen + hLen - 1) / hLen;
427 DCHECK_GT(L, 0);
428
429 size_t total_generated_size = L * hLen;
430 std::vector<BYTE> generated_key(total_generated_size);
431 BYTE* block_offset = &generated_key[0];
432
433 // 3. For each block of the derived key apply the function F defined below
434 // to the password P, the salt S, the iteration count c, and the block
435 // index to compute the block:
436 // T_1 = F (P, S, c, 1)
437 // T_2 = F (P, S, c, 2)
438 // ...
439 // T_l = F (P, S, c, l)
440 // <snip>
441 // 4. Concatenate the blocks and extract the first dkLen octets to produce
442 // a derived key DK:
443 // DK = T_1 || T_2 || ... || T_l<0..r-1>
444 for (uint32 block_index = 1; block_index <= L; ++block_index) {
[email protected]08ce4d42010-10-21 00:31:19445 if (!ComputePBKDF2Block(prf, hLen, salt, iterations, block_index,
446 block_offset))
[email protected]692033a2010-04-09 18:40:50447 return NULL;
[email protected]692033a2010-04-09 18:40:50448 block_offset += hLen;
449 }
450
451 // Convert the derived key bytes into a key handle for the desired algorithm.
452 ScopedHCRYPTKEY key;
453 if (!ImportRawKey(provider, alg, &generated_key[0], dkLen, &key))
454 return NULL;
455
456 SymmetricKey* result = new SymmetricKey(provider.release(), key.release(),
457 &generated_key[0], dkLen);
458
459 SecureZeroMemory(&generated_key[0], total_generated_size);
460
461 return result;
462}
463
464// static
465SymmetricKey* SymmetricKey::Import(Algorithm algorithm,
[email protected]896200b32010-07-20 19:21:18466 const std::string& raw_key) {
[email protected]c6e86ae2010-09-14 03:40:40467 DWORD provider_type = 0;
468 ALG_ID alg = 0;
469 switch (algorithm) {
470 case AES:
471 provider_type = PROV_RSA_AES;
472 alg = GetAESAlgIDForKeySize(raw_key.size() * 8);
473 break;
474 case HMAC_SHA1:
475 provider_type = PROV_RSA_FULL;
476 alg = CALG_HMAC;
477 break;
478 default:
479 NOTREACHED();
480 break;
481 }
482 if (provider_type == 0 || alg == 0)
483 return NULL;
[email protected]692033a2010-04-09 18:40:50484
485 ScopedHCRYPTPROV provider;
[email protected]08ce4d42010-10-21 00:31:19486 BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type,
487 CRYPT_VERIFYCONTEXT);
[email protected]692033a2010-04-09 18:40:50488 if (!ok)
489 return NULL;
490
[email protected]692033a2010-04-09 18:40:50491 ScopedHCRYPTKEY key;
[email protected]896200b32010-07-20 19:21:18492 if (!ImportRawKey(provider, alg, raw_key.data(), raw_key.size(), &key))
[email protected]692033a2010-04-09 18:40:50493 return NULL;
494
495 return new SymmetricKey(provider.release(), key.release(),
[email protected]896200b32010-07-20 19:21:18496 raw_key.data(), raw_key.size());
[email protected]39422e32010-03-25 19:13:00497}
498
[email protected]41c78fa2010-03-22 20:08:41499bool SymmetricKey::GetRawKey(std::string* raw_key) {
[email protected]692033a2010-04-09 18:40:50500 // Short circuit for when the key was supplied to the constructor.
501 if (!raw_key_.empty()) {
502 *raw_key = raw_key_;
503 return true;
504 }
505
506 DWORD size = 0;
[email protected]08ce4d42010-10-21 00:31:19507 BOOL ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, NULL, &size);
[email protected]692033a2010-04-09 18:40:50508 if (!ok)
509 return false;
510
511 std::vector<BYTE> result(size);
512
[email protected]08ce4d42010-10-21 00:31:19513 ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, &result[0], &size);
[email protected]692033a2010-04-09 18:40:50514 if (!ok)
515 return false;
516
517 PlaintextBlobHeader* header =
518 reinterpret_cast<PlaintextBlobHeader*>(&result[0]);
519 raw_key->assign(reinterpret_cast<char*>(&result[sizeof(*header)]),
520 header->cbKeySize);
521
522 SecureZeroMemory(&result[0], size);
523
524 return true;
525}
526
[email protected]08ce4d42010-10-21 00:31:19527SymmetricKey::SymmetricKey(HCRYPTPROV provider,
528 HCRYPTKEY key,
[email protected]692033a2010-04-09 18:40:50529 const void* key_data, size_t key_size_in_bytes)
530 : provider_(provider), key_(key) {
531 if (key_data) {
532 raw_key_.assign(reinterpret_cast<const char*>(key_data),
533 key_size_in_bytes);
534 }
[email protected]41c78fa2010-03-22 20:08:41535}
536
[email protected]4b559b4d2011-04-14 17:37:14537} // namespace crypto