Home | History | Annotate | Download | only in crypto
      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 
     16 namespace crypto {
     17 
     18 namespace {
     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.
     23 struct 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).
     31 ALG_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.
     53 bool 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|.
    102 bool 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.
    140 bool 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|.
    170 bool 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.
    213 bool 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.
    241 bool 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 
    310 SymmetricKey::~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
    318 SymmetricKey* 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
    358 SymmetricKey* 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
    467 SymmetricKey* 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 
    501 bool 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 
    529 SymmetricKey::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