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 <vector>
8
9// TODO(wtc): replace scoped_array by std::vector.
10#include "base/memory/scoped_ptr.h"
11#include "base/sys_byteorder.h"
12
13namespace crypto {
14
15namespace {
16
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.
20struct PlaintextBlobHeader {
21  BLOBHEADER hdr;
22  DWORD cbKeySize;
23};
24
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).
28ALG_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;
40  }
41};
42
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.
50bool 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);
55
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));
64
65  key_header->hdr.bType = PLAINTEXTKEYBLOB;
66  key_header->hdr.bVersion = CUR_BLOB_VERSION;
67  key_header->hdr.aiKeyAlg = alg;
68
69  key_header->cbKeySize = static_cast<DWORD>(key_size);
70
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;
80  }
81
82  BOOL ok =
83      CryptImportKey(provider, actual_key, actual_size, 0, flags, &unsafe_key);
84
85  // Clean up the temporary copy of key, regardless of whether it was imported
86  // sucessfully or not.
87  SecureZeroMemory(actual_key, actual_size);
88
89  if (!ok)
90    return false;
91
92  key->reset(unsafe_key);
93  return true;
94}
95
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|.
99bool GenerateAESKey(size_t key_size_in_bits,
100                    ScopedHCRYPTPROV* provider,
101                    ScopedHCRYPTKEY* key) {
102  DCHECK(provider);
103  DCHECK(key);
104
105  ALG_ID alg = GetAESAlgIDForKeySize(key_size_in_bits);
106  if (alg == 0)
107    return false;
108
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;
117
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;
128
129  key->swap(safe_key);
130  provider->swap(safe_provider);
131
132  return true;
133}
134
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.
137bool 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;
152  }
153  if (hash_size == 0)
154    return false;
155
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);
160}
161
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|.
167bool 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);
175
176  if (!CheckHMACKeySize(key_size_in_bits, alg))
177    return false;
178
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;
186
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;
192
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);
200  }
201
202  SecureZeroMemory(random.get(), key_size_in_bytes);
203  return rv;
204}
205
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.
210bool 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;
218
219  HMAC_INFO hmac_info;
220  memset(&hmac_info, 0, sizeof(hmac_info));
221  hmac_info.HashAlgid = hash_alg;
222
223  ok = CryptSetHashParam(safe_hash, HP_HMAC_INFO,
224                         reinterpret_cast<const BYTE*>(&hmac_info), 0);
225  if (!ok)
226    return false;
227
228  hash->swap(safe_hash);
229  return true;
230}
231
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.
238bool 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;
258
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;
264
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);
270
271  std::vector<BYTE> hash_value(hash_size);
272
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;
277
278  memcpy(output_buf, &hash_value[0], hash_size);
279
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;
288
289    ok = CryptHashData(safe_hash, &hash_value[0], hash_size, 0);
290    if (!ok)
291      return false;
292
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;
297
298    for (int i = 0; i < hash_size; ++i)
299      output_buf[i] ^= hash_value[i];
300  }
301
302  return true;
303}
304
305}  // namespace
306
307SymmetricKey::~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());
312}
313
314// static
315SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm,
316                                              size_t key_size_in_bits) {
317  DCHECK_GE(key_size_in_bits, 8);
318
319  ScopedHCRYPTPROV provider;
320  ScopedHCRYPTKEY key;
321
322  bool ok = false;
323  scoped_ptr<BYTE[]> raw_key;
324
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;
333  }
334
335  if (!ok) {
336    NOTREACHED();
337    return NULL;
338  }
339
340  size_t key_size_in_bytes = key_size_in_bits / 8;
341  if (raw_key == NULL)
342    key_size_in_bytes = 0;
343
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);
350
351  return result;
352}
353
354// static
355SymmetricKey* 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.
363
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;
380  }
381  if (provider_type == 0 || alg == 0)
382    return NULL;
383
384  ScopedHCRYPTPROV provider;
385  BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type,
386                                CRYPT_VERIFYCONTEXT);
387  if (!ok)
388    return NULL;
389
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;
398
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;
405
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;
412
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);
416
417  if ((dkLen / hLen) > 0xFFFFFFFF) {
418    DLOG(ERROR) << "Derived key too long.";
419    return NULL;
420  }
421
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);
427
428  size_t total_generated_size = L * hLen;
429  std::vector<BYTE> generated_key(total_generated_size);
430  BYTE* block_offset = &generated_key[0];
431
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;
448  }
449
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;
454
455  SymmetricKey* result = new SymmetricKey(provider.release(), key.release(),
456                                          &generated_key[0], dkLen);
457
458  SecureZeroMemory(&generated_key[0], total_generated_size);
459
460  return result;
461}
462
463// static
464SymmetricKey* 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;
480  }
481  if (provider_type == 0 || alg == 0)
482    return NULL;
483
484  ScopedHCRYPTPROV provider;
485  BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type,
486                                CRYPT_VERIFYCONTEXT);
487  if (!ok)
488    return NULL;
489
490  ScopedHCRYPTKEY key;
491  if (!ImportRawKey(provider, alg, raw_key.data(), raw_key.size(), &key))
492    return NULL;
493
494  return new SymmetricKey(provider.release(), key.release(),
495                          raw_key.data(), raw_key.size());
496}
497
498bool 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;
503  }
504
505  DWORD size = 0;
506  BOOL ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, NULL, &size);
507  if (!ok)
508    return false;
509
510  std::vector<BYTE> result(size);
511
512  ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, &result[0], &size);
513  if (!ok)
514    return false;
515
516  PlaintextBlobHeader* header =
517      reinterpret_cast<PlaintextBlobHeader*>(&result[0]);
518  raw_key->assign(reinterpret_cast<char*>(&result[sizeof(*header)]),
519                  header->cbKeySize);
520
521  SecureZeroMemory(&result[0], size);
522
523  return true;
524}
525
526SymmetricKey::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);
533  }
534}
535
536}  // namespace crypto
537