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.
5 #include "base/crypto/symmetric_key.h"
7 #include <winsock2.h> // For htonl.
11 // TODO(wtc): replace scoped_array by std::vector.
12 #include "base/memory/scoped_ptr.h"
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
{
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
) {
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
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
,
53 const void* key_data
, DWORD key_size
,
54 ScopedHCRYPTKEY
* key
) {
55 DCHECK_GT(key_size
, 0);
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
));
65 key_header
->hdr
.bType
= PLAINTEXTKEYBLOB
;
66 key_header
->hdr
.bVersion
= CUR_BLOB_VERSION
;
67 key_header
->hdr
.aiKeyAlg
= alg
;
69 key_header
->cbKeySize
= 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
78 key_header
->hdr
.aiKeyAlg
= CALG_RC2
;
79 flags
|= CRYPT_IPSEC_HMAC_KEY
;
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
);
92 key
->reset(unsafe_key
);
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
) {
105 ALG_ID alg
= GetAESAlgIDForKeySize(key_size_in_bits
);
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
);
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
124 ok
= CryptGenKey(safe_provider
.get(), alg
, CRYPT_EXPORTABLE
,
130 provider
->swap(safe_provider
);
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
) {
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
,
169 ScopedHCRYPTPROV
* provider
,
170 ScopedHCRYPTKEY
* key
,
171 scoped_array
<BYTE
>* raw_key
) {
176 if (!CheckHMACKeySize(key_size_in_bits
, alg
))
179 ScopedHCRYPTPROV safe_provider
;
180 // See comment in GenerateAESKey as to why NULL is acceptable for the
182 BOOL ok
= CryptAcquireContext(safe_provider
.receive(), NULL
, NULL
,
183 PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
);
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());
193 ScopedHCRYPTKEY safe_key
;
194 bool rv
= ImportRawKey(safe_provider
, CALG_HMAC
, random
.get(),
195 key_size_in_bytes
, &safe_key
);
198 provider
->swap(safe_provider
);
199 raw_key
->swap(random
);
202 SecureZeroMemory(random
.get(), key_size_in_bytes
);
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
,
213 ScopedHCRYPTHASH
* hash
) {
214 ScopedHCRYPTHASH safe_hash
;
215 BOOL ok
= CryptCreateHash(provider
, CALG_HMAC
, key
, 0, safe_hash
.receive());
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);
228 hash
->swap(safe_hash
);
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
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
,
240 const std::string
& salt
,
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
250 // U_1 = PRF(P, S || INT (i))
253 // U_c = PRF(P, U_{c-1})
254 ScopedHCRYPTHASH safe_hash
;
255 BOOL ok
= CryptDuplicateHash(hash
, NULL
, 0, safe_hash
.receive());
259 // Iteration U_1: Compute PRF for S.
260 ok
= CryptHashData(safe_hash
, reinterpret_cast<const BYTE
*>(salt
.data()),
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);
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
)
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
) {
285 ok
= CryptDuplicateHash(hash
, NULL
, 0, safe_hash
.receive());
289 ok
= CryptHashData(safe_hash
, &hash_value
[0], hash_size
, 0);
294 ok
= CryptGetHashParam(safe_hash
, HP_HASHVAL
, &hash_value
[0], &size
, 0);
295 if (!ok
|| size
!= hash_size
)
298 for (int i
= 0; i
< hash_size
; ++i
)
299 output_buf
[i
] ^= hash_value
[i
];
307 SymmetricKey::~SymmetricKey() {
308 // TODO(wtc): create a "secure" string type that zeroes itself in the
310 if (!raw_key_
.empty())
311 SecureZeroMemory(const_cast<char *>(raw_key_
.data()), raw_key_
.size());
315 SymmetricKey
* SymmetricKey::GenerateRandomKey(Algorithm algorithm
,
316 size_t key_size_in_bits
) {
317 DCHECK_GE(key_size_in_bits
, 8);
319 ScopedHCRYPTPROV provider
;
323 scoped_array
<BYTE
> raw_key
;
327 ok
= GenerateAESKey(key_size_in_bits
, &provider
, &key
);
330 ok
= GenerateHMACKey(key_size_in_bits
, CALG_SHA1
, &provider
,
340 size_t key_size_in_bytes
= key_size_in_bits
/ 8;
342 key_size_in_bytes
= 0;
344 SymmetricKey
* result
= new SymmetricKey(provider
.release(),
349 SecureZeroMemory(raw_key
.get(), key_size_in_bytes
);
355 SymmetricKey
* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm
,
356 const std::string
& password
,
357 const std::string
& salt
,
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;
370 provider_type
= PROV_RSA_AES
;
371 alg
= GetAESAlgIDForKeySize(key_size_in_bits
);
374 provider_type
= PROV_RSA_FULL
;
381 if (provider_type
== 0 || alg
== 0)
384 ScopedHCRYPTPROV provider
;
385 BOOL ok
= CryptAcquireContext(provider
.receive(), NULL
, NULL
, provider_type
,
386 CRYPT_VERIFYCONTEXT
);
390 // Convert the user password into a key suitable to be fed into the PRF
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
))
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
))
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)
413 // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and stop.
414 size_t dkLen
= key_size_in_bits
/ 8;
417 if ((dkLen
/ hLen
) > 0xFFFFFFFF) {
418 DLOG(ERROR
) << "Derived key too long.";
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
425 size_t L
= (dkLen
+ hLen
- 1) / hLen
;
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)
438 // T_l = F (P, S, c, l)
440 // 4. Concatenate the blocks and extract the first dkLen octets to produce
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
,
447 block_offset
+= hLen
;
450 // Convert the derived key bytes into a key handle for the desired algorithm.
452 if (!ImportRawKey(provider
, alg
, &generated_key
[0], dkLen
, &key
))
455 SymmetricKey
* result
= new SymmetricKey(provider
.release(), key
.release(),
456 &generated_key
[0], dkLen
);
458 SecureZeroMemory(&generated_key
[0], total_generated_size
);
464 SymmetricKey
* SymmetricKey::Import(Algorithm algorithm
,
465 const std::string
& raw_key
) {
466 DWORD provider_type
= 0;
470 provider_type
= PROV_RSA_AES
;
471 alg
= GetAESAlgIDForKeySize(raw_key
.size() * 8);
474 provider_type
= PROV_RSA_FULL
;
481 if (provider_type
== 0 || alg
== 0)
484 ScopedHCRYPTPROV provider
;
485 BOOL ok
= CryptAcquireContext(provider
.receive(), NULL
, NULL
, provider_type
,
486 CRYPT_VERIFYCONTEXT
);
491 if (!ImportRawKey(provider
, alg
, raw_key
.data(), raw_key
.size(), &key
))
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()) {
506 BOOL ok
= CryptExportKey(key_
, 0, PLAINTEXTKEYBLOB
, 0, NULL
, &size
);
510 std::vector
<BYTE
> result(size
);
512 ok
= CryptExportKey(key_
, 0, PLAINTEXTKEYBLOB
, 0, &result
[0], &size
);
516 PlaintextBlobHeader
* header
=
517 reinterpret_cast<PlaintextBlobHeader
*>(&result
[0]);
518 raw_key
->assign(reinterpret_cast<char*>(&result
[sizeof(*header
)]),
521 SecureZeroMemory(&result
[0], size
);
526 SymmetricKey::SymmetricKey(HCRYPTPROV provider
,
528 const void* key_data
, size_t key_size_in_bytes
)
529 : provider_(provider
), key_(key
) {
531 raw_key_
.assign(reinterpret_cast<const char*>(key_data
),