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.
4
5 #include <stddef.h>
6 #include <stdint.h>
7
8 #include <memory>
9
10 #include "base/logging.h"
11 #include "base/numerics/safe_math.h"
12 #include "components/webcrypto/algorithm_implementation.h"
13 #include "components/webcrypto/algorithms/secret_key_util.h"
14 #include "components/webcrypto/algorithms/util.h"
15 #include "components/webcrypto/blink_key_handle.h"
16 #include "components/webcrypto/crypto_data.h"
17 #include "components/webcrypto/jwk.h"
18 #include "components/webcrypto/status.h"
19 #include "crypto/openssl_util.h"
20 #include "crypto/secure_util.h"
21 #include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
22 #include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
23 #include "third_party/boringssl/src/include/openssl/hmac.h"
24
25 namespace webcrypto {
26
27 namespace {
28
GetDigestBlockSizeBits(const blink::WebCryptoAlgorithm & algorithm,unsigned int * block_size_bits)29 Status GetDigestBlockSizeBits(const blink::WebCryptoAlgorithm& algorithm,
30 unsigned int* block_size_bits) {
31 const EVP_MD* md = GetDigest(algorithm);
32 if (!md)
33 return Status::ErrorUnsupported();
34 *block_size_bits = static_cast<unsigned int>(8 * EVP_MD_block_size(md));
35 return Status::Success();
36 }
37
38 // Gets the requested key length in bits for an HMAC import operation.
GetHmacImportKeyLengthBits(const blink::WebCryptoHmacImportParams * params,unsigned int key_data_byte_length,unsigned int * keylen_bits)39 Status GetHmacImportKeyLengthBits(
40 const blink::WebCryptoHmacImportParams* params,
41 unsigned int key_data_byte_length,
42 unsigned int* keylen_bits) {
43 if (key_data_byte_length == 0)
44 return Status::ErrorHmacImportEmptyKey();
45
46 // Make sure that the key data's length can be represented in bits without
47 // overflow.
48 base::CheckedNumeric<unsigned int> checked_keylen_bits(key_data_byte_length);
49 checked_keylen_bits *= 8;
50
51 if (!checked_keylen_bits.IsValid())
52 return Status::ErrorDataTooLarge();
53
54 unsigned int data_keylen_bits = checked_keylen_bits.ValueOrDie();
55
56 // Determine how many bits of the input to use.
57 *keylen_bits = data_keylen_bits;
58 if (params->HasLengthBits()) {
59 // The requested bit length must be:
60 // * No longer than the input data length
61 // * At most 7 bits shorter.
62 if (NumBitsToBytes(params->OptionalLengthBits()) != key_data_byte_length)
63 return Status::ErrorHmacImportBadLength();
64 *keylen_bits = params->OptionalLengthBits();
65 }
66
67 return Status::Success();
68 }
69
GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash)70 const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) {
71 switch (hash) {
72 case blink::kWebCryptoAlgorithmIdSha1:
73 return "HS1";
74 case blink::kWebCryptoAlgorithmIdSha256:
75 return "HS256";
76 case blink::kWebCryptoAlgorithmIdSha384:
77 return "HS384";
78 case blink::kWebCryptoAlgorithmIdSha512:
79 return "HS512";
80 default:
81 return nullptr;
82 }
83 }
84
85 const blink::WebCryptoKeyUsageMask kAllKeyUsages =
86 blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageVerify;
87
SignHmac(const std::vector<uint8_t> & raw_key,const blink::WebCryptoAlgorithm & hash,const CryptoData & data,std::vector<uint8_t> * buffer)88 Status SignHmac(const std::vector<uint8_t>& raw_key,
89 const blink::WebCryptoAlgorithm& hash,
90 const CryptoData& data,
91 std::vector<uint8_t>* buffer) {
92 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
93
94 const EVP_MD* digest_algorithm = GetDigest(hash);
95 if (!digest_algorithm)
96 return Status::ErrorUnsupported();
97 size_t hmac_expected_length = EVP_MD_size(digest_algorithm);
98
99 buffer->resize(hmac_expected_length);
100
101 unsigned int hmac_actual_length;
102 if (!HMAC(digest_algorithm, raw_key.data(), raw_key.size(), data.bytes(),
103 data.byte_length(), buffer->data(), &hmac_actual_length)) {
104 return Status::OperationError();
105 }
106
107 // HMAC() promises to use at most EVP_MD_CTX_size(). If this was not the
108 // case then memory corruption may have just occurred.
109 CHECK_EQ(hmac_expected_length, hmac_actual_length);
110
111 return Status::Success();
112 }
113
114 class HmacImplementation : public AlgorithmImplementation {
115 public:
HmacImplementation()116 HmacImplementation() {}
117
GenerateKey(const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usages,GenerateKeyResult * result) const118 Status GenerateKey(const blink::WebCryptoAlgorithm& algorithm,
119 bool extractable,
120 blink::WebCryptoKeyUsageMask usages,
121 GenerateKeyResult* result) const override {
122 Status status = CheckKeyCreationUsages(kAllKeyUsages, usages);
123 if (status.IsError())
124 return status;
125
126 const blink::WebCryptoHmacKeyGenParams* params =
127 algorithm.HmacKeyGenParams();
128
129 unsigned int keylen_bits = 0;
130 if (params->HasLengthBits()) {
131 keylen_bits = params->OptionalLengthBits();
132 // Zero-length HMAC keys are disallowed by the spec.
133 if (keylen_bits == 0)
134 return Status::ErrorGenerateHmacKeyLengthZero();
135 } else {
136 status = GetDigestBlockSizeBits(params->GetHash(), &keylen_bits);
137 if (status.IsError())
138 return status;
139 }
140
141 return GenerateWebCryptoSecretKey(blink::WebCryptoKeyAlgorithm::CreateHmac(
142 params->GetHash().Id(), keylen_bits),
143 extractable, usages, keylen_bits, result);
144 }
145
ImportKey(blink::WebCryptoKeyFormat format,const CryptoData & key_data,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usages,blink::WebCryptoKey * key) const146 Status ImportKey(blink::WebCryptoKeyFormat format,
147 const CryptoData& key_data,
148 const blink::WebCryptoAlgorithm& algorithm,
149 bool extractable,
150 blink::WebCryptoKeyUsageMask usages,
151 blink::WebCryptoKey* key) const override {
152 switch (format) {
153 case blink::kWebCryptoKeyFormatRaw:
154 return ImportKeyRaw(key_data, algorithm, extractable, usages, key);
155 case blink::kWebCryptoKeyFormatJwk:
156 return ImportKeyJwk(key_data, algorithm, extractable, usages, key);
157 default:
158 return Status::ErrorUnsupportedImportKeyFormat();
159 }
160 }
161
ExportKey(blink::WebCryptoKeyFormat format,const blink::WebCryptoKey & key,std::vector<uint8_t> * buffer) const162 Status ExportKey(blink::WebCryptoKeyFormat format,
163 const blink::WebCryptoKey& key,
164 std::vector<uint8_t>* buffer) const override {
165 switch (format) {
166 case blink::kWebCryptoKeyFormatRaw:
167 return ExportKeyRaw(key, buffer);
168 case blink::kWebCryptoKeyFormatJwk:
169 return ExportKeyJwk(key, buffer);
170 default:
171 return Status::ErrorUnsupportedExportKeyFormat();
172 }
173 }
174
ImportKeyRaw(const CryptoData & key_data,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usages,blink::WebCryptoKey * key) const175 Status ImportKeyRaw(const CryptoData& key_data,
176 const blink::WebCryptoAlgorithm& algorithm,
177 bool extractable,
178 blink::WebCryptoKeyUsageMask usages,
179 blink::WebCryptoKey* key) const {
180 Status status = CheckKeyCreationUsages(kAllKeyUsages, usages);
181 if (status.IsError())
182 return status;
183
184 const blink::WebCryptoHmacImportParams* params =
185 algorithm.HmacImportParams();
186
187 unsigned int keylen_bits = 0;
188 status = GetHmacImportKeyLengthBits(params, key_data.byte_length(),
189 &keylen_bits);
190 if (status.IsError())
191 return status;
192
193 const blink::WebCryptoKeyAlgorithm key_algorithm =
194 blink::WebCryptoKeyAlgorithm::CreateHmac(params->GetHash().Id(),
195 keylen_bits);
196
197 // If no bit truncation was requested, then done!
198 if ((keylen_bits % 8) == 0) {
199 return CreateWebCryptoSecretKey(key_data, key_algorithm, extractable,
200 usages, key);
201 }
202
203 // Otherwise zero out the unused bits in the key data before importing.
204 std::vector<uint8_t> modified_key_data(
205 key_data.bytes(), key_data.bytes() + key_data.byte_length());
206 TruncateToBitLength(keylen_bits, &modified_key_data);
207 return CreateWebCryptoSecretKey(CryptoData(modified_key_data),
208 key_algorithm, extractable, usages, key);
209 }
210
ImportKeyJwk(const CryptoData & key_data,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usages,blink::WebCryptoKey * key) const211 Status ImportKeyJwk(const CryptoData& key_data,
212 const blink::WebCryptoAlgorithm& algorithm,
213 bool extractable,
214 blink::WebCryptoKeyUsageMask usages,
215 blink::WebCryptoKey* key) const {
216 Status status = CheckKeyCreationUsages(kAllKeyUsages, usages);
217 if (status.IsError())
218 return status;
219
220 const char* algorithm_name =
221 GetJwkHmacAlgorithmName(algorithm.HmacImportParams()->GetHash().Id());
222 if (!algorithm_name)
223 return Status::ErrorUnexpected();
224
225 std::vector<uint8_t> raw_data;
226 JwkReader jwk;
227 status = ReadSecretKeyNoExpectedAlgJwk(key_data, extractable, usages,
228 &raw_data, &jwk);
229 if (status.IsError())
230 return status;
231 status = jwk.VerifyAlg(algorithm_name);
232 if (status.IsError())
233 return status;
234
235 return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages,
236 key);
237 }
238
ExportKeyRaw(const blink::WebCryptoKey & key,std::vector<uint8_t> * buffer) const239 Status ExportKeyRaw(const blink::WebCryptoKey& key,
240 std::vector<uint8_t>* buffer) const {
241 *buffer = GetSymmetricKeyData(key);
242 return Status::Success();
243 }
244
ExportKeyJwk(const blink::WebCryptoKey & key,std::vector<uint8_t> * buffer) const245 Status ExportKeyJwk(const blink::WebCryptoKey& key,
246 std::vector<uint8_t>* buffer) const {
247 const std::vector<uint8_t>& raw_data = GetSymmetricKeyData(key);
248
249 const char* algorithm_name =
250 GetJwkHmacAlgorithmName(key.Algorithm().HmacParams()->GetHash().Id());
251 if (!algorithm_name)
252 return Status::ErrorUnexpected();
253
254 WriteSecretKeyJwk(CryptoData(raw_data), algorithm_name, key.Extractable(),
255 key.Usages(), buffer);
256
257 return Status::Success();
258 }
259
Sign(const blink::WebCryptoAlgorithm & algorithm,const blink::WebCryptoKey & key,const CryptoData & data,std::vector<uint8_t> * buffer) const260 Status Sign(const blink::WebCryptoAlgorithm& algorithm,
261 const blink::WebCryptoKey& key,
262 const CryptoData& data,
263 std::vector<uint8_t>* buffer) const override {
264 const blink::WebCryptoAlgorithm& hash =
265 key.Algorithm().HmacParams()->GetHash();
266
267 return SignHmac(GetSymmetricKeyData(key), hash, data, buffer);
268 }
269
Verify(const blink::WebCryptoAlgorithm & algorithm,const blink::WebCryptoKey & key,const CryptoData & signature,const CryptoData & data,bool * signature_match) const270 Status Verify(const blink::WebCryptoAlgorithm& algorithm,
271 const blink::WebCryptoKey& key,
272 const CryptoData& signature,
273 const CryptoData& data,
274 bool* signature_match) const override {
275 std::vector<uint8_t> result;
276 Status status = Sign(algorithm, key, data, &result);
277
278 if (status.IsError())
279 return status;
280
281 // Do not allow verification of truncated MACs.
282 *signature_match = result.size() == signature.byte_length() &&
283 crypto::SecureMemEqual(result.data(), signature.bytes(),
284 signature.byte_length());
285
286 return Status::Success();
287 }
288
DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm & algorithm,blink::WebCryptoKeyType type,bool extractable,blink::WebCryptoKeyUsageMask usages,const CryptoData & key_data,blink::WebCryptoKey * key) const289 Status DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm,
290 blink::WebCryptoKeyType type,
291 bool extractable,
292 blink::WebCryptoKeyUsageMask usages,
293 const CryptoData& key_data,
294 blink::WebCryptoKey* key) const override {
295 if (algorithm.ParamsType() != blink::kWebCryptoKeyAlgorithmParamsTypeHmac ||
296 type != blink::kWebCryptoKeyTypeSecret)
297 return Status::ErrorUnexpected();
298
299 return CreateWebCryptoSecretKey(key_data, algorithm, extractable, usages,
300 key);
301 }
302
GetKeyLength(const blink::WebCryptoAlgorithm & key_length_algorithm,bool * has_length_bits,unsigned int * length_bits) const303 Status GetKeyLength(const blink::WebCryptoAlgorithm& key_length_algorithm,
304 bool* has_length_bits,
305 unsigned int* length_bits) const override {
306 const blink::WebCryptoHmacImportParams* params =
307 key_length_algorithm.HmacImportParams();
308
309 *has_length_bits = true;
310 if (params->HasLengthBits()) {
311 *length_bits = params->OptionalLengthBits();
312 if (*length_bits == 0)
313 return Status::ErrorGetHmacKeyLengthZero();
314 return Status::Success();
315 }
316
317 return GetDigestBlockSizeBits(params->GetHash(), length_bits);
318 }
319 };
320
321 } // namespace
322
CreateHmacImplementation()323 std::unique_ptr<AlgorithmImplementation> CreateHmacImplementation() {
324 return std::make_unique<HmacImplementation>();
325 }
326
327 } // namespace webcrypto
328