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 <vector>
11
12// TODO(wtc): replace scoped_array by std::vector.
13#include "base/memory/scoped_ptr.h"
14#include "base/sys_byteorder.h"
15
16namespace crypto {
17
18namespace {
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.
23struct 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).
31ALG_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.
53bool 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|.
102bool 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.
140bool 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|.
170bool GenerateHMACKey(size_t key_size_in_bits,
171                     ALG_ID alg,
172                     ScopedHCRYPTPROV* provider,
173                     ScopedHCRYPTKEY* key,
174                     scoped_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  scoped_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.
213bool 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.
241bool 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
310SymmetricKey::~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
318SymmetricKey* 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  scoped_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
358SymmetricKey* 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
467SymmetricKey* 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
501bool 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
529SymmetricKey::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
540