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.
5 #include "crypto/hmac.h"
12 #include "base/logging.h"
13 #include "crypto/scoped_capi_types.h"
14 #include "crypto/third_party/nss/chromium-blapi.h"
15 #include "crypto/third_party/nss/chromium-sha256.h"
16 #include "crypto/wincrypt_shim.h"
22 // Implementation of HMAC-SHA-256:
24 // SHA-256 is supported in Windows XP SP3 or later. We still need to support
25 // Windows XP SP2, so unfortunately we have to implement HMAC-SHA-256 here.
28 SHA256_BLOCK_SIZE
= 64 // Block size (in bytes) of the input to SHA-256.
31 // NSS doesn't accept size_t for text size, divide the data into smaller
33 void Wrapped_SHA256_Update(SHA256Context
* ctx
, const unsigned char* text
,
35 const unsigned int kChunkSize
= 1 << 30;
36 while (text_len
> kChunkSize
) {
37 SHA256_Update(ctx
, text
, kChunkSize
);
39 text_len
-= kChunkSize
;
41 SHA256_Update(ctx
, text
, (unsigned int)text_len
);
44 // See FIPS 198: The Keyed-Hash Message Authentication Code (HMAC).
45 void ComputeHMACSHA256(const unsigned char* key
, size_t key_len
,
46 const unsigned char* text
, size_t text_len
,
47 unsigned char* output
, size_t output_len
) {
50 // Pre-process the key, if necessary.
51 unsigned char key0
[SHA256_BLOCK_SIZE
];
52 if (key_len
> SHA256_BLOCK_SIZE
) {
54 Wrapped_SHA256_Update(&ctx
, key
, key_len
);
55 SHA256_End(&ctx
, key0
, NULL
, SHA256_LENGTH
);
56 memset(key0
+ SHA256_LENGTH
, 0, SHA256_BLOCK_SIZE
- SHA256_LENGTH
);
58 memcpy(key0
, key
, key_len
);
59 if (key_len
< SHA256_BLOCK_SIZE
)
60 memset(key0
+ key_len
, 0, SHA256_BLOCK_SIZE
- key_len
);
63 unsigned char padded_key
[SHA256_BLOCK_SIZE
];
64 unsigned char inner_hash
[SHA256_LENGTH
];
66 // XOR key0 with ipad.
67 for (int i
= 0; i
< SHA256_BLOCK_SIZE
; ++i
)
68 padded_key
[i
] = key0
[i
] ^ 0x36;
70 // Compute the inner hash.
72 SHA256_Update(&ctx
, padded_key
, SHA256_BLOCK_SIZE
);
73 Wrapped_SHA256_Update(&ctx
, text
, text_len
);
74 SHA256_End(&ctx
, inner_hash
, NULL
, SHA256_LENGTH
);
76 // XOR key0 with opad.
77 for (int i
= 0; i
< SHA256_BLOCK_SIZE
; ++i
)
78 padded_key
[i
] = key0
[i
] ^ 0x5c;
80 // Compute the outer hash.
82 SHA256_Update(&ctx
, padded_key
, SHA256_BLOCK_SIZE
);
83 SHA256_Update(&ctx
, inner_hash
, SHA256_LENGTH
);
84 SHA256_End(&ctx
, output
, NULL
, (unsigned int) output_len
);
89 struct HMACPlatformData
{
91 if (!raw_key_
.empty()) {
92 SecureZeroMemory(&raw_key_
[0], raw_key_
.size());
95 // Destroy the key before releasing the provider.
99 ScopedHCRYPTPROV provider_
;
100 ScopedHCRYPTKEY key_
;
102 // For HMAC-SHA-256 only.
103 std::vector
<unsigned char> raw_key_
;
106 HMAC::HMAC(HashAlgorithm hash_alg
)
107 : hash_alg_(hash_alg
), plat_(new HMACPlatformData()) {
108 // Only SHA-1 and SHA-256 hash algorithms are supported now.
109 DCHECK(hash_alg_
== SHA1
|| hash_alg_
== SHA256
);
112 bool HMAC::Init(const unsigned char* key
, size_t key_length
) {
113 if (plat_
->provider_
|| plat_
->key_
|| !plat_
->raw_key_
.empty()) {
114 // Init must not be called more than once on the same HMAC object.
119 if (hash_alg_
== SHA256
) {
120 plat_
->raw_key_
.assign(key
, key
+ key_length
);
124 if (!CryptAcquireContext(plat_
->provider_
.receive(), NULL
, NULL
,
125 PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
)) {
130 // This code doesn't work on Win2k because PLAINTEXTKEYBLOB and
131 // CRYPT_IPSEC_HMAC_KEY are not supported on Windows 2000. PLAINTEXTKEYBLOB
132 // allows the import of an unencrypted key. For Win2k support, a cubmbersome
133 // exponent-of-one key procedure must be used:
134 // http://support.microsoft.com/kb/228786/en-us
135 // CRYPT_IPSEC_HMAC_KEY allows keys longer than 16 bytes.
142 size_t key_blob_size
= std::max(offsetof(KeyBlob
, key_data
) + key_length
,
144 std::vector
<BYTE
> key_blob_storage
= std::vector
<BYTE
>(key_blob_size
);
145 KeyBlob
* key_blob
= reinterpret_cast<KeyBlob
*>(&key_blob_storage
[0]);
146 key_blob
->header
.bType
= PLAINTEXTKEYBLOB
;
147 key_blob
->header
.bVersion
= CUR_BLOB_VERSION
;
148 key_blob
->header
.reserved
= 0;
149 key_blob
->header
.aiKeyAlg
= CALG_RC2
;
150 key_blob
->key_size
= static_cast<DWORD
>(key_length
);
151 memcpy(key_blob
->key_data
, key
, key_length
);
153 if (!CryptImportKey(plat_
->provider_
, &key_blob_storage
[0],
154 (DWORD
)key_blob_storage
.size(), 0,
155 CRYPT_IPSEC_HMAC_KEY
, plat_
->key_
.receive())) {
160 // Destroy the copy of the key.
161 SecureZeroMemory(key_blob
->key_data
, key_length
);
169 bool HMAC::Sign(const base::StringPiece
& data
,
170 unsigned char* digest
,
171 size_t digest_length
) const {
172 if (hash_alg_
== SHA256
) {
173 if (plat_
->raw_key_
.empty())
175 ComputeHMACSHA256(&plat_
->raw_key_
[0], plat_
->raw_key_
.size(),
176 reinterpret_cast<const unsigned char*>(data
.data()),
177 data
.size(), digest
, digest_length
);
181 if (!plat_
->provider_
|| !plat_
->key_
)
184 if (hash_alg_
!= SHA1
) {
189 ScopedHCRYPTHASH hash
;
190 if (!CryptCreateHash(plat_
->provider_
, CALG_HMAC
, plat_
->key_
, 0,
195 memset(&hmac_info
, 0, sizeof(hmac_info
));
196 hmac_info
.HashAlgid
= CALG_SHA1
;
197 if (!CryptSetHashParam(hash
, HP_HMAC_INFO
,
198 reinterpret_cast<BYTE
*>(&hmac_info
), 0))
201 if (!CryptHashData(hash
, reinterpret_cast<const BYTE
*>(data
.data()),
202 static_cast<DWORD
>(data
.size()), 0))
205 DWORD sha1_size
= static_cast<DWORD
>(digest_length
);
206 return !!CryptGetHashParam(hash
, HP_HASHVAL
, digest
, &sha1_size
, 0);
209 } // namespace crypto