Refactor: Extract common code for generating asymmetric keys to a helper.
[chromium-blink-merge.git] / content / child / webcrypto / webcrypto_util.cc
blob5df16330b7cec68a5b53dc2a7f6f72f277e14145
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"
7 #include <set>
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"
17 namespace content {
19 namespace webcrypto {
21 namespace {
23 // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros,
24 // to unsigned int.
25 bool BigIntegerToUint(const uint8_t* data,
26 unsigned int data_size,
27 unsigned int* result) {
28 if (data_size == 0)
29 return false;
31 *result = 0;
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;
40 return true;
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();
54 default:
55 return Status::ErrorUnsupported();
59 } // namespace
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
68 // values".
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;
88 return true;
91 return false;
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;
100 *usages = 0;
101 for (size_t i = 0; i < key_ops->GetSize(); ++i) {
102 std::string key_op;
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.
111 if (*usages & usage)
112 return Status::ErrorJwkDuplicateKeyOps();
113 *usages |= usage;
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
118 // duplicates.
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);
134 return jwk_key_ops;
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,
148 length_bits));
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) {
176 return (a & b) == 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
235 // overflow.
236 unsigned int data_keylen_bits = 0;
238 base::CheckedNumeric<unsigned int> keylen_bits(key_data_byte_length);
239 keylen_bits *= 8;
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) {
312 switch (format) {
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();
327 default:
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
343 if (remainder_bits)
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,
391 combined_usages);
392 if (status.IsError())
393 return status;
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