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 "content/child/webcrypto/webcrypto_util.h"
9 #include "base/logging.h"
10 #include "base/numerics/safe_math.h"
11 #include "base/strings/stringprintf.h"
12 #include "content/child/webcrypto/status.h"
13 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
14 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
15 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
23 // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros,
25 bool BigIntegerToUint(const uint8_t* data
,
26 unsigned int data_size
,
27 unsigned int* result
) {
32 for (size_t i
= 0; i
< data_size
; ++i
) {
33 size_t reverse_i
= data_size
- i
- 1;
35 if (reverse_i
>= sizeof(*result
) && data
[i
])
36 return false; // Too large for a uint.
38 *result
|= data
[i
] << 8 * reverse_i
;
43 Status
GetShaBlockSizeBits(const blink::WebCryptoAlgorithm
& algorithm
,
44 unsigned int* block_size_bits
) {
45 switch (algorithm
.id()) {
46 case blink::WebCryptoAlgorithmIdSha1
:
47 case blink::WebCryptoAlgorithmIdSha256
:
48 *block_size_bits
= 512;
49 return Status::Success();
50 case blink::WebCryptoAlgorithmIdSha384
:
51 case blink::WebCryptoAlgorithmIdSha512
:
52 *block_size_bits
= 1024;
53 return Status::Success();
55 return Status::ErrorUnsupported();
61 struct JwkToWebCryptoUsage
{
62 const char* const jwk_key_op
;
63 const blink::WebCryptoKeyUsage webcrypto_usage
;
66 // Keep this ordered according to the definition
67 // order of WebCrypto's "recognized key usage
70 // This is not required for spec compliance,
71 // however it makes the ordering of key_ops match
72 // that of WebCrypto's Key.usages.
73 const JwkToWebCryptoUsage kJwkWebCryptoUsageMap
[] = {
74 {"encrypt", blink::WebCryptoKeyUsageEncrypt
},
75 {"decrypt", blink::WebCryptoKeyUsageDecrypt
},
76 {"sign", blink::WebCryptoKeyUsageSign
},
77 {"verify", blink::WebCryptoKeyUsageVerify
},
78 {"deriveKey", blink::WebCryptoKeyUsageDeriveKey
},
79 {"deriveBits", blink::WebCryptoKeyUsageDeriveBits
},
80 {"wrapKey", blink::WebCryptoKeyUsageWrapKey
},
81 {"unwrapKey", blink::WebCryptoKeyUsageUnwrapKey
}};
83 bool JwkKeyOpToWebCryptoUsage(const std::string
& key_op
,
84 blink::WebCryptoKeyUsage
* usage
) {
85 for (size_t i
= 0; i
< arraysize(kJwkWebCryptoUsageMap
); ++i
) {
86 if (kJwkWebCryptoUsageMap
[i
].jwk_key_op
== key_op
) {
87 *usage
= kJwkWebCryptoUsageMap
[i
].webcrypto_usage
;
94 // Composes a Web Crypto usage mask from an array of JWK key_ops values.
95 Status
GetWebCryptoUsagesFromJwkKeyOps(const base::ListValue
* key_ops
,
96 blink::WebCryptoKeyUsageMask
* usages
) {
97 // This set keeps track of all unrecognized key_ops values.
98 std::set
<std::string
> unrecognized_usages
;
101 for (size_t i
= 0; i
< key_ops
->GetSize(); ++i
) {
103 if (!key_ops
->GetString(i
, &key_op
)) {
104 return Status::ErrorJwkMemberWrongType(
105 base::StringPrintf("key_ops[%d]", static_cast<int>(i
)), "string");
108 blink::WebCryptoKeyUsage usage
;
109 if (JwkKeyOpToWebCryptoUsage(key_op
, &usage
)) {
110 // Ensure there are no duplicate usages.
112 return Status::ErrorJwkDuplicateKeyOps();
116 // Reaching here means the usage was unrecognized. Such usages are skipped
117 // over, however they are kept track of in a set to ensure there were no
119 if (!unrecognized_usages
.insert(key_op
).second
)
120 return Status::ErrorJwkDuplicateKeyOps();
122 return Status::Success();
125 // Composes a JWK key_ops List from a Web Crypto usage mask.
126 // Note: Caller must assume ownership of returned instance.
127 base::ListValue
* CreateJwkKeyOpsFromWebCryptoUsages(
128 blink::WebCryptoKeyUsageMask usages
) {
129 base::ListValue
* jwk_key_ops
= new base::ListValue();
130 for (size_t i
= 0; i
< arraysize(kJwkWebCryptoUsageMap
); ++i
) {
131 if (usages
& kJwkWebCryptoUsageMap
[i
].webcrypto_usage
)
132 jwk_key_ops
->AppendString(kJwkWebCryptoUsageMap
[i
].jwk_key_op
);
137 blink::WebCryptoAlgorithm
CreateAlgorithm(blink::WebCryptoAlgorithmId id
) {
138 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(id
, NULL
);
141 blink::WebCryptoAlgorithm
CreateHmacImportAlgorithm(
142 blink::WebCryptoAlgorithmId hash_id
,
143 unsigned int length_bits
) {
144 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id
));
145 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
146 blink::WebCryptoAlgorithmIdHmac
,
147 new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id
), true,
151 blink::WebCryptoAlgorithm
CreateHmacImportAlgorithmNoLength(
152 blink::WebCryptoAlgorithmId hash_id
) {
153 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id
));
154 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
155 blink::WebCryptoAlgorithmIdHmac
,
156 new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id
), false, 0));
159 blink::WebCryptoAlgorithm
CreateRsaHashedImportAlgorithm(
160 blink::WebCryptoAlgorithmId id
,
161 blink::WebCryptoAlgorithmId hash_id
) {
162 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id
));
163 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
164 id
, new blink::WebCryptoRsaHashedImportParams(CreateAlgorithm(hash_id
)));
167 blink::WebCryptoAlgorithm
CreateEcImportAlgorithm(
168 blink::WebCryptoAlgorithmId id
,
169 blink::WebCryptoNamedCurve named_curve
) {
170 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
171 id
, new blink::WebCryptoEcKeyImportParams(named_curve
));
174 bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a
,
175 blink::WebCryptoKeyUsageMask b
) {
179 // TODO(eroman): Move this helper to WebCryptoKey.
180 bool KeyUsageAllows(const blink::WebCryptoKey
& key
,
181 const blink::WebCryptoKeyUsage usage
) {
182 return ((key
.usages() & usage
) != 0);
185 // The WebCrypto spec defines the default value for the tag length, as well as
186 // the allowed values for tag length.
187 Status
GetAesGcmTagLengthInBits(const blink::WebCryptoAesGcmParams
* params
,
188 unsigned int* tag_length_bits
) {
189 *tag_length_bits
= 128;
190 if (params
->hasTagLengthBits())
191 *tag_length_bits
= params
->optionalTagLengthBits();
193 if (*tag_length_bits
!= 32 && *tag_length_bits
!= 64 &&
194 *tag_length_bits
!= 96 && *tag_length_bits
!= 104 &&
195 *tag_length_bits
!= 112 && *tag_length_bits
!= 120 &&
196 *tag_length_bits
!= 128)
197 return Status::ErrorInvalidAesGcmTagLength();
199 return Status::Success();
202 Status
GetAesKeyGenLengthInBits(const blink::WebCryptoAesKeyGenParams
* params
,
203 unsigned int* keylen_bits
) {
204 *keylen_bits
= params
->lengthBits();
206 if (*keylen_bits
== 128 || *keylen_bits
== 256)
207 return Status::Success();
209 // BoringSSL does not support 192-bit AES.
210 if (*keylen_bits
== 192)
211 return Status::ErrorAes192BitUnsupported();
213 return Status::ErrorGenerateAesKeyLength();
216 Status
GetHmacKeyGenLengthInBits(const blink::WebCryptoHmacKeyGenParams
* params
,
217 unsigned int* keylen_bits
) {
218 if (!params
->hasLengthBits())
219 return GetShaBlockSizeBits(params
->hash(), keylen_bits
);
221 *keylen_bits
= params
->optionalLengthBits();
223 // Zero-length HMAC keys are disallowed by the spec.
224 if (*keylen_bits
== 0)
225 return Status::ErrorGenerateHmacKeyLengthZero();
227 return Status::Success();
230 Status
GetHmacImportKeyLengthBits(
231 const blink::WebCryptoHmacImportParams
* params
,
232 unsigned int key_data_byte_length
,
233 unsigned int* keylen_bits
) {
234 // Make sure that the key data's length can be represented in bits without
236 unsigned int data_keylen_bits
= 0;
238 base::CheckedNumeric
<unsigned int> keylen_bits(key_data_byte_length
);
241 if (!keylen_bits
.IsValid())
242 return Status::ErrorDataTooLarge();
244 data_keylen_bits
= keylen_bits
.ValueOrDie();
247 // Determine how many bits of the input to use.
248 *keylen_bits
= data_keylen_bits
;
249 if (params
->hasLengthBits()) {
250 // The requested bit length must be:
251 // * No longer than the input data length
252 // * At most 7 bits shorter.
253 if (NumBitsToBytes(params
->optionalLengthBits()) != key_data_byte_length
) {
254 return Status::ErrorHmacImportBadLength();
256 *keylen_bits
= params
->optionalLengthBits();
259 return Status::Success();
262 Status
VerifyAesKeyLengthForImport(unsigned int keylen_bytes
) {
263 if (keylen_bytes
== 16 || keylen_bytes
== 32)
264 return Status::Success();
266 // BoringSSL does not support 192-bit AES.
267 if (keylen_bytes
== 24)
268 return Status::ErrorAes192BitUnsupported();
270 return Status::ErrorImportAesKeyLength();
273 Status
CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages
,
274 blink::WebCryptoKeyUsageMask actual_usages
) {
275 if (!ContainsKeyUsages(all_possible_usages
, actual_usages
))
276 return Status::ErrorCreateKeyBadUsages();
277 return Status::Success();
280 Status
GetRsaKeyGenParameters(
281 const blink::WebCryptoRsaHashedKeyGenParams
* params
,
282 unsigned int* public_exponent
,
283 unsigned int* modulus_length_bits
) {
284 *modulus_length_bits
= params
->modulusLengthBits();
286 // Limit key sizes to those supported by NSS:
287 // * Multiple of 8 bits
288 // * 256 bits to 16K bits
289 if (*modulus_length_bits
< 256 || *modulus_length_bits
> 16384 ||
290 (*modulus_length_bits
% 8) != 0) {
291 return Status::ErrorGenerateRsaUnsupportedModulus();
294 if (!BigIntegerToUint(params
->publicExponent().data(),
295 params
->publicExponent().size(), public_exponent
)) {
296 return Status::ErrorGenerateKeyPublicExponent();
299 // OpenSSL hangs when given bad public exponents, whereas NSS simply fails. To
300 // avoid feeding OpenSSL data that will hang use a whitelist.
301 if (*public_exponent
!= 3 && *public_exponent
!= 65537)
302 return Status::ErrorGenerateKeyPublicExponent();
304 return Status::Success();
307 Status
VerifyUsagesBeforeImportAsymmetricKey(
308 blink::WebCryptoKeyFormat format
,
309 blink::WebCryptoKeyUsageMask all_public_key_usages
,
310 blink::WebCryptoKeyUsageMask all_private_key_usages
,
311 blink::WebCryptoKeyUsageMask usages
) {
313 case blink::WebCryptoKeyFormatSpki
:
314 return CheckKeyCreationUsages(all_public_key_usages
, usages
);
315 case blink::WebCryptoKeyFormatPkcs8
:
316 return CheckKeyCreationUsages(all_private_key_usages
, usages
);
317 case blink::WebCryptoKeyFormatJwk
: {
318 // The JWK could represent either a public key or private key. The usages
319 // must make sense for one of the two. The usages will be checked again by
320 // ImportKeyJwk() once the key type has been determined.
321 if (CheckKeyCreationUsages(all_public_key_usages
, usages
).IsError() &&
322 CheckKeyCreationUsages(all_private_key_usages
, usages
).IsError()) {
323 return Status::ErrorCreateKeyBadUsages();
325 return Status::Success();
328 return Status::ErrorUnsupportedImportKeyFormat();
332 void TruncateToBitLength(size_t length_bits
, std::vector
<uint8_t>* bytes
) {
333 size_t length_bytes
= NumBitsToBytes(length_bits
);
335 if (bytes
->size() != length_bytes
) {
336 CHECK_LT(length_bytes
, bytes
->size());
337 bytes
->resize(length_bytes
);
340 size_t remainder_bits
= length_bits
% 8;
342 // Zero any "unused bits" in the final byte
344 (*bytes
)[bytes
->size() - 1] &= ~((0xFF) >> remainder_bits
);
347 Status
GetAesKeyLength(const blink::WebCryptoAlgorithm
& key_length_algorithm
,
348 bool* has_length_bits
,
349 unsigned int* length_bits
) {
350 const blink::WebCryptoAesDerivedKeyParams
* params
=
351 key_length_algorithm
.aesDerivedKeyParams();
353 *has_length_bits
= true;
354 *length_bits
= params
->lengthBits();
356 if (*length_bits
== 128 || *length_bits
== 256)
357 return Status::Success();
359 // BoringSSL does not support 192-bit AES.
360 if (*length_bits
== 192)
361 return Status::ErrorAes192BitUnsupported();
363 return Status::ErrorGetAesKeyLength();
366 Status
GetHmacKeyLength(const blink::WebCryptoAlgorithm
& key_length_algorithm
,
367 bool* has_length_bits
,
368 unsigned int* length_bits
) {
369 const blink::WebCryptoHmacImportParams
* params
=
370 key_length_algorithm
.hmacImportParams();
372 if (params
->hasLengthBits()) {
373 *has_length_bits
= true;
374 *length_bits
= params
->optionalLengthBits();
375 if (*length_bits
== 0)
376 return Status::ErrorGetHmacKeyLengthZero();
377 return Status::Success();
380 *has_length_bits
= true;
381 return GetShaBlockSizeBits(params
->hash(), length_bits
);
384 Status
GetUsagesForGenerateAsymmetricKey(
385 blink::WebCryptoKeyUsageMask combined_usages
,
386 blink::WebCryptoKeyUsageMask all_public_usages
,
387 blink::WebCryptoKeyUsageMask all_private_usages
,
388 blink::WebCryptoKeyUsageMask
* public_usages
,
389 blink::WebCryptoKeyUsageMask
* private_usages
) {
390 Status status
= CheckKeyCreationUsages(all_public_usages
| all_private_usages
,
392 if (status
.IsError())
395 *public_usages
= combined_usages
& all_public_usages
;
396 *private_usages
= combined_usages
& all_private_usages
;
398 if (*private_usages
== 0)
399 return Status::ErrorCreateKeyEmptyUsages();
401 return Status::Success();
404 } // namespace webcrypto
406 } // namespace content