• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/child/webcrypto/webcrypto_util.h"
6 
7 #include "base/logging.h"
8 #include "base/strings/stringprintf.h"
9 #include "content/child/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"
13 
14 namespace content {
15 
16 namespace webcrypto {
17 
18 namespace {
19 
20 // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros,
21 // to unsigned int.
BigIntegerToUint(const uint8_t * data,unsigned int data_size,unsigned int * result)22 bool BigIntegerToUint(const uint8_t* data,
23                       unsigned int data_size,
24                       unsigned int* result) {
25   // TODO(eroman): Fix handling of empty biginteger. http://crbug.com/373552
26   if (data_size == 0)
27     return false;
28 
29   *result = 0;
30   for (size_t i = 0; i < data_size; ++i) {
31     size_t reverse_i = data_size - i - 1;
32 
33     if (reverse_i >= sizeof(*result) && data[i])
34       return false;  // Too large for a uint.
35 
36     *result |= data[i] << 8 * reverse_i;
37   }
38   return true;
39 }
40 
41 }  // namespace
42 
43 struct JwkToWebCryptoUsage {
44   const char* const jwk_key_op;
45   const blink::WebCryptoKeyUsage webcrypto_usage;
46 };
47 
48 // Keep this ordered according to the definition
49 // order of WebCrypto's "recognized key usage
50 // values".
51 //
52 // This is not required for spec compliance,
53 // however it makes the ordering of key_ops match
54 // that of WebCrypto's Key.usages.
55 const JwkToWebCryptoUsage kJwkWebCryptoUsageMap[] = {
56     {"encrypt", blink::WebCryptoKeyUsageEncrypt},
57     {"decrypt", blink::WebCryptoKeyUsageDecrypt},
58     {"sign", blink::WebCryptoKeyUsageSign},
59     {"verify", blink::WebCryptoKeyUsageVerify},
60     {"deriveKey", blink::WebCryptoKeyUsageDeriveKey},
61     {"deriveBits", blink::WebCryptoKeyUsageDeriveBits},
62     {"wrapKey", blink::WebCryptoKeyUsageWrapKey},
63     {"unwrapKey", blink::WebCryptoKeyUsageUnwrapKey}};
64 
65 // Modifies the input usage_mask by according to the key_op value.
JwkKeyOpToWebCryptoUsage(const std::string & key_op,blink::WebCryptoKeyUsageMask * usage_mask)66 bool JwkKeyOpToWebCryptoUsage(const std::string& key_op,
67                               blink::WebCryptoKeyUsageMask* usage_mask) {
68   for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) {
69     if (kJwkWebCryptoUsageMap[i].jwk_key_op == key_op) {
70       *usage_mask |= kJwkWebCryptoUsageMap[i].webcrypto_usage;
71       return true;
72     }
73   }
74   return false;
75 }
76 
77 // Composes a Web Crypto usage mask from an array of JWK key_ops values.
GetWebCryptoUsagesFromJwkKeyOps(const base::ListValue * jwk_key_ops_value,blink::WebCryptoKeyUsageMask * usage_mask)78 Status GetWebCryptoUsagesFromJwkKeyOps(
79     const base::ListValue* jwk_key_ops_value,
80     blink::WebCryptoKeyUsageMask* usage_mask) {
81   *usage_mask = 0;
82   for (size_t i = 0; i < jwk_key_ops_value->GetSize(); ++i) {
83     std::string key_op;
84     if (!jwk_key_ops_value->GetString(i, &key_op)) {
85       return Status::ErrorJwkPropertyWrongType(
86           base::StringPrintf("key_ops[%d]", static_cast<int>(i)), "string");
87     }
88     // Unrecognized key_ops are silently skipped.
89     ignore_result(JwkKeyOpToWebCryptoUsage(key_op, usage_mask));
90   }
91   return Status::Success();
92 }
93 
94 // Composes a JWK key_ops List from a Web Crypto usage mask.
95 // Note: Caller must assume ownership of returned instance.
CreateJwkKeyOpsFromWebCryptoUsages(blink::WebCryptoKeyUsageMask usage_mask)96 base::ListValue* CreateJwkKeyOpsFromWebCryptoUsages(
97     blink::WebCryptoKeyUsageMask usage_mask) {
98   base::ListValue* jwk_key_ops = new base::ListValue();
99   for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) {
100     if (usage_mask & kJwkWebCryptoUsageMap[i].webcrypto_usage)
101       jwk_key_ops->AppendString(kJwkWebCryptoUsageMap[i].jwk_key_op);
102   }
103   return jwk_key_ops;
104 }
105 
CreateAlgorithm(blink::WebCryptoAlgorithmId id)106 blink::WebCryptoAlgorithm CreateAlgorithm(blink::WebCryptoAlgorithmId id) {
107   return blink::WebCryptoAlgorithm::adoptParamsAndCreate(id, NULL);
108 }
109 
CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmId hash_id)110 blink::WebCryptoAlgorithm CreateHmacImportAlgorithm(
111     blink::WebCryptoAlgorithmId hash_id) {
112   DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id));
113   return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
114       blink::WebCryptoAlgorithmIdHmac,
115       new blink::WebCryptoHmacImportParams(CreateAlgorithm(hash_id)));
116 }
117 
CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmId id,blink::WebCryptoAlgorithmId hash_id)118 blink::WebCryptoAlgorithm CreateRsaHashedImportAlgorithm(
119     blink::WebCryptoAlgorithmId id,
120     blink::WebCryptoAlgorithmId hash_id) {
121   DCHECK(blink::WebCryptoAlgorithm::isHash(hash_id));
122   DCHECK(id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 ||
123          id == blink::WebCryptoAlgorithmIdRsaOaep);
124   return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
125       id, new blink::WebCryptoRsaHashedImportParams(CreateAlgorithm(hash_id)));
126 }
127 
ContainsKeyUsages(blink::WebCryptoKeyUsageMask a,blink::WebCryptoKeyUsageMask b)128 bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a,
129                        blink::WebCryptoKeyUsageMask b) {
130   return (a & b) == b;
131 }
132 
133 // TODO(eroman): Move this helper to WebCryptoKey.
KeyUsageAllows(const blink::WebCryptoKey & key,const blink::WebCryptoKeyUsage usage)134 bool KeyUsageAllows(const blink::WebCryptoKey& key,
135                     const blink::WebCryptoKeyUsage usage) {
136   return ((key.usages() & usage) != 0);
137 }
138 
IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id)139 bool IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id) {
140   return alg_id == blink::WebCryptoAlgorithmIdRsaOaep ||
141          alg_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5;
142 }
143 
IsAlgorithmAsymmetric(blink::WebCryptoAlgorithmId alg_id)144 bool IsAlgorithmAsymmetric(blink::WebCryptoAlgorithmId alg_id) {
145   // TODO(padolph): include all other asymmetric algorithms once they are
146   // defined, e.g. EC and DH.
147   return IsAlgorithmRsa(alg_id);
148 }
149 
150 // The WebCrypto spec defines the default value for the tag length, as well as
151 // the allowed values for tag length.
GetAesGcmTagLengthInBits(const blink::WebCryptoAesGcmParams * params,unsigned int * tag_length_bits)152 Status GetAesGcmTagLengthInBits(const blink::WebCryptoAesGcmParams* params,
153                                 unsigned int* tag_length_bits) {
154   *tag_length_bits = 128;
155   if (params->hasTagLengthBits())
156     *tag_length_bits = params->optionalTagLengthBits();
157 
158   if (*tag_length_bits != 32 && *tag_length_bits != 64 &&
159       *tag_length_bits != 96 && *tag_length_bits != 104 &&
160       *tag_length_bits != 112 && *tag_length_bits != 120 &&
161       *tag_length_bits != 128)
162     return Status::ErrorInvalidAesGcmTagLength();
163 
164   return Status::Success();
165 }
166 
GetAesKeyGenLengthInBits(const blink::WebCryptoAesKeyGenParams * params,unsigned int * keylen_bits)167 Status GetAesKeyGenLengthInBits(const blink::WebCryptoAesKeyGenParams* params,
168                                 unsigned int* keylen_bits) {
169   *keylen_bits = params->lengthBits();
170 
171   if (*keylen_bits == 128 || *keylen_bits == 256)
172     return Status::Success();
173 
174   // BoringSSL does not support 192-bit AES.
175   if (*keylen_bits == 192)
176     return Status::ErrorAes192BitUnsupported();
177 
178   return Status::ErrorGenerateKeyLength();
179 }
180 
GetHmacKeyGenLengthInBits(const blink::WebCryptoHmacKeyGenParams * params,unsigned int * keylen_bits)181 Status GetHmacKeyGenLengthInBits(const blink::WebCryptoHmacKeyGenParams* params,
182                                  unsigned int* keylen_bits) {
183   if (!params->hasLengthBits()) {
184     switch (params->hash().id()) {
185       case blink::WebCryptoAlgorithmIdSha1:
186       case blink::WebCryptoAlgorithmIdSha256:
187         *keylen_bits = 512;
188         return Status::Success();
189       case blink::WebCryptoAlgorithmIdSha384:
190       case blink::WebCryptoAlgorithmIdSha512:
191         *keylen_bits = 1024;
192         return Status::Success();
193       default:
194         return Status::ErrorUnsupported();
195     }
196   }
197 
198   if (params->optionalLengthBits() % 8)
199     return Status::ErrorGenerateKeyLength();
200 
201   *keylen_bits = params->optionalLengthBits();
202 
203   // TODO(eroman): NSS fails when generating a zero-length secret key.
204   if (*keylen_bits == 0)
205     return Status::ErrorGenerateKeyLength();
206 
207   return Status::Success();
208 }
209 
VerifyAesKeyLengthForImport(unsigned int keylen_bytes)210 Status VerifyAesKeyLengthForImport(unsigned int keylen_bytes) {
211   if (keylen_bytes == 16 || keylen_bytes == 32)
212     return Status::Success();
213 
214   // BoringSSL does not support 192-bit AES.
215   if (keylen_bytes == 24)
216     return Status::ErrorAes192BitUnsupported();
217 
218   return Status::ErrorImportAesKeyLength();
219 }
220 
CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages,blink::WebCryptoKeyUsageMask actual_usages)221 Status CheckKeyCreationUsages(blink::WebCryptoKeyUsageMask all_possible_usages,
222                               blink::WebCryptoKeyUsageMask actual_usages) {
223   if (!ContainsKeyUsages(all_possible_usages, actual_usages))
224     return Status::ErrorCreateKeyBadUsages();
225   return Status::Success();
226 }
227 
GetRsaKeyGenParameters(const blink::WebCryptoRsaHashedKeyGenParams * params,unsigned int * public_exponent,unsigned int * modulus_length_bits)228 Status GetRsaKeyGenParameters(
229     const blink::WebCryptoRsaHashedKeyGenParams* params,
230     unsigned int* public_exponent,
231     unsigned int* modulus_length_bits) {
232   *modulus_length_bits = params->modulusLengthBits();
233 
234   // Limit key sizes to those supported by NSS:
235   //   * Multiple of 8 bits
236   //   * 256 bits to 16K bits
237   if (*modulus_length_bits < 256 || *modulus_length_bits > 16384 ||
238       (*modulus_length_bits % 8) != 0) {
239     return Status::ErrorGenerateRsaUnsupportedModulus();
240   }
241 
242   if (!BigIntegerToUint(params->publicExponent().data(),
243                         params->publicExponent().size(),
244                         public_exponent)) {
245     return Status::ErrorGenerateKeyPublicExponent();
246   }
247 
248   // OpenSSL hangs when given bad public exponents, whereas NSS simply fails. To
249   // avoid feeding OpenSSL data that will hang use a whitelist.
250   if (*public_exponent != 3 && *public_exponent != 65537)
251     return Status::ErrorGenerateKeyPublicExponent();
252 
253   return Status::Success();
254 }
255 
256 }  // namespace webcrypto
257 
258 }  // namespace content
259