// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/hmac.h" #include #include #include #include #include "base/logging.h" namespace base { struct HMACPlatformData { // Windows Crypt API resources. HCRYPTPROV provider_; HCRYPTHASH hash_; HCRYPTKEY hkey_; }; HMAC::HMAC(HashAlgorithm hash_alg, const unsigned char* key, int key_length) : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { if (!CryptAcquireContext(&plat_->provider_, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) plat_->provider_ = NULL; // This code doesn't work on Win2k because PLAINTEXTKEYBLOB and // CRYPT_IPSEC_HMAC_KEY are not supported on Windows 2000. PLAINTEXTKEYBLOB // allows the import of an unencrypted key. For Win2k support, a cubmbersome // exponent-of-one key procedure must be used: // http://support.microsoft.com/kb/228786/en-us // CRYPT_IPSEC_HMAC_KEY allows keys longer than 16 bytes. struct KeyBlob { BLOBHEADER header; DWORD key_size; BYTE key_data[1]; }; size_t key_blob_size = std::max(offsetof(KeyBlob, key_data) + key_length, sizeof(KeyBlob)); std::vector key_blob_storage = std::vector(key_blob_size); KeyBlob* key_blob = reinterpret_cast(&key_blob_storage[0]); key_blob->header.bType = PLAINTEXTKEYBLOB; key_blob->header.bVersion = CUR_BLOB_VERSION; key_blob->header.reserved = 0; key_blob->header.aiKeyAlg = CALG_RC2; key_blob->key_size = key_length; memcpy(key_blob->key_data, key, key_length); if (!CryptImportKey(plat_->provider_, &key_blob_storage[0], key_blob_storage.size(), 0, CRYPT_IPSEC_HMAC_KEY, &plat_->hkey_)) { plat_->hkey_ = NULL; } // Destroy the copy of the key. SecureZeroMemory(key_blob->key_data, key_length); } HMAC::~HMAC() { if (plat_->hkey_) CryptDestroyKey(plat_->hkey_); if (plat_->hash_) CryptDestroyHash(plat_->hash_); if (plat_->provider_) CryptReleaseContext(plat_->provider_, 0); } bool HMAC::Sign(const std::string& data, unsigned char* digest, int digest_length) { if (!plat_->provider_ || !plat_->hkey_) return false; if (hash_alg_ != SHA1) { NOTREACHED(); return false; } if (!CryptCreateHash( plat_->provider_, CALG_HMAC, plat_->hkey_, 0, &plat_->hash_)) return false; HMAC_INFO hmac_info; memset(&hmac_info, 0, sizeof(hmac_info)); hmac_info.HashAlgid = CALG_SHA1; if (!CryptSetHashParam(plat_->hash_, HP_HMAC_INFO, reinterpret_cast(&hmac_info), 0)) return false; if (!CryptHashData(plat_->hash_, reinterpret_cast(data.data()), static_cast(data.size()), 0)) return false; DWORD sha1_size = digest_length; if (!CryptGetHashParam(plat_->hash_, HP_HASHVAL, digest, &sha1_size, 0)) return false; return true; } } // namespace base