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/strings/stringprintf.h"
11 #include "content/child/webcrypto/status.h"
12 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
13 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
14 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
22 // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros,
24 bool BigIntegerToUint(const uint8_t* data,
25 unsigned int data_size,
26 unsigned int* result) {
31 for (size_t i = 0; i < data_size; ++i) {
32 size_t reverse_i = data_size - i - 1;
34 if (reverse_i >= sizeof(*result) && data[i])
35 return false; // Too large for a uint.
37 *result |= data[i] << 8 * reverse_i;
44 struct JwkToWebCryptoUsage {
45 const char* const jwk_key_op;
46 const blink::WebCryptoKeyUsage webcrypto_usage;
49 // Keep this ordered according to the definition
50 // order of WebCrypto's "recognized key usage
53 // This is not required for spec compliance,
54 // however it makes the ordering of key_ops match
55 // that of WebCrypto's Key.usages.
56 const JwkToWebCryptoUsage kJwkWebCryptoUsageMap[] = {
57 {"encrypt", blink::WebCryptoKeyUsageEncrypt},
58 {"decrypt", blink::WebCryptoKeyUsageDecrypt},
59 {"sign", blink::WebCryptoKeyUsageSign},
60 {"verify", blink::WebCryptoKeyUsageVerify},
61 {"deriveKey", blink::WebCryptoKeyUsageDeriveKey},
62 {"deriveBits", blink::WebCryptoKeyUsageDeriveBits},
63 {"wrapKey", blink::WebCryptoKeyUsageWrapKey},
64 {"unwrapKey", blink::WebCryptoKeyUsageUnwrapKey}};
66 bool JwkKeyOpToWebCryptoUsage(const std::string& key_op,
67 blink::WebCryptoKeyUsage* usage) {
68 for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) {
69 if (kJwkWebCryptoUsageMap[i].jwk_key_op == key_op) {
70 *usage = kJwkWebCryptoUsageMap[i].webcrypto_usage;
77 // Composes a Web Crypto usage mask from an array of JWK key_ops values.
78 Status GetWebCryptoUsagesFromJwkKeyOps(const base::ListValue* key_ops,
79 blink::WebCryptoKeyUsageMask* usages) {
80 // This set keeps track of all unrecognized key_ops values.
81 std::set<std::string> unrecognized_usages;
84 for (size_t i = 0; i < key_ops->GetSize(); ++i) {
86 if (!key_ops->GetString(i, &key_op)) {
87 return Status::ErrorJwkPropertyWrongType(
88 base::StringPrintf("key_ops[%d]", static_cast<int>(i)), "string");
91 blink::WebCryptoKeyUsage usage;
92 if (JwkKeyOpToWebCryptoUsage(key_op, &usage)) {
93 // Ensure there are no duplicate usages.
95 return Status::ErrorJwkDuplicateKeyOps();
99 // Reaching here means the usage was unrecognized. Such usages are skipped
100 // over, however they are kept track of in a set to ensure there were no
102 if (!unrecognized_usages.insert(key_op).second)
103 return Status::ErrorJwkDuplicateKeyOps();
105 return Status::Success();
108 // Composes a JWK key_ops List from a Web Crypto usage mask.
109 // Note: Caller must assume ownership of returned instance.
110 base::ListValue* CreateJwkKeyOpsFromWebCryptoUsages(
111 blink::WebCryptoKeyUsageMask usages) {
112 base::ListValue* jwk_key_ops = new base::ListValue();
113 for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) {
114 if (usages & kJwkWebCryptoUsageMap[i].webcrypto_usage)
115 jwk_key_ops->AppendString(kJwkWebCryptoUsageMap[i].jwk_key_op);
120 blink::WebCryptoAlgorithm CreateAlgorithm(blink::WebCryptoAlgorithmId id) {
121 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(id, NULL);
124 blink::WebCryptoAlgorithm CreateHmacImportAlgorithm(
125 blink::WebCryptoAlgorithmId hash_id) {
126 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id));
127 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
128 blink::WebCryptoAlgorithmIdHmac,
129 new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id)));
132 blink::WebCryptoAlgorithm CreateRsaHashedImportAlgorithm(
133 blink::WebCryptoAlgorithmId id,
134 blink::WebCryptoAlgorithmId hash_id) {
135 DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id));
136 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
137 id, new blink::WebCryptoRsaHashedImportParams(CreateAlgorithm(hash_id)));
140 bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a,
141 blink::WebCryptoKeyUsageMask b) {
145 // TODO(eroman): Move this helper to WebCryptoKey.
146 bool KeyUsageAllows(const blink::WebCryptoKey& key,
147 const blink::WebCryptoKeyUsage usage) {
148 return ((key.usages() & usage) != 0);
151 // The WebCrypto spec defines the default value for the tag length, as well as
152 // the allowed values for tag length.
153 Status GetAesGcmTagLengthInBits(const blink::WebCryptoAesGcmParams* params,
154 unsigned int* tag_length_bits) {
155 *tag_length_bits = 128;
156 if (params->hasTagLengthBits())
157 *tag_length_bits = params->optionalTagLengthBits();
159 if (*tag_length_bits != 32 && *tag_length_bits != 64 &&
160 *tag_length_bits != 96 && *tag_length_bits != 104 &&
161 *tag_length_bits != 112 && *tag_length_bits != 120 &&
162 *tag_length_bits != 128)
163 return Status::ErrorInvalidAesGcmTagLength();
165 return Status::Success();
168 Status GetAesKeyGenLengthInBits(const blink::WebCryptoAesKeyGenParams* params,
169 unsigned int* keylen_bits) {
170 *keylen_bits = params->lengthBits();
172 if (*keylen_bits == 128 || *keylen_bits == 256)
173 return Status::Success();
175 // BoringSSL does not support 192-bit AES.
176 if (*keylen_bits == 192)
177 return Status::ErrorAes192BitUnsupported();
179 return Status::ErrorGenerateKeyLength();
182 Status GetHmacKeyGenLengthInBits(const blink::WebCryptoHmacKeyGenParams* params,
183 unsigned int* keylen_bits) {
184 if (!params->hasLengthBits()) {
185 switch (params->hash().id()) {
186 case blink::WebCryptoAlgorithmIdSha1:
187 case blink::WebCryptoAlgorithmIdSha256:
189 return Status::Success();
190 case blink::WebCryptoAlgorithmIdSha384:
191 case blink::WebCryptoAlgorithmIdSha512:
193 return Status::Success();
195 return Status::ErrorUnsupported();
199 if (params->optionalLengthBits() % 8)
200 return Status::ErrorGenerateKeyLength();
202 *keylen_bits = params->optionalLengthBits();
204 // TODO(eroman): NSS fails when generating a zero-length secret key.
205 if (*keylen_bits == 0)
206 return Status::ErrorGenerateKeyLength();
208 return Status::Success();
211 Status VerifyAesKeyLengthForImport(unsigned int keylen_bytes) {
212 if (keylen_bytes == 16 || keylen_bytes == 32)
213 return Status::Success();
215 // BoringSSL does not support 192-bit AES.
216 if (keylen_bytes == 24)
217 return Status::ErrorAes192BitUnsupported();
219 return Status::ErrorImportAesKeyLength();
222 Status CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages,
223 blink::WebCryptoKeyUsageMask actual_usages) {
224 if (!ContainsKeyUsages(all_possible_usages, actual_usages))
225 return Status::ErrorCreateKeyBadUsages();
226 return Status::Success();
229 Status GetRsaKeyGenParameters(
230 const blink::WebCryptoRsaHashedKeyGenParams* params,
231 unsigned int* public_exponent,
232 unsigned int* modulus_length_bits) {
233 *modulus_length_bits = params->modulusLengthBits();
235 // Limit key sizes to those supported by NSS:
236 // * Multiple of 8 bits
237 // * 256 bits to 16K bits
238 if (*modulus_length_bits < 256 || *modulus_length_bits > 16384 ||
239 (*modulus_length_bits % 8) != 0) {
240 return Status::ErrorGenerateRsaUnsupportedModulus();
243 if (!BigIntegerToUint(params->publicExponent().data(),
244 params->publicExponent().size(),
246 return Status::ErrorGenerateKeyPublicExponent();
249 // OpenSSL hangs when given bad public exponents, whereas NSS simply fails. To
250 // avoid feeding OpenSSL data that will hang use a whitelist.
251 if (*public_exponent != 3 && *public_exponent != 65537)
252 return Status::ErrorGenerateKeyPublicExponent();
254 return Status::Success();
257 } // namespace webcrypto
259 } // namespace content