Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2006-2008 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 "base/hmac.h"
      6 
      7 #include <windows.h>
      8 #include <wincrypt.h>
      9 
     10 #include <algorithm>
     11 #include <vector>
     12 
     13 #include "base/logging.h"
     14 
     15 namespace base {
     16 
     17 struct HMACPlatformData {
     18   // Windows Crypt API resources.
     19   HCRYPTPROV provider_;
     20   HCRYPTHASH hash_;
     21   HCRYPTKEY hkey_;
     22 };
     23 
     24 HMAC::HMAC(HashAlgorithm hash_alg)
     25     : hash_alg_(hash_alg), plat_(new HMACPlatformData()) {
     26   // Only SHA-1 digest is supported now.
     27   DCHECK(hash_alg_ == SHA1);
     28 }
     29 
     30 bool HMAC::Init(const unsigned char *key, int key_length) {
     31   if (plat_->provider_ || plat_->hkey_) {
     32     // Init must not be called more than once on the same HMAC object.
     33     NOTREACHED();
     34     return false;
     35   }
     36 
     37   if (!CryptAcquireContext(&plat_->provider_, NULL, NULL,
     38                            PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
     39     NOTREACHED();
     40     plat_->provider_ = NULL;
     41     return false;
     42   }
     43 
     44   // This code doesn't work on Win2k because PLAINTEXTKEYBLOB and
     45   // CRYPT_IPSEC_HMAC_KEY are not supported on Windows 2000.  PLAINTEXTKEYBLOB
     46   // allows the import of an unencrypted key.  For Win2k support, a cubmbersome
     47   // exponent-of-one key procedure must be used:
     48   //     http://support.microsoft.com/kb/228786/en-us
     49   // CRYPT_IPSEC_HMAC_KEY allows keys longer than 16 bytes.
     50 
     51   struct KeyBlob {
     52     BLOBHEADER header;
     53     DWORD key_size;
     54     BYTE key_data[1];
     55   };
     56   size_t key_blob_size = std::max(offsetof(KeyBlob, key_data) + key_length,
     57                                   sizeof(KeyBlob));
     58   std::vector<BYTE> key_blob_storage = std::vector<BYTE>(key_blob_size);
     59   KeyBlob* key_blob = reinterpret_cast<KeyBlob*>(&key_blob_storage[0]);
     60   key_blob->header.bType = PLAINTEXTKEYBLOB;
     61   key_blob->header.bVersion = CUR_BLOB_VERSION;
     62   key_blob->header.reserved = 0;
     63   key_blob->header.aiKeyAlg = CALG_RC2;
     64   key_blob->key_size = key_length;
     65   memcpy(key_blob->key_data, key, key_length);
     66 
     67   if (!CryptImportKey(plat_->provider_, &key_blob_storage[0],
     68                       key_blob_storage.size(), 0, CRYPT_IPSEC_HMAC_KEY,
     69                       &plat_->hkey_)) {
     70     NOTREACHED();
     71     plat_->hkey_ = NULL;
     72     return false;
     73   }
     74 
     75   // Destroy the copy of the key.
     76   SecureZeroMemory(key_blob->key_data, key_length);
     77 
     78   return true;
     79 }
     80 
     81 HMAC::~HMAC() {
     82   BOOL ok;
     83   if (plat_->hkey_) {
     84     ok = CryptDestroyKey(plat_->hkey_);
     85     DCHECK(ok);
     86   }
     87   if (plat_->hash_) {
     88     ok = CryptDestroyHash(plat_->hash_);
     89     DCHECK(ok);
     90   }
     91   if (plat_->provider_) {
     92     ok = CryptReleaseContext(plat_->provider_, 0);
     93     DCHECK(ok);
     94   }
     95 }
     96 
     97 bool HMAC::Sign(const std::string& data,
     98                 unsigned char* digest,
     99                 int digest_length) {
    100   if (!plat_->provider_ || !plat_->hkey_)
    101     return false;
    102 
    103   if (hash_alg_ != SHA1) {
    104     NOTREACHED();
    105     return false;
    106   }
    107 
    108   if (!CryptCreateHash(
    109           plat_->provider_, CALG_HMAC, plat_->hkey_, 0, &plat_->hash_))
    110     return false;
    111 
    112   HMAC_INFO hmac_info;
    113   memset(&hmac_info, 0, sizeof(hmac_info));
    114   hmac_info.HashAlgid = CALG_SHA1;
    115   if (!CryptSetHashParam(plat_->hash_, HP_HMAC_INFO,
    116                          reinterpret_cast<BYTE*>(&hmac_info), 0))
    117     return false;
    118 
    119   if (!CryptHashData(plat_->hash_,
    120                      reinterpret_cast<const BYTE*>(data.data()),
    121                      static_cast<DWORD>(data.size()), 0))
    122     return false;
    123 
    124   DWORD sha1_size = digest_length;
    125   if (!CryptGetHashParam(plat_->hash_, HP_HASHVAL, digest, &sha1_size, 0))
    126     return false;
    127 
    128   return true;
    129 }
    130 
    131 }  // namespace base
    132