cros: Update SAML flow.
[chromium-blink-merge.git] / crypto / symmetric_key_win.cc
blobb3d65f6613760c02c9af90612516afa80c53cb5b
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.
5 #include "crypto/symmetric_key.h"
7 #include <vector>
9 // TODO(wtc): replace scoped_array by std::vector.
10 #include "base/memory/scoped_ptr.h"
11 #include "base/sys_byteorder.h"
13 namespace crypto {
15 namespace {
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;
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;
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.
50 bool ImportRawKey(HCRYPTPROV provider,
51 ALG_ID alg,
52 const void* key_data, size_t key_size,
53 ScopedHCRYPTKEY* key) {
54 DCHECK_GT(key_size, 0);
56 DWORD actual_size =
57 static_cast<DWORD>(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));
65 key_header->hdr.bType = PLAINTEXTKEYBLOB;
66 key_header->hdr.bVersion = CUR_BLOB_VERSION;
67 key_header->hdr.aiKeyAlg = alg;
69 key_header->cbKeySize = static_cast<DWORD>(key_size);
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;
82 BOOL ok =
83 CryptImportKey(provider, actual_key, actual_size, 0, flags, &unsafe_key);
85 // Clean up the temporary copy of key, regardless of whether it was imported
86 // sucessfully or not.
87 SecureZeroMemory(actual_key, actual_size);
89 if (!ok)
90 return false;
92 key->reset(unsafe_key);
93 return true;
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);
105 ALG_ID alg = GetAESAlgIDForKeySize(key_size_in_bits);
106 if (alg == 0)
107 return false;
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;
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;
129 key->swap(safe_key);
130 provider->swap(safe_provider);
132 return true;
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;
153 if (hash_size == 0)
154 return false;
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);
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_ptr<BYTE[]>* raw_key) {
172 DCHECK(provider);
173 DCHECK(key);
174 DCHECK(raw_key);
176 if (!CheckHMACKeySize(key_size_in_bits, alg))
177 return false;
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;
187 DWORD key_size_in_bytes = static_cast<DWORD>(key_size_in_bits / 8);
188 scoped_ptr<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;
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);
202 SecureZeroMemory(random.get(), key_size_in_bytes);
203 return rv;
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;
219 HMAC_INFO hmac_info;
220 memset(&hmac_info, 0, sizeof(hmac_info));
221 hmac_info.HashAlgid = hash_alg;
223 ok = CryptSetHashParam(safe_hash, HP_HMAC_INFO,
224 reinterpret_cast<const BYTE*>(&hmac_info), 0);
225 if (!ok)
226 return false;
228 hash->swap(safe_hash);
229 return true;
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;
259 // Iteration U_1: Compute PRF for S.
260 ok = CryptHashData(safe_hash, reinterpret_cast<const BYTE*>(salt.data()),
261 static_cast<DWORD>(salt.size()), 0);
262 if (!ok)
263 return false;
265 // Iteration U_1: and append (big-endian) INT (i).
266 uint32 big_endian_block_index = base::HostToNet32(block_index);
267 ok = CryptHashData(safe_hash,
268 reinterpret_cast<BYTE*>(&big_endian_block_index),
269 sizeof(big_endian_block_index), 0);
271 std::vector<BYTE> hash_value(hash_size);
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;
278 memcpy(output_buf, &hash_value[0], hash_size);
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;
289 ok = CryptHashData(safe_hash, &hash_value[0], hash_size, 0);
290 if (!ok)
291 return false;
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;
298 for (int i = 0; i < hash_size; ++i)
299 output_buf[i] ^= hash_value[i];
302 return true;
305 } // namespace
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());
314 // static
315 SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm,
316 size_t key_size_in_bits) {
317 DCHECK_GE(key_size_in_bits, 8);
319 ScopedHCRYPTPROV provider;
320 ScopedHCRYPTKEY key;
322 bool ok = false;
323 scoped_ptr<BYTE[]> raw_key;
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;
335 if (!ok) {
336 NOTREACHED();
337 return NULL;
340 size_t key_size_in_bytes = key_size_in_bits / 8;
341 if (raw_key == NULL)
342 key_size_in_bytes = 0;
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);
351 return result;
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.
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;
381 if (provider_type == 0 || alg == 0)
382 return NULL;
384 ScopedHCRYPTPROV provider;
385 BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type,
386 CRYPT_VERIFYCONTEXT);
387 if (!ok)
388 return NULL;
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;
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;
406 DWORD hLen = 0;
407 DWORD param_size = sizeof(hLen);
408 ok = CryptGetHashParam(prf, HP_HASHSIZE,
409 reinterpret_cast<BYTE*>(&hLen), &param_size, 0);
410 if (!ok || hLen == 0)
411 return NULL;
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);
417 if ((dkLen / hLen) > 0xFFFFFFFF) {
418 DLOG(ERROR) << "Derived key too long.";
419 return NULL;
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);
428 size_t total_generated_size = L * hLen;
429 std::vector<BYTE> generated_key(total_generated_size);
430 BYTE* block_offset = &generated_key[0];
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;
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;
455 SymmetricKey* result = new SymmetricKey(provider.release(), key.release(),
456 &generated_key[0], dkLen);
458 SecureZeroMemory(&generated_key[0], total_generated_size);
460 return result;
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;
481 if (provider_type == 0 || alg == 0)
482 return NULL;
484 ScopedHCRYPTPROV provider;
485 BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type,
486 CRYPT_VERIFYCONTEXT);
487 if (!ok)
488 return NULL;
490 ScopedHCRYPTKEY key;
491 if (!ImportRawKey(provider, alg, raw_key.data(), raw_key.size(), &key))
492 return NULL;
494 return new SymmetricKey(provider.release(), key.release(),
495 raw_key.data(), raw_key.size());
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;
505 DWORD size = 0;
506 BOOL ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, NULL, &size);
507 if (!ok)
508 return false;
510 std::vector<BYTE> result(size);
512 ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, &result[0], &size);
513 if (!ok)
514 return false;
516 PlaintextBlobHeader* header =
517 reinterpret_cast<PlaintextBlobHeader*>(&result[0]);
518 raw_key->assign(reinterpret_cast<char*>(&result[sizeof(*header)]),
519 header->cbKeySize);
521 SecureZeroMemory(&result[0], size);
523 return true;
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);
536 } // namespace crypto