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/stl_util.h"
9 #include "content/child/webcrypto/algorithm_implementation.h"
10 #include "content/child/webcrypto/crypto_data.h"
11 #include "content/child/webcrypto/jwk.h"
12 #include "content/child/webcrypto/openssl/key_openssl.h"
13 #include "content/child/webcrypto/openssl/util_openssl.h"
14 #include "content/child/webcrypto/status.h"
15 #include "content/child/webcrypto/webcrypto_util.h"
16 #include "crypto/openssl_util.h"
17 #include "crypto/secure_util.h"
18 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
19 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
27 const blink::WebCryptoKeyUsageMask kAllKeyUsages
=
28 blink::WebCryptoKeyUsageSign
| blink::WebCryptoKeyUsageVerify
;
30 Status
SignHmac(const std::vector
<uint8_t>& raw_key
,
31 const blink::WebCryptoAlgorithm
& hash
,
32 const CryptoData
& data
,
33 std::vector
<uint8_t>* buffer
) {
34 crypto::OpenSSLErrStackTracer
err_tracer(FROM_HERE
);
36 const EVP_MD
* digest_algorithm
= GetDigest(hash
.id());
37 if (!digest_algorithm
)
38 return Status::ErrorUnsupported();
39 unsigned int hmac_expected_length
= EVP_MD_size(digest_algorithm
);
41 // OpenSSL wierdness here.
42 // First, HMAC() needs a void* for the key data, so make one up front as a
43 // cosmetic to avoid a cast. Second, OpenSSL does not like a NULL key,
44 // which will result if the raw_key vector is empty; an entirely valid
45 // case. Handle this specific case by pointing to a fresh array.
46 const unsigned char null_key
[] = {0};
47 const void* const raw_key_voidp
= raw_key
.size() ? &raw_key
[0] : null_key
;
49 buffer
->resize(hmac_expected_length
);
50 crypto::ScopedOpenSSLSafeSizeBuffer
<EVP_MAX_MD_SIZE
> hmac_result(
51 vector_as_array(buffer
), hmac_expected_length
);
53 unsigned int hmac_actual_length
;
54 unsigned char* const success
=
55 HMAC(digest_algorithm
, raw_key_voidp
, raw_key
.size(), data
.bytes(),
56 data
.byte_length(), hmac_result
.safe_buffer(), &hmac_actual_length
);
57 if (!success
|| hmac_actual_length
!= hmac_expected_length
)
58 return Status::OperationError();
60 return Status::Success();
63 class HmacImplementation
: public AlgorithmImplementation
{
65 HmacImplementation() {}
67 Status
GenerateKey(const blink::WebCryptoAlgorithm
& algorithm
,
69 blink::WebCryptoKeyUsageMask usages
,
70 GenerateKeyResult
* result
) const override
{
71 Status status
= CheckKeyCreationUsages(kAllKeyUsages
, usages
, false);
75 const blink::WebCryptoHmacKeyGenParams
* params
=
76 algorithm
.hmacKeyGenParams();
78 unsigned int keylen_bits
= 0;
79 status
= GetHmacKeyGenLengthInBits(params
, &keylen_bits
);
83 return GenerateWebCryptoSecretKey(blink::WebCryptoKeyAlgorithm::createHmac(
84 params
->hash().id(), keylen_bits
),
85 extractable
, usages
, keylen_bits
, result
);
88 Status
VerifyKeyUsagesBeforeImportKey(
89 blink::WebCryptoKeyFormat format
,
90 blink::WebCryptoKeyUsageMask usages
) const override
{
92 case blink::WebCryptoKeyFormatRaw
:
93 case blink::WebCryptoKeyFormatJwk
:
94 return CheckKeyCreationUsages(kAllKeyUsages
, usages
, false);
96 return Status::ErrorUnsupportedImportKeyFormat();
100 Status
ImportKeyRaw(const CryptoData
& key_data
,
101 const blink::WebCryptoAlgorithm
& algorithm
,
103 blink::WebCryptoKeyUsageMask usages
,
104 blink::WebCryptoKey
* key
) const override
{
105 const blink::WebCryptoHmacImportParams
* params
=
106 algorithm
.hmacImportParams();
108 unsigned int keylen_bits
= 0;
109 Status status
= GetHmacImportKeyLengthBits(params
, key_data
.byte_length(),
111 if (status
.IsError())
114 const blink::WebCryptoKeyAlgorithm key_algorithm
=
115 blink::WebCryptoKeyAlgorithm::createHmac(params
->hash().id(),
118 // If no bit truncation was requested, then done!
119 if ((keylen_bits
% 8) == 0) {
120 return CreateWebCryptoSecretKey(key_data
, key_algorithm
, extractable
,
124 // Otherwise zero out the unused bits in the key data before importing.
125 std::vector
<uint8_t> modified_key_data(
126 key_data
.bytes(), key_data
.bytes() + key_data
.byte_length());
127 TruncateToBitLength(keylen_bits
, &modified_key_data
);
128 return CreateWebCryptoSecretKey(CryptoData(modified_key_data
),
129 key_algorithm
, extractable
, usages
, key
);
132 Status
ImportKeyJwk(const CryptoData
& key_data
,
133 const blink::WebCryptoAlgorithm
& algorithm
,
135 blink::WebCryptoKeyUsageMask usages
,
136 blink::WebCryptoKey
* key
) const override
{
137 const char* algorithm_name
=
138 GetJwkHmacAlgorithmName(algorithm
.hmacImportParams()->hash().id());
140 return Status::ErrorUnexpected();
142 std::vector
<uint8_t> raw_data
;
143 Status status
= ReadSecretKeyJwk(key_data
, algorithm_name
, extractable
,
145 if (status
.IsError())
148 return ImportKeyRaw(CryptoData(raw_data
), algorithm
, extractable
, usages
,
152 Status
ExportKeyRaw(const blink::WebCryptoKey
& key
,
153 std::vector
<uint8_t>* buffer
) const override
{
154 *buffer
= SymKeyOpenSsl::Cast(key
)->raw_key_data();
155 return Status::Success();
158 Status
ExportKeyJwk(const blink::WebCryptoKey
& key
,
159 std::vector
<uint8_t>* buffer
) const override
{
160 SymKeyOpenSsl
* sym_key
= SymKeyOpenSsl::Cast(key
);
161 const std::vector
<uint8_t>& raw_data
= sym_key
->raw_key_data();
163 const char* algorithm_name
=
164 GetJwkHmacAlgorithmName(key
.algorithm().hmacParams()->hash().id());
166 return Status::ErrorUnexpected();
168 WriteSecretKeyJwk(CryptoData(raw_data
), algorithm_name
, key
.extractable(),
169 key
.usages(), buffer
);
171 return Status::Success();
174 Status
Sign(const blink::WebCryptoAlgorithm
& algorithm
,
175 const blink::WebCryptoKey
& key
,
176 const CryptoData
& data
,
177 std::vector
<uint8_t>* buffer
) const override
{
178 const blink::WebCryptoAlgorithm
& hash
=
179 key
.algorithm().hmacParams()->hash();
181 return SignHmac(SymKeyOpenSsl::Cast(key
)->raw_key_data(), hash
, data
,
185 Status
Verify(const blink::WebCryptoAlgorithm
& algorithm
,
186 const blink::WebCryptoKey
& key
,
187 const CryptoData
& signature
,
188 const CryptoData
& data
,
189 bool* signature_match
) const override
{
190 std::vector
<uint8_t> result
;
191 Status status
= Sign(algorithm
, key
, data
, &result
);
193 if (status
.IsError())
196 // Do not allow verification of truncated MACs.
198 result
.size() == signature
.byte_length() &&
199 crypto::SecureMemEqual(vector_as_array(&result
), signature
.bytes(),
200 signature
.byte_length());
202 return Status::Success();
205 Status
SerializeKeyForClone(
206 const blink::WebCryptoKey
& key
,
207 blink::WebVector
<uint8_t>* key_data
) const override
{
208 key_data
->assign(SymKeyOpenSsl::Cast(key
)->serialized_key_data());
209 return Status::Success();
212 Status
DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm
& algorithm
,
213 blink::WebCryptoKeyType type
,
215 blink::WebCryptoKeyUsageMask usages
,
216 const CryptoData
& key_data
,
217 blink::WebCryptoKey
* key
) const override
{
218 return CreateWebCryptoSecretKey(key_data
, algorithm
, extractable
, usages
,
222 Status
GetKeyLength(const blink::WebCryptoAlgorithm
& key_length_algorithm
,
223 bool* has_length_bits
,
224 unsigned int* length_bits
) const override
{
225 return GetHmacKeyLength(key_length_algorithm
, has_length_bits
, length_bits
);
231 AlgorithmImplementation
* CreatePlatformHmacImplementation() {
232 return new HmacImplementation
;
235 } // namespace webcrypto
237 } // namespace content