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 // TODO(eroman): Move this helper to WebCryptoKey.
99 bool KeyUsageAllows(const blink::WebCryptoKey
& key
,
100 const blink::WebCryptoKeyUsage usage
) {
101 return ((key
.usages() & usage
) != 0);
104 // The WebCrypto spec defines the default value for the tag length, as well as
105 // the allowed values for tag length.
106 Status
GetAesGcmTagLengthInBits(const blink::WebCryptoAesGcmParams
* params
,
107 unsigned int* tag_length_bits
) {
108 *tag_length_bits
= 128;
109 if (params
->hasTagLengthBits())
110 *tag_length_bits
= params
->optionalTagLengthBits();
112 if (*tag_length_bits
!= 32 && *tag_length_bits
!= 64 &&
113 *tag_length_bits
!= 96 && *tag_length_bits
!= 104 &&
114 *tag_length_bits
!= 112 && *tag_length_bits
!= 120 &&
115 *tag_length_bits
!= 128)
116 return Status::ErrorInvalidAesGcmTagLength();
118 return Status::Success();
121 Status
GetAesKeyGenLengthInBits(const blink::WebCryptoAesKeyGenParams
* params
,
122 unsigned int* keylen_bits
) {
123 *keylen_bits
= params
->lengthBits();
125 if (*keylen_bits
== 128 || *keylen_bits
== 256)
126 return Status::Success();
128 // BoringSSL does not support 192-bit AES.
129 if (*keylen_bits
== 192)
130 return Status::ErrorAes192BitUnsupported();
132 return Status::ErrorGenerateAesKeyLength();
135 Status
GetHmacKeyGenLengthInBits(const blink::WebCryptoHmacKeyGenParams
* params
,
136 unsigned int* keylen_bits
) {
137 if (!params
->hasLengthBits())
138 return GetShaBlockSizeBits(params
->hash(), keylen_bits
);
140 *keylen_bits
= params
->optionalLengthBits();
142 // Zero-length HMAC keys are disallowed by the spec.
143 if (*keylen_bits
== 0)
144 return Status::ErrorGenerateHmacKeyLengthZero();
146 return Status::Success();
149 Status
GetHmacImportKeyLengthBits(
150 const blink::WebCryptoHmacImportParams
* params
,
151 unsigned int key_data_byte_length
,
152 unsigned int* keylen_bits
) {
153 if (key_data_byte_length
== 0)
154 return Status::ErrorHmacImportEmptyKey();
156 // Make sure that the key data's length can be represented in bits without
158 base::CheckedNumeric
<unsigned int> checked_keylen_bits(key_data_byte_length
);
159 checked_keylen_bits
*= 8;
161 if (!checked_keylen_bits
.IsValid())
162 return Status::ErrorDataTooLarge();
164 unsigned int data_keylen_bits
= checked_keylen_bits
.ValueOrDie();
166 // Determine how many bits of the input to use.
167 *keylen_bits
= data_keylen_bits
;
168 if (params
->hasLengthBits()) {
169 // The requested bit length must be:
170 // * No longer than the input data length
171 // * At most 7 bits shorter.
172 if (NumBitsToBytes(params
->optionalLengthBits()) != key_data_byte_length
)
173 return Status::ErrorHmacImportBadLength();
174 *keylen_bits
= params
->optionalLengthBits();
177 return Status::Success();
180 Status
VerifyAesKeyLengthForImport(unsigned int keylen_bytes
) {
181 if (keylen_bytes
== 16 || keylen_bytes
== 32)
182 return Status::Success();
184 // BoringSSL does not support 192-bit AES.
185 if (keylen_bytes
== 24)
186 return Status::ErrorAes192BitUnsupported();
188 return Status::ErrorImportAesKeyLength();
191 Status
CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages
,
192 blink::WebCryptoKeyUsageMask actual_usages
,
193 bool allow_empty_usages
) {
194 if (!allow_empty_usages
&& actual_usages
== 0)
195 return Status::ErrorCreateKeyEmptyUsages();
197 if (!ContainsKeyUsages(all_possible_usages
, actual_usages
))
198 return Status::ErrorCreateKeyBadUsages();
199 return Status::Success();
202 Status
GetRsaKeyGenParameters(
203 const blink::WebCryptoRsaHashedKeyGenParams
* params
,
204 unsigned int* public_exponent
,
205 unsigned int* modulus_length_bits
) {
206 *modulus_length_bits
= params
->modulusLengthBits();
208 // Limit key sizes to those supported by NSS:
209 // * Multiple of 8 bits
210 // * 256 bits to 16K bits
211 if (*modulus_length_bits
< 256 || *modulus_length_bits
> 16384 ||
212 (*modulus_length_bits
% 8) != 0) {
213 return Status::ErrorGenerateRsaUnsupportedModulus();
216 if (!BigIntegerToUint(params
->publicExponent().data(),
217 params
->publicExponent().size(), public_exponent
)) {
218 return Status::ErrorGenerateKeyPublicExponent();
221 // OpenSSL hangs when given bad public exponents, whereas NSS simply fails. To
222 // avoid feeding OpenSSL data that will hang use a whitelist.
223 if (*public_exponent
!= 3 && *public_exponent
!= 65537)
224 return Status::ErrorGenerateKeyPublicExponent();
226 return Status::Success();
229 Status
VerifyUsagesBeforeImportAsymmetricKey(
230 blink::WebCryptoKeyFormat format
,
231 blink::WebCryptoKeyUsageMask all_public_key_usages
,
232 blink::WebCryptoKeyUsageMask all_private_key_usages
,
233 blink::WebCryptoKeyUsageMask usages
) {
235 case blink::WebCryptoKeyFormatSpki
:
236 return CheckKeyCreationUsages(all_public_key_usages
, usages
, true);
237 case blink::WebCryptoKeyFormatPkcs8
:
238 return CheckKeyCreationUsages(all_private_key_usages
, usages
, false);
239 case blink::WebCryptoKeyFormatJwk
: {
240 // The JWK could represent either a public key or private key. The usages
241 // must make sense for one of the two. The usages will be checked again by
242 // ImportKeyJwk() once the key type has been determined.
243 if (CheckKeyCreationUsages(all_public_key_usages
, usages
, true)
245 CheckKeyCreationUsages(all_private_key_usages
, usages
, false)
247 return Status::ErrorCreateKeyBadUsages();
249 return Status::Success();
252 return Status::ErrorUnsupportedImportKeyFormat();
256 void TruncateToBitLength(size_t length_bits
, std::vector
<uint8_t>* bytes
) {
257 size_t length_bytes
= NumBitsToBytes(length_bits
);
259 if (bytes
->size() != length_bytes
) {
260 CHECK_LT(length_bytes
, bytes
->size());
261 bytes
->resize(length_bytes
);
264 size_t remainder_bits
= length_bits
% 8;
266 // Zero any "unused bits" in the final byte
268 (*bytes
)[bytes
->size() - 1] &= ~((0xFF) >> remainder_bits
);
271 Status
GetAesKeyLength(const blink::WebCryptoAlgorithm
& key_length_algorithm
,
272 bool* has_length_bits
,
273 unsigned int* length_bits
) {
274 const blink::WebCryptoAesDerivedKeyParams
* params
=
275 key_length_algorithm
.aesDerivedKeyParams();
277 *has_length_bits
= true;
278 *length_bits
= params
->lengthBits();
280 if (*length_bits
== 128 || *length_bits
== 256)
281 return Status::Success();
283 // BoringSSL does not support 192-bit AES.
284 if (*length_bits
== 192)
285 return Status::ErrorAes192BitUnsupported();
287 return Status::ErrorGetAesKeyLength();
290 Status
GetHmacKeyLength(const blink::WebCryptoAlgorithm
& key_length_algorithm
,
291 bool* has_length_bits
,
292 unsigned int* length_bits
) {
293 const blink::WebCryptoHmacImportParams
* params
=
294 key_length_algorithm
.hmacImportParams();
296 if (params
->hasLengthBits()) {
297 *has_length_bits
= true;
298 *length_bits
= params
->optionalLengthBits();
299 if (*length_bits
== 0)
300 return Status::ErrorGetHmacKeyLengthZero();
301 return Status::Success();
304 *has_length_bits
= true;
305 return GetShaBlockSizeBits(params
->hash(), length_bits
);
308 Status
GetUsagesForGenerateAsymmetricKey(
309 blink::WebCryptoKeyUsageMask combined_usages
,
310 blink::WebCryptoKeyUsageMask all_public_usages
,
311 blink::WebCryptoKeyUsageMask all_private_usages
,
312 blink::WebCryptoKeyUsageMask
* public_usages
,
313 blink::WebCryptoKeyUsageMask
* private_usages
) {
314 Status status
= CheckKeyCreationUsages(all_public_usages
| all_private_usages
,
315 combined_usages
, true);
316 if (status
.IsError())
319 *public_usages
= combined_usages
& all_public_usages
;
320 *private_usages
= combined_usages
& all_private_usages
;
322 if (*private_usages
== 0)
323 return Status::ErrorCreateKeyEmptyUsages();
325 return Status::Success();
328 } // namespace webcrypto