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