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 <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 
     13 namespace crypto {
     14 
     15 namespace {
     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.
     20 struct 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).
     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;
     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.
     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);
     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|.
     99 bool 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.
    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;
    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|.
    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);
    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.
    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;
    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.
    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;
    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 
    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());
    312 }
    313 
    314 // static
    315 SymmetricKey* 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
    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.
    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
    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;
    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 
    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;
    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 
    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);
    533   }
    534 }
    535 
    536 }  // namespace crypto
    537