blob: 03f81db1f3f644115f9ef21f60d09b417fa4f92c [file] [log] [blame]
[email protected]db163f82010-04-02 21:01:351// Copyright (c) 2010 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
5#include "base/hmac.h"
[email protected]301415e2008-09-04 19:00:376
[email protected]fbcfafe2008-09-08 13:58:107#include <windows.h>
8#include <wincrypt.h>
9
[email protected]301415e2008-09-04 19:00:3710#include <algorithm>
11#include <vector>
12
initial.commitd7cae122008-07-26 21:49:3813#include "base/logging.h"
[email protected]db163f82010-04-02 21:01:3514#include "base/third_party/nss/blapi.h"
15#include "base/third_party/nss/sha256.h"
initial.commitd7cae122008-07-26 21:49:3816
[email protected]301415e2008-09-04 19:00:3717namespace base {
18
[email protected]db163f82010-04-02 21:01:3519namespace {
20
21// Implementation of HMAC-SHA-256:
22//
23// SHA-256 is supported in Windows XP SP3 or later. We still need to support
24// Windows XP SP2, so unfortunately we have to implement HMAC-SHA-256 here.
25
26enum {
27 SHA256_BLOCK_SIZE = 64 // Block size (in bytes) of the input to SHA-256.
28};
29
30// See FIPS 198: The Keyed-Hash Message Authentication Code (HMAC).
31void ComputeHMACSHA256(const unsigned char* key, size_t key_len,
32 const unsigned char* text, size_t text_len,
33 unsigned char* output, size_t output_len) {
34 SHA256Context ctx;
35
36 // Pre-process the key, if necessary.
37 unsigned char key0[SHA256_BLOCK_SIZE];
38 if (key_len > SHA256_BLOCK_SIZE) {
39 SHA256_Begin(&ctx);
40 SHA256_Update(&ctx, key, key_len);
41 SHA256_End(&ctx, key0, NULL, SHA256_LENGTH);
42 memset(key0 + SHA256_LENGTH, 0, SHA256_BLOCK_SIZE - SHA256_LENGTH);
43 } else {
44 memcpy(key0, key, key_len);
45 memset(key0 + key_len, 0, SHA256_BLOCK_SIZE - key_len);
46 }
47
48 unsigned char padded_key[SHA256_BLOCK_SIZE];
49 unsigned char inner_hash[SHA256_LENGTH];
50
51 // XOR key0 with ipad.
52 for (int i = 0; i < SHA256_BLOCK_SIZE; ++i)
53 padded_key[i] = key0[i] ^ 0x36;
54
55 // Compute the inner hash.
56 SHA256_Begin(&ctx);
57 SHA256_Update(&ctx, padded_key, SHA256_BLOCK_SIZE);
58 SHA256_Update(&ctx, text, text_len);
59 SHA256_End(&ctx, inner_hash, NULL, SHA256_LENGTH);
60
61 // XOR key0 with opad.
62 for (int i = 0; i < SHA256_BLOCK_SIZE; ++i)
63 padded_key[i] = key0[i] ^ 0x5c;
64
65 // Compute the outer hash.
66 SHA256_Begin(&ctx);
67 SHA256_Update(&ctx, padded_key, SHA256_BLOCK_SIZE);
68 SHA256_Update(&ctx, inner_hash, SHA256_LENGTH);
69 SHA256_End(&ctx, output, NULL, output_len);
70}
71
72} // namespace
73
[email protected]fbcfafe2008-09-08 13:58:1074struct HMACPlatformData {
[email protected]db163f82010-04-02 21:01:3575 HMACPlatformData() : provider_(0), hash_(0), hkey_(0) {}
76
[email protected]fbcfafe2008-09-08 13:58:1077 // Windows Crypt API resources.
78 HCRYPTPROV provider_;
79 HCRYPTHASH hash_;
80 HCRYPTKEY hkey_;
[email protected]db163f82010-04-02 21:01:3581
82 // For HMAC-SHA-256 only.
83 std::vector<unsigned char> raw_key_;
[email protected]fbcfafe2008-09-08 13:58:1084};
85
[email protected]d91f8432009-05-05 23:55:5986HMAC::HMAC(HashAlgorithm hash_alg)
[email protected]fbcfafe2008-09-08 13:58:1087 : hash_alg_(hash_alg), plat_(new HMACPlatformData()) {
[email protected]db163f82010-04-02 21:01:3588 // Only SHA-1 and SHA-256 hash algorithms are supported now.
89 DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256);
[email protected]d91f8432009-05-05 23:55:5990}
91
92bool HMAC::Init(const unsigned char *key, int key_length) {
[email protected]db163f82010-04-02 21:01:3593 if (plat_->provider_ || plat_->hkey_ || !plat_->raw_key_.empty()) {
[email protected]d91f8432009-05-05 23:55:5994 // Init must not be called more than once on the same HMAC object.
95 NOTREACHED();
96 return false;
97 }
98
[email protected]db163f82010-04-02 21:01:3599 if (hash_alg_ == SHA256) {
100 if (key_length < SHA256_LENGTH / 2)
101 return false; // Key is too short.
102 plat_->raw_key_.assign(key, key + key_length);
103 return true;
104 }
105
[email protected]fbcfafe2008-09-08 13:58:10106 if (!CryptAcquireContext(&plat_->provider_, NULL, NULL,
[email protected]d91f8432009-05-05 23:55:59107 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
108 NOTREACHED();
[email protected]fbcfafe2008-09-08 13:58:10109 plat_->provider_ = NULL;
[email protected]d91f8432009-05-05 23:55:59110 return false;
111 }
initial.commitd7cae122008-07-26 21:49:38112
[email protected]301415e2008-09-04 19:00:37113 // This code doesn't work on Win2k because PLAINTEXTKEYBLOB and
114 // CRYPT_IPSEC_HMAC_KEY are not supported on Windows 2000. PLAINTEXTKEYBLOB
115 // allows the import of an unencrypted key. For Win2k support, a cubmbersome
116 // exponent-of-one key procedure must be used:
117 // http://support.microsoft.com/kb/228786/en-us
118 // CRYPT_IPSEC_HMAC_KEY allows keys longer than 16 bytes.
initial.commitd7cae122008-07-26 21:49:38119
[email protected]301415e2008-09-04 19:00:37120 struct KeyBlob {
initial.commitd7cae122008-07-26 21:49:38121 BLOBHEADER header;
122 DWORD key_size;
[email protected]301415e2008-09-04 19:00:37123 BYTE key_data[1];
124 };
125 size_t key_blob_size = std::max(offsetof(KeyBlob, key_data) + key_length,
126 sizeof(KeyBlob));
127 std::vector<BYTE> key_blob_storage = std::vector<BYTE>(key_blob_size);
128 KeyBlob* key_blob = reinterpret_cast<KeyBlob*>(&key_blob_storage[0]);
129 key_blob->header.bType = PLAINTEXTKEYBLOB;
130 key_blob->header.bVersion = CUR_BLOB_VERSION;
131 key_blob->header.reserved = 0;
132 key_blob->header.aiKeyAlg = CALG_RC2;
133 key_blob->key_size = key_length;
134 memcpy(key_blob->key_data, key, key_length);
initial.commitd7cae122008-07-26 21:49:38135
[email protected]fbcfafe2008-09-08 13:58:10136 if (!CryptImportKey(plat_->provider_, &key_blob_storage[0],
137 key_blob_storage.size(), 0, CRYPT_IPSEC_HMAC_KEY,
138 &plat_->hkey_)) {
[email protected]d91f8432009-05-05 23:55:59139 NOTREACHED();
[email protected]fbcfafe2008-09-08 13:58:10140 plat_->hkey_ = NULL;
[email protected]d91f8432009-05-05 23:55:59141 return false;
[email protected]301415e2008-09-04 19:00:37142 }
initial.commitd7cae122008-07-26 21:49:38143
144 // Destroy the copy of the key.
[email protected]301415e2008-09-04 19:00:37145 SecureZeroMemory(key_blob->key_data, key_length);
[email protected]d91f8432009-05-05 23:55:59146
147 return true;
initial.commitd7cae122008-07-26 21:49:38148}
149
[email protected]fbcfafe2008-09-08 13:58:10150HMAC::~HMAC() {
[email protected]db163f82010-04-02 21:01:35151 if (!plat_->raw_key_.empty())
152 SecureZeroMemory(&plat_->raw_key_[0], plat_->raw_key_.size());
153
[email protected]d91f8432009-05-05 23:55:59154 BOOL ok;
155 if (plat_->hkey_) {
156 ok = CryptDestroyKey(plat_->hkey_);
157 DCHECK(ok);
158 }
159 if (plat_->hash_) {
160 ok = CryptDestroyHash(plat_->hash_);
161 DCHECK(ok);
162 }
163 if (plat_->provider_) {
164 ok = CryptReleaseContext(plat_->provider_, 0);
165 DCHECK(ok);
166 }
[email protected]fbcfafe2008-09-08 13:58:10167}
168
169bool HMAC::Sign(const std::string& data,
170 unsigned char* digest,
171 int digest_length) {
[email protected]db163f82010-04-02 21:01:35172 if (hash_alg_ == SHA256) {
173 if (plat_->raw_key_.empty())
174 return false;
175 ComputeHMACSHA256(&plat_->raw_key_[0], plat_->raw_key_.size(),
176 reinterpret_cast<const unsigned char*>(data.data()),
177 data.size(), digest, digest_length);
178 return true;
179 }
180
[email protected]fbcfafe2008-09-08 13:58:10181 if (!plat_->provider_ || !plat_->hkey_)
182 return false;
183
184 if (hash_alg_ != SHA1) {
185 NOTREACHED();
186 return false;
187 }
188
189 if (!CryptCreateHash(
190 plat_->provider_, CALG_HMAC, plat_->hkey_, 0, &plat_->hash_))
initial.commitd7cae122008-07-26 21:49:38191 return false;
192
193 HMAC_INFO hmac_info;
194 memset(&hmac_info, 0, sizeof(hmac_info));
195 hmac_info.HashAlgid = CALG_SHA1;
[email protected]fbcfafe2008-09-08 13:58:10196 if (!CryptSetHashParam(plat_->hash_, HP_HMAC_INFO,
initial.commitd7cae122008-07-26 21:49:38197 reinterpret_cast<BYTE*>(&hmac_info), 0))
198 return false;
199
[email protected]fbcfafe2008-09-08 13:58:10200 if (!CryptHashData(plat_->hash_,
initial.commitd7cae122008-07-26 21:49:38201 reinterpret_cast<const BYTE*>(data.data()),
202 static_cast<DWORD>(data.size()), 0))
203 return false;
204
205 DWORD sha1_size = digest_length;
[email protected]fbcfafe2008-09-08 13:58:10206 if (!CryptGetHashParam(plat_->hash_, HP_HASHVAL, digest, &sha1_size, 0))
initial.commitd7cae122008-07-26 21:49:38207 return false;
208
209 return true;
210}
license.botbf09a502008-08-24 00:55:55211
[email protected]301415e2008-09-04 19:00:37212} // namespace base