1 // Copyright 2014 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 <openssl/hmac.h>
7 #include "base/logging.h"
8 #include "base/numerics/safe_math.h"
9 #include "base/stl_util.h"
10 #include "components/webcrypto/algorithm_implementation.h"
11 #include "components/webcrypto/algorithms/secret_key_util.h"
12 #include "components/webcrypto/algorithms/util_openssl.h"
13 #include "components/webcrypto/crypto_data.h"
14 #include "components/webcrypto/jwk.h"
15 #include "components/webcrypto/key.h"
16 #include "components/webcrypto/status.h"
17 #include "components/webcrypto/webcrypto_util.h"
18 #include "crypto/openssl_util.h"
19 #include "crypto/secure_util.h"
20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
21 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
27 // TODO(eroman): Use EVP_MD_block_size() instead.
28 Status
GetShaBlockSizeBits(const blink::WebCryptoAlgorithm
& algorithm
,
29 unsigned int* block_size_bits
) {
30 switch (algorithm
.id()) {
31 case blink::WebCryptoAlgorithmIdSha1
:
32 case blink::WebCryptoAlgorithmIdSha256
:
33 *block_size_bits
= 512;
34 return Status::Success();
35 case blink::WebCryptoAlgorithmIdSha384
:
36 case blink::WebCryptoAlgorithmIdSha512
:
37 *block_size_bits
= 1024;
38 return Status::Success();
40 return Status::ErrorUnsupported();
44 // Gets the requested key length in bits for an HMAC import operation.
45 Status
GetHmacImportKeyLengthBits(
46 const blink::WebCryptoHmacImportParams
* params
,
47 unsigned int key_data_byte_length
,
48 unsigned int* keylen_bits
) {
49 if (key_data_byte_length
== 0)
50 return Status::ErrorHmacImportEmptyKey();
52 // Make sure that the key data's length can be represented in bits without
54 base::CheckedNumeric
<unsigned int> checked_keylen_bits(key_data_byte_length
);
55 checked_keylen_bits
*= 8;
57 if (!checked_keylen_bits
.IsValid())
58 return Status::ErrorDataTooLarge();
60 unsigned int data_keylen_bits
= checked_keylen_bits
.ValueOrDie();
62 // Determine how many bits of the input to use.
63 *keylen_bits
= data_keylen_bits
;
64 if (params
->hasLengthBits()) {
65 // The requested bit length must be:
66 // * No longer than the input data length
67 // * At most 7 bits shorter.
68 if (NumBitsToBytes(params
->optionalLengthBits()) != key_data_byte_length
)
69 return Status::ErrorHmacImportBadLength();
70 *keylen_bits
= params
->optionalLengthBits();
73 return Status::Success();
76 const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash
) {
78 case blink::WebCryptoAlgorithmIdSha1
:
80 case blink::WebCryptoAlgorithmIdSha256
:
82 case blink::WebCryptoAlgorithmIdSha384
:
84 case blink::WebCryptoAlgorithmIdSha512
:
91 const blink::WebCryptoKeyUsageMask kAllKeyUsages
=
92 blink::WebCryptoKeyUsageSign
| blink::WebCryptoKeyUsageVerify
;
94 Status
SignHmac(const std::vector
<uint8_t>& raw_key
,
95 const blink::WebCryptoAlgorithm
& hash
,
96 const CryptoData
& data
,
97 std::vector
<uint8_t>* buffer
) {
98 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
100 const EVP_MD
* digest_algorithm
= GetDigest(hash
.id());
101 if (!digest_algorithm
)
102 return Status::ErrorUnsupported();
103 size_t hmac_expected_length
= EVP_MD_size(digest_algorithm
);
105 buffer
->resize(hmac_expected_length
);
106 crypto::ScopedOpenSSLSafeSizeBuffer
<EVP_MAX_MD_SIZE
> hmac_result(
107 vector_as_array(buffer
), hmac_expected_length
);
109 unsigned int hmac_actual_length
;
110 unsigned char* const success
= HMAC(
111 digest_algorithm
, vector_as_array(&raw_key
), raw_key
.size(), data
.bytes(),
112 data
.byte_length(), hmac_result
.safe_buffer(), &hmac_actual_length
);
113 if (!success
|| hmac_actual_length
!= hmac_expected_length
)
114 return Status::OperationError();
116 return Status::Success();
119 class HmacImplementation
: public AlgorithmImplementation
{
121 HmacImplementation() {}
123 Status
GenerateKey(const blink::WebCryptoAlgorithm
& algorithm
,
125 blink::WebCryptoKeyUsageMask usages
,
126 GenerateKeyResult
* result
) const override
{
127 Status status
= CheckKeyCreationUsages(kAllKeyUsages
, usages
, false);
128 if (status
.IsError())
131 const blink::WebCryptoHmacKeyGenParams
* params
=
132 algorithm
.hmacKeyGenParams();
134 unsigned int keylen_bits
= 0;
135 if (params
->hasLengthBits()) {
136 keylen_bits
= params
->optionalLengthBits();
137 // Zero-length HMAC keys are disallowed by the spec.
138 if (keylen_bits
== 0)
139 return Status::ErrorGenerateHmacKeyLengthZero();
141 status
= GetShaBlockSizeBits(params
->hash(), &keylen_bits
);
142 if (status
.IsError())
146 return GenerateWebCryptoSecretKey(blink::WebCryptoKeyAlgorithm::createHmac(
147 params
->hash().id(), keylen_bits
),
148 extractable
, usages
, keylen_bits
, result
);
151 Status
VerifyKeyUsagesBeforeImportKey(
152 blink::WebCryptoKeyFormat format
,
153 blink::WebCryptoKeyUsageMask usages
) const override
{
155 case blink::WebCryptoKeyFormatRaw
:
156 case blink::WebCryptoKeyFormatJwk
:
157 return CheckKeyCreationUsages(kAllKeyUsages
, usages
, false);
159 return Status::ErrorUnsupportedImportKeyFormat();
163 Status
ImportKeyRaw(const CryptoData
& key_data
,
164 const blink::WebCryptoAlgorithm
& algorithm
,
166 blink::WebCryptoKeyUsageMask usages
,
167 blink::WebCryptoKey
* key
) const override
{
168 const blink::WebCryptoHmacImportParams
* params
=
169 algorithm
.hmacImportParams();
171 unsigned int keylen_bits
= 0;
172 Status status
= GetHmacImportKeyLengthBits(params
, key_data
.byte_length(),
174 if (status
.IsError())
177 const blink::WebCryptoKeyAlgorithm key_algorithm
=
178 blink::WebCryptoKeyAlgorithm::createHmac(params
->hash().id(),
181 // If no bit truncation was requested, then done!
182 if ((keylen_bits
% 8) == 0) {
183 return CreateWebCryptoSecretKey(key_data
, key_algorithm
, extractable
,
187 // Otherwise zero out the unused bits in the key data before importing.
188 std::vector
<uint8_t> modified_key_data(
189 key_data
.bytes(), key_data
.bytes() + key_data
.byte_length());
190 TruncateToBitLength(keylen_bits
, &modified_key_data
);
191 return CreateWebCryptoSecretKey(CryptoData(modified_key_data
),
192 key_algorithm
, extractable
, usages
, key
);
195 Status
ImportKeyJwk(const CryptoData
& key_data
,
196 const blink::WebCryptoAlgorithm
& algorithm
,
198 blink::WebCryptoKeyUsageMask usages
,
199 blink::WebCryptoKey
* key
) const override
{
200 const char* algorithm_name
=
201 GetJwkHmacAlgorithmName(algorithm
.hmacImportParams()->hash().id());
203 return Status::ErrorUnexpected();
205 std::vector
<uint8_t> raw_data
;
207 Status status
= ReadSecretKeyNoExpectedAlgJwk(key_data
, extractable
, usages
,
209 if (status
.IsError())
211 status
= jwk
.VerifyAlg(algorithm_name
);
212 if (status
.IsError())
215 return ImportKeyRaw(CryptoData(raw_data
), algorithm
, extractable
, usages
,
219 Status
ExportKeyRaw(const blink::WebCryptoKey
& key
,
220 std::vector
<uint8_t>* buffer
) const override
{
221 *buffer
= GetSymmetricKeyData(key
);
222 return Status::Success();
225 Status
ExportKeyJwk(const blink::WebCryptoKey
& key
,
226 std::vector
<uint8_t>* buffer
) const override
{
227 const std::vector
<uint8_t>& raw_data
= GetSymmetricKeyData(key
);
229 const char* algorithm_name
=
230 GetJwkHmacAlgorithmName(key
.algorithm().hmacParams()->hash().id());
232 return Status::ErrorUnexpected();
234 WriteSecretKeyJwk(CryptoData(raw_data
), algorithm_name
, key
.extractable(),
235 key
.usages(), buffer
);
237 return Status::Success();
240 Status
Sign(const blink::WebCryptoAlgorithm
& algorithm
,
241 const blink::WebCryptoKey
& key
,
242 const CryptoData
& data
,
243 std::vector
<uint8_t>* buffer
) const override
{
244 const blink::WebCryptoAlgorithm
& hash
=
245 key
.algorithm().hmacParams()->hash();
247 return SignHmac(GetSymmetricKeyData(key
), hash
, data
, buffer
);
250 Status
Verify(const blink::WebCryptoAlgorithm
& algorithm
,
251 const blink::WebCryptoKey
& key
,
252 const CryptoData
& signature
,
253 const CryptoData
& data
,
254 bool* signature_match
) const override
{
255 std::vector
<uint8_t> result
;
256 Status status
= Sign(algorithm
, key
, data
, &result
);
258 if (status
.IsError())
261 // Do not allow verification of truncated MACs.
263 result
.size() == signature
.byte_length() &&
264 crypto::SecureMemEqual(vector_as_array(&result
), signature
.bytes(),
265 signature
.byte_length());
267 return Status::Success();
270 Status
DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm
& algorithm
,
271 blink::WebCryptoKeyType type
,
273 blink::WebCryptoKeyUsageMask usages
,
274 const CryptoData
& key_data
,
275 blink::WebCryptoKey
* key
) const override
{
276 return CreateWebCryptoSecretKey(key_data
, algorithm
, extractable
, usages
,
280 Status
GetKeyLength(const blink::WebCryptoAlgorithm
& key_length_algorithm
,
281 bool* has_length_bits
,
282 unsigned int* length_bits
) const override
{
283 const blink::WebCryptoHmacImportParams
* params
=
284 key_length_algorithm
.hmacImportParams();
286 *has_length_bits
= true;
287 if (params
->hasLengthBits()) {
288 *length_bits
= params
->optionalLengthBits();
289 if (*length_bits
== 0)
290 return Status::ErrorGetHmacKeyLengthZero();
291 return Status::Success();
294 return GetShaBlockSizeBits(params
->hash(), length_bits
);
300 scoped_ptr
<AlgorithmImplementation
> CreateHmacImplementation() {
301 return make_scoped_ptr(new HmacImplementation
);
304 } // namespace webcrypto