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 "components/webcrypto/webcrypto_util.h"
7 #include "base/logging.h"
8 #include "base/numerics/safe_math.h"
9 #include "components/webcrypto/status.h"
10 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
11 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
12 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
18 // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros,
20 bool BigIntegerToUint(const uint8_t* data
,
22 unsigned int* result
) {
27 for (size_t i
= 0; i
< data_size
; ++i
) {
28 size_t reverse_i
= data_size
- i
- 1;
30 if (reverse_i
>= sizeof(*result
) && data
[i
])
31 return false; // Too large for a uint.
33 *result
|= data
[i
] << 8 * reverse_i
;
38 Status
GetShaBlockSizeBits(const blink::WebCryptoAlgorithm
& algorithm
,
39 unsigned int* block_size_bits
) {
40 switch (algorithm
.id()) {
41 case blink::WebCryptoAlgorithmIdSha1
:
42 case blink::WebCryptoAlgorithmIdSha256
:
43 *block_size_bits
= 512;
44 return Status::Success();
45 case blink::WebCryptoAlgorithmIdSha384
:
46 case blink::WebCryptoAlgorithmIdSha512
:
47 *block_size_bits
= 1024;
48 return Status::Success();
50 return Status::ErrorUnsupported();
56 blink::WebCryptoAlgorithm
CreateAlgorithm(blink::WebCryptoAlgorithmId id
) {
57 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(id
, NULL
);
60 blink::WebCryptoAlgorithm
CreateHmacImportAlgorithm(
61 blink::WebCryptoAlgorithmId hash_id
,
62 unsigned int length_bits
) {
63 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id
));
64 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
65 blink::WebCryptoAlgorithmIdHmac
,
66 new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id
), true,
70 blink::WebCryptoAlgorithm
CreateHmacImportAlgorithmNoLength(
71 blink::WebCryptoAlgorithmId hash_id
) {
72 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id
));
73 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
74 blink::WebCryptoAlgorithmIdHmac
,
75 new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id
), false, 0));
78 blink::WebCryptoAlgorithm
CreateRsaHashedImportAlgorithm(
79 blink::WebCryptoAlgorithmId id
,
80 blink::WebCryptoAlgorithmId hash_id
) {
81 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id
));
82 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
83 id
, new blink::WebCryptoRsaHashedImportParams(CreateAlgorithm(hash_id
)));
86 blink::WebCryptoAlgorithm
CreateEcImportAlgorithm(
87 blink::WebCryptoAlgorithmId id
,
88 blink::WebCryptoNamedCurve named_curve
) {
89 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
90 id
, new blink::WebCryptoEcKeyImportParams(named_curve
));
93 bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a
,
94 blink::WebCryptoKeyUsageMask b
) {
98 // The WebCrypto spec defines the default value for the tag length, as well as
99 // the allowed values for tag length.
100 Status
GetAesGcmTagLengthInBits(const blink::WebCryptoAesGcmParams
* params
,
101 unsigned int* tag_length_bits
) {
102 *tag_length_bits
= 128;
103 if (params
->hasTagLengthBits())
104 *tag_length_bits
= params
->optionalTagLengthBits();
106 if (*tag_length_bits
!= 32 && *tag_length_bits
!= 64 &&
107 *tag_length_bits
!= 96 && *tag_length_bits
!= 104 &&
108 *tag_length_bits
!= 112 && *tag_length_bits
!= 120 &&
109 *tag_length_bits
!= 128)
110 return Status::ErrorInvalidAesGcmTagLength();
112 return Status::Success();
115 Status
GetAesKeyGenLengthInBits(const blink::WebCryptoAesKeyGenParams
* params
,
116 unsigned int* keylen_bits
) {
117 *keylen_bits
= params
->lengthBits();
119 if (*keylen_bits
== 128 || *keylen_bits
== 256)
120 return Status::Success();
122 // BoringSSL does not support 192-bit AES.
123 if (*keylen_bits
== 192)
124 return Status::ErrorAes192BitUnsupported();
126 return Status::ErrorGenerateAesKeyLength();
129 Status
GetHmacKeyGenLengthInBits(const blink::WebCryptoHmacKeyGenParams
* params
,
130 unsigned int* keylen_bits
) {
131 if (!params
->hasLengthBits())
132 return GetShaBlockSizeBits(params
->hash(), keylen_bits
);
134 *keylen_bits
= params
->optionalLengthBits();
136 // Zero-length HMAC keys are disallowed by the spec.
137 if (*keylen_bits
== 0)
138 return Status::ErrorGenerateHmacKeyLengthZero();
140 return Status::Success();
143 Status
GetHmacImportKeyLengthBits(
144 const blink::WebCryptoHmacImportParams
* params
,
145 unsigned int key_data_byte_length
,
146 unsigned int* keylen_bits
) {
147 if (key_data_byte_length
== 0)
148 return Status::ErrorHmacImportEmptyKey();
150 // Make sure that the key data's length can be represented in bits without
152 base::CheckedNumeric
<unsigned int> checked_keylen_bits(key_data_byte_length
);
153 checked_keylen_bits
*= 8;
155 if (!checked_keylen_bits
.IsValid())
156 return Status::ErrorDataTooLarge();
158 unsigned int data_keylen_bits
= checked_keylen_bits
.ValueOrDie();
160 // Determine how many bits of the input to use.
161 *keylen_bits
= data_keylen_bits
;
162 if (params
->hasLengthBits()) {
163 // The requested bit length must be:
164 // * No longer than the input data length
165 // * At most 7 bits shorter.
166 if (NumBitsToBytes(params
->optionalLengthBits()) != key_data_byte_length
)
167 return Status::ErrorHmacImportBadLength();
168 *keylen_bits
= params
->optionalLengthBits();
171 return Status::Success();
174 Status
VerifyAesKeyLengthForImport(unsigned int keylen_bytes
) {
175 if (keylen_bytes
== 16 || keylen_bytes
== 32)
176 return Status::Success();
178 // BoringSSL does not support 192-bit AES.
179 if (keylen_bytes
== 24)
180 return Status::ErrorAes192BitUnsupported();
182 return Status::ErrorImportAesKeyLength();
185 Status
CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages
,
186 blink::WebCryptoKeyUsageMask actual_usages
,
187 bool allow_empty_usages
) {
188 if (!allow_empty_usages
&& actual_usages
== 0)
189 return Status::ErrorCreateKeyEmptyUsages();
191 if (!ContainsKeyUsages(all_possible_usages
, actual_usages
))
192 return Status::ErrorCreateKeyBadUsages();
193 return Status::Success();
196 Status
GetRsaKeyGenParameters(
197 const blink::WebCryptoRsaHashedKeyGenParams
* params
,
198 unsigned int* public_exponent
,
199 unsigned int* modulus_length_bits
) {
200 *modulus_length_bits
= params
->modulusLengthBits();
202 // Limit the RSA key sizes to:
203 // * Multiple of 8 bits
204 // * 256 bits to 16K bits
205 // This corresponds to the values that NSS would allow, which was relevant
206 // back when Chromium's WebCrypto supported both OpenSSL and NSS.
207 if (*modulus_length_bits
< 256 || *modulus_length_bits
> 16384 ||
208 (*modulus_length_bits
% 8) != 0) {
209 return Status::ErrorGenerateRsaUnsupportedModulus();
212 if (!BigIntegerToUint(params
->publicExponent().data(),
213 params
->publicExponent().size(), public_exponent
)) {
214 return Status::ErrorGenerateKeyPublicExponent();
217 // OpenSSL hangs when given bad public exponents. Use a whitelist to avoid
218 // feeding OpenSSL data that could hang.
219 if (*public_exponent
!= 3 && *public_exponent
!= 65537)
220 return Status::ErrorGenerateKeyPublicExponent();
222 return Status::Success();
225 Status
VerifyUsagesBeforeImportAsymmetricKey(
226 blink::WebCryptoKeyFormat format
,
227 blink::WebCryptoKeyUsageMask all_public_key_usages
,
228 blink::WebCryptoKeyUsageMask all_private_key_usages
,
229 blink::WebCryptoKeyUsageMask usages
) {
231 case blink::WebCryptoKeyFormatSpki
:
232 return CheckKeyCreationUsages(all_public_key_usages
, usages
, true);
233 case blink::WebCryptoKeyFormatPkcs8
:
234 return CheckKeyCreationUsages(all_private_key_usages
, usages
, false);
235 case blink::WebCryptoKeyFormatJwk
: {
236 // The JWK could represent either a public key or private key. The usages
237 // must make sense for one of the two. The usages will be checked again by
238 // ImportKeyJwk() once the key type has been determined.
239 if (CheckKeyCreationUsages(all_public_key_usages
, usages
, true)
241 CheckKeyCreationUsages(all_private_key_usages
, usages
, false)
243 return Status::ErrorCreateKeyBadUsages();
245 return Status::Success();
248 return Status::ErrorUnsupportedImportKeyFormat();
252 void TruncateToBitLength(size_t length_bits
, std::vector
<uint8_t>* bytes
) {
253 size_t length_bytes
= NumBitsToBytes(length_bits
);
255 if (bytes
->size() != length_bytes
) {
256 CHECK_LT(length_bytes
, bytes
->size());
257 bytes
->resize(length_bytes
);
260 size_t remainder_bits
= length_bits
% 8;
262 // Zero any "unused bits" in the final byte
264 (*bytes
)[bytes
->size() - 1] &= ~((0xFF) >> remainder_bits
);
267 Status
GetAesKeyLength(const blink::WebCryptoAlgorithm
& key_length_algorithm
,
268 bool* has_length_bits
,
269 unsigned int* length_bits
) {
270 const blink::WebCryptoAesDerivedKeyParams
* params
=
271 key_length_algorithm
.aesDerivedKeyParams();
273 *has_length_bits
= true;
274 *length_bits
= params
->lengthBits();
276 if (*length_bits
== 128 || *length_bits
== 256)
277 return Status::Success();
279 // BoringSSL does not support 192-bit AES.
280 if (*length_bits
== 192)
281 return Status::ErrorAes192BitUnsupported();
283 return Status::ErrorGetAesKeyLength();
286 Status
GetHmacKeyLength(const blink::WebCryptoAlgorithm
& key_length_algorithm
,
287 bool* has_length_bits
,
288 unsigned int* length_bits
) {
289 const blink::WebCryptoHmacImportParams
* params
=
290 key_length_algorithm
.hmacImportParams();
292 if (params
->hasLengthBits()) {
293 *has_length_bits
= true;
294 *length_bits
= params
->optionalLengthBits();
295 if (*length_bits
== 0)
296 return Status::ErrorGetHmacKeyLengthZero();
297 return Status::Success();
300 *has_length_bits
= true;
301 return GetShaBlockSizeBits(params
->hash(), length_bits
);
304 Status
GetUsagesForGenerateAsymmetricKey(
305 blink::WebCryptoKeyUsageMask combined_usages
,
306 blink::WebCryptoKeyUsageMask all_public_usages
,
307 blink::WebCryptoKeyUsageMask all_private_usages
,
308 blink::WebCryptoKeyUsageMask
* public_usages
,
309 blink::WebCryptoKeyUsageMask
* private_usages
) {
310 Status status
= CheckKeyCreationUsages(all_public_usages
| all_private_usages
,
311 combined_usages
, true);
312 if (status
.IsError())
315 *public_usages
= combined_usages
& all_public_usages
;
316 *private_usages
= combined_usages
& all_private_usages
;
318 if (*private_usages
== 0)
319 return Status::ErrorCreateKeyEmptyUsages();
321 return Status::Success();
324 } // namespace webcrypto