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 "components/webcrypto/algorithms/aes.h"
6 
7 #include <stddef.h>
8 
9 #include "base/logging.h"
10 #include "components/webcrypto/algorithms/secret_key_util.h"
11 #include "components/webcrypto/algorithms/util.h"
12 #include "components/webcrypto/blink_key_handle.h"
13 #include "components/webcrypto/crypto_data.h"
14 #include "components/webcrypto/jwk.h"
15 #include "components/webcrypto/status.h"
16 #include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
17 #include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
18 
19 namespace webcrypto {
20 
21 namespace {
22 
23 // Creates an AES algorithm name for the given key size (in bytes). For
24 // instance "A128CBC" is the result of suffix="CBC", keylen_bytes=16.
MakeJwkAesAlgorithmName(const std::string & suffix,size_t keylen_bytes)25 std::string MakeJwkAesAlgorithmName(const std::string& suffix,
26                                     size_t keylen_bytes) {
27   if (keylen_bytes == 16)
28     return std::string("A128") + suffix;
29   if (keylen_bytes == 24)
30     return std::string("A192") + suffix;
31   if (keylen_bytes == 32)
32     return std::string("A256") + suffix;
33   return std::string();
34 }
35 
36 // Synthesizes an import algorithm given a key algorithm, so that
37 // deserialization can re-use the ImportKey*() methods.
SynthesizeImportAlgorithmForClone(const blink::WebCryptoKeyAlgorithm & algorithm)38 blink::WebCryptoAlgorithm SynthesizeImportAlgorithmForClone(
39     const blink::WebCryptoKeyAlgorithm& algorithm) {
40   return blink::WebCryptoAlgorithm::AdoptParamsAndCreate(algorithm.Id(),
41                                                          nullptr);
42 }
43 
44 }  // namespace
45 
AesAlgorithm(blink::WebCryptoKeyUsageMask all_key_usages,const std::string & jwk_suffix)46 AesAlgorithm::AesAlgorithm(blink::WebCryptoKeyUsageMask all_key_usages,
47                            const std::string& jwk_suffix)
48     : all_key_usages_(all_key_usages), jwk_suffix_(jwk_suffix) {
49 }
50 
AesAlgorithm(const std::string & jwk_suffix)51 AesAlgorithm::AesAlgorithm(const std::string& jwk_suffix)
52     : all_key_usages_(blink::kWebCryptoKeyUsageEncrypt |
53                       blink::kWebCryptoKeyUsageDecrypt |
54                       blink::kWebCryptoKeyUsageWrapKey |
55                       blink::kWebCryptoKeyUsageUnwrapKey),
56       jwk_suffix_(jwk_suffix) {}
57 
GenerateKey(const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usages,GenerateKeyResult * result) const58 Status AesAlgorithm::GenerateKey(const blink::WebCryptoAlgorithm& algorithm,
59                                  bool extractable,
60                                  blink::WebCryptoKeyUsageMask usages,
61                                  GenerateKeyResult* result) const {
62   Status status = CheckKeyCreationUsages(all_key_usages_, usages);
63   if (status.IsError())
64     return status;
65 
66   uint16_t keylen_bits = algorithm.AesKeyGenParams()->LengthBits();
67 
68   // 192-bit AES is intentionally unsupported (http://crbug.com/533699).
69   if (keylen_bits == 192)
70     return Status::ErrorAes192BitUnsupported();
71 
72   if (keylen_bits != 128 && keylen_bits != 256)
73     return Status::ErrorGenerateAesKeyLength();
74 
75   return GenerateWebCryptoSecretKey(
76       blink::WebCryptoKeyAlgorithm::CreateAes(algorithm.Id(), keylen_bits),
77       extractable, usages, keylen_bits, result);
78 }
79 
ImportKey(blink::WebCryptoKeyFormat format,const CryptoData & key_data,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usages,blink::WebCryptoKey * key) const80 Status AesAlgorithm::ImportKey(blink::WebCryptoKeyFormat format,
81                                const CryptoData& key_data,
82                                const blink::WebCryptoAlgorithm& algorithm,
83                                bool extractable,
84                                blink::WebCryptoKeyUsageMask usages,
85                                blink::WebCryptoKey* key) const {
86   switch (format) {
87     case blink::kWebCryptoKeyFormatRaw:
88       return ImportKeyRaw(key_data, algorithm, extractable, usages, key);
89     case blink::kWebCryptoKeyFormatJwk:
90       return ImportKeyJwk(key_data, algorithm, extractable, usages, key);
91     default:
92       return Status::ErrorUnsupportedImportKeyFormat();
93   }
94 }
95 
ExportKey(blink::WebCryptoKeyFormat format,const blink::WebCryptoKey & key,std::vector<uint8_t> * buffer) const96 Status AesAlgorithm::ExportKey(blink::WebCryptoKeyFormat format,
97                                const blink::WebCryptoKey& key,
98                                std::vector<uint8_t>* buffer) const {
99   switch (format) {
100     case blink::kWebCryptoKeyFormatRaw:
101       return ExportKeyRaw(key, buffer);
102     case blink::kWebCryptoKeyFormatJwk:
103       return ExportKeyJwk(key, buffer);
104     default:
105       return Status::ErrorUnsupportedExportKeyFormat();
106   }
107 }
108 
ImportKeyRaw(const CryptoData & key_data,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usages,blink::WebCryptoKey * key) const109 Status AesAlgorithm::ImportKeyRaw(const CryptoData& key_data,
110                                   const blink::WebCryptoAlgorithm& algorithm,
111                                   bool extractable,
112                                   blink::WebCryptoKeyUsageMask usages,
113                                   blink::WebCryptoKey* key) const {
114   Status status = CheckKeyCreationUsages(all_key_usages_, usages);
115   if (status.IsError())
116     return status;
117 
118   const unsigned int keylen_bytes = key_data.byte_length();
119 
120   // 192-bit AES is intentionally unsupported (http://crbug.com/533699).
121   if (keylen_bytes == 24)
122     return Status::ErrorAes192BitUnsupported();
123 
124   if (keylen_bytes != 16 && keylen_bytes != 32)
125     return Status::ErrorImportAesKeyLength();
126 
127   // No possibility of overflow.
128   unsigned int keylen_bits = keylen_bytes * 8;
129 
130   return CreateWebCryptoSecretKey(
131       key_data,
132       blink::WebCryptoKeyAlgorithm::CreateAes(algorithm.Id(), keylen_bits),
133       extractable, usages, key);
134 }
135 
ImportKeyJwk(const CryptoData & key_data,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usages,blink::WebCryptoKey * key) const136 Status AesAlgorithm::ImportKeyJwk(const CryptoData& key_data,
137                                   const blink::WebCryptoAlgorithm& algorithm,
138                                   bool extractable,
139                                   blink::WebCryptoKeyUsageMask usages,
140                                   blink::WebCryptoKey* key) const {
141   Status status = CheckKeyCreationUsages(all_key_usages_, usages);
142   if (status.IsError())
143     return status;
144 
145   std::vector<uint8_t> raw_data;
146   JwkReader jwk;
147   status = ReadSecretKeyNoExpectedAlgJwk(key_data, extractable, usages,
148                                          &raw_data, &jwk);
149   if (status.IsError())
150     return status;
151 
152   bool has_jwk_alg;
153   std::string jwk_alg;
154   status = jwk.GetAlg(&jwk_alg, &has_jwk_alg);
155   if (status.IsError())
156     return status;
157 
158   if (has_jwk_alg) {
159     std::string expected_algorithm_name =
160         MakeJwkAesAlgorithmName(jwk_suffix_, raw_data.size());
161 
162     if (jwk_alg != expected_algorithm_name) {
163       // Give a different error message if the key length was wrong.
164       if (jwk_alg == MakeJwkAesAlgorithmName(jwk_suffix_, 16) ||
165           jwk_alg == MakeJwkAesAlgorithmName(jwk_suffix_, 24) ||
166           jwk_alg == MakeJwkAesAlgorithmName(jwk_suffix_, 32)) {
167         return Status::ErrorJwkIncorrectKeyLength();
168       }
169       return Status::ErrorJwkAlgorithmInconsistent();
170     }
171   }
172 
173   return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages,
174                       key);
175 }
176 
ExportKeyRaw(const blink::WebCryptoKey & key,std::vector<uint8_t> * buffer) const177 Status AesAlgorithm::ExportKeyRaw(const blink::WebCryptoKey& key,
178                                   std::vector<uint8_t>* buffer) const {
179   *buffer = GetSymmetricKeyData(key);
180   return Status::Success();
181 }
182 
ExportKeyJwk(const blink::WebCryptoKey & key,std::vector<uint8_t> * buffer) const183 Status AesAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key,
184                                   std::vector<uint8_t>* buffer) const {
185   const std::vector<uint8_t>& raw_data = GetSymmetricKeyData(key);
186 
187   WriteSecretKeyJwk(CryptoData(raw_data),
188                     MakeJwkAesAlgorithmName(jwk_suffix_, raw_data.size()),
189                     key.Extractable(), key.Usages(), buffer);
190 
191   return Status::Success();
192 }
193 
DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm & algorithm,blink::WebCryptoKeyType type,bool extractable,blink::WebCryptoKeyUsageMask usages,const CryptoData & key_data,blink::WebCryptoKey * key) const194 Status AesAlgorithm::DeserializeKeyForClone(
195     const blink::WebCryptoKeyAlgorithm& algorithm,
196     blink::WebCryptoKeyType type,
197     bool extractable,
198     blink::WebCryptoKeyUsageMask usages,
199     const CryptoData& key_data,
200     blink::WebCryptoKey* key) const {
201   if (algorithm.ParamsType() != blink::kWebCryptoKeyAlgorithmParamsTypeAes ||
202       type != blink::kWebCryptoKeyTypeSecret)
203     return Status::ErrorUnexpected();
204 
205   return ImportKeyRaw(key_data, SynthesizeImportAlgorithmForClone(algorithm),
206                       extractable, usages, key);
207 }
208 
GetKeyLength(const blink::WebCryptoAlgorithm & key_length_algorithm,bool * has_length_bits,unsigned int * length_bits) const209 Status AesAlgorithm::GetKeyLength(
210     const blink::WebCryptoAlgorithm& key_length_algorithm,
211     bool* has_length_bits,
212     unsigned int* length_bits) const {
213   *has_length_bits = true;
214   *length_bits = key_length_algorithm.AesDerivedKeyParams()->LengthBits();
215 
216   if (*length_bits == 128 || *length_bits == 256)
217     return Status::Success();
218 
219   // 192-bit AES is intentionally unsupported (http://crbug.com/533699).
220   if (*length_bits == 192)
221     return Status::ErrorAes192BitUnsupported();
222 
223   return Status::ErrorGetAesKeyLength();
224 }
225 
226 }  // namespace webcrypto
227