Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / webcrypto / algorithms / hmac.cc
blob2bc8e84e5998b09b3cef296973ed74c9270167be
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"
23 namespace webcrypto {
25 namespace {
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();
39 default:
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
53 // overflow.
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) {
77 switch (hash) {
78 case blink::WebCryptoAlgorithmIdSha1:
79 return "HS1";
80 case blink::WebCryptoAlgorithmIdSha256:
81 return "HS256";
82 case blink::WebCryptoAlgorithmIdSha384:
83 return "HS384";
84 case blink::WebCryptoAlgorithmIdSha512:
85 return "HS512";
86 default:
87 return NULL;
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 {
120 public:
121 HmacImplementation() {}
123 Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm,
124 bool extractable,
125 blink::WebCryptoKeyUsageMask usages,
126 GenerateKeyResult* result) const override {
127 Status status = CheckKeyCreationUsages(kAllKeyUsages, usages, false);
128 if (status.IsError())
129 return status;
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();
140 } else {
141 status = GetShaBlockSizeBits(params->hash(), &keylen_bits);
142 if (status.IsError())
143 return status;
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 {
154 switch (format) {
155 case blink::WebCryptoKeyFormatRaw:
156 case blink::WebCryptoKeyFormatJwk:
157 return CheckKeyCreationUsages(kAllKeyUsages, usages, false);
158 default:
159 return Status::ErrorUnsupportedImportKeyFormat();
163 Status ImportKeyRaw(const CryptoData& key_data,
164 const blink::WebCryptoAlgorithm& algorithm,
165 bool extractable,
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(),
173 &keylen_bits);
174 if (status.IsError())
175 return status;
177 const blink::WebCryptoKeyAlgorithm key_algorithm =
178 blink::WebCryptoKeyAlgorithm::createHmac(params->hash().id(),
179 keylen_bits);
181 // If no bit truncation was requested, then done!
182 if ((keylen_bits % 8) == 0) {
183 return CreateWebCryptoSecretKey(key_data, key_algorithm, extractable,
184 usages, key);
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,
197 bool extractable,
198 blink::WebCryptoKeyUsageMask usages,
199 blink::WebCryptoKey* key) const override {
200 const char* algorithm_name =
201 GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id());
202 if (!algorithm_name)
203 return Status::ErrorUnexpected();
205 std::vector<uint8_t> raw_data;
206 JwkReader jwk;
207 Status status = ReadSecretKeyNoExpectedAlgJwk(key_data, extractable, usages,
208 &raw_data, &jwk);
209 if (status.IsError())
210 return status;
211 status = jwk.VerifyAlg(algorithm_name);
212 if (status.IsError())
213 return status;
215 return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages,
216 key);
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());
231 if (!algorithm_name)
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())
259 return status;
261 // Do not allow verification of truncated MACs.
262 *signature_match =
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,
272 bool extractable,
273 blink::WebCryptoKeyUsageMask usages,
274 const CryptoData& key_data,
275 blink::WebCryptoKey* key) const override {
276 return CreateWebCryptoSecretKey(key_data, algorithm, extractable, usages,
277 key);
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);
298 } // namespace
300 scoped_ptr<AlgorithmImplementation> CreateHmacImplementation() {
301 return make_scoped_ptr(new HmacImplementation);
304 } // namespace webcrypto