• Home
  • History
  • Annotate
  • 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 "jwk.h"
6 
7 #include <algorithm>
8 #include <functional>
9 #include <map>
10 
11 #include "base/base64.h"
12 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_piece.h"
16 #include "content/child/webcrypto/crypto_data.h"
17 #include "content/child/webcrypto/status.h"
18 #include "content/child/webcrypto/webcrypto_util.h"
19 
20 // TODO(eroman): The algorithm-specific logic in this file for AES and RSA
21 // should be moved into the corresponding AlgorithmImplementation file. It
22 // exists in this file to avoid duplication between OpenSSL and NSS
23 // implementations.
24 
25 // JSON Web Key Format (JWK)
26 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21
27 //
28 // A JWK is a simple JSON dictionary with the following entries
29 // - "kty" (Key Type) Parameter, REQUIRED
30 // - <kty-specific parameters, see below>, REQUIRED
31 // - "use" (Key Use) Parameter, OPTIONAL
32 // - "key_ops" (Key Operations) Parameter, OPTIONAL
33 // - "alg" (Algorithm) Parameter, OPTIONAL
34 // - "ext" (Key Exportability), OPTIONAL
35 // (all other entries are ignored)
36 //
37 // OPTIONAL here means that this code does not require the entry to be present
38 // in the incoming JWK, because the method input parameters contain similar
39 // information. If the optional JWK entry is present, it will be validated
40 // against the corresponding input parameter for consistency and combined with
41 // it according to rules defined below.
42 //
43 // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK
44 // values are parsed out and combined with the method input parameters to
45 // build a Web Crypto Key:
46 // Web Crypto Key type            <-- (deduced)
47 // Web Crypto Key extractable     <-- JWK ext + input extractable
48 // Web Crypto Key algorithm       <-- JWK alg + input algorithm
49 // Web Crypto Key keyUsage        <-- JWK use, key_ops + input usage_mask
50 // Web Crypto Key keying material <-- kty-specific parameters
51 //
52 // Values for each JWK entry are case-sensitive and defined in
53 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18.
54 // Note that not all values specified by JOSE are handled by this code. Only
55 // handled values are listed.
56 // - kty (Key Type)
57 //   +-------+--------------------------------------------------------------+
58 //   | "RSA" | RSA [RFC3447]                                                |
59 //   | "oct" | Octet sequence (used to represent symmetric keys)            |
60 //   +-------+--------------------------------------------------------------+
61 //
62 // - key_ops (Key Use Details)
63 //   The key_ops field is an array that contains one or more strings from
64 //   the table below, and describes the operations for which this key may be
65 //   used.
66 //   +-------+--------------------------------------------------------------+
67 //   | "encrypt"    | encrypt operations                                    |
68 //   | "decrypt"    | decrypt operations                                    |
69 //   | "sign"       | sign (MAC) operations                                 |
70 //   | "verify"     | verify (MAC) operations                               |
71 //   | "wrapKey"    | key wrap                                              |
72 //   | "unwrapKey"  | key unwrap                                            |
73 //   | "deriveKey"  | key derivation                                        |
74 //   | "deriveBits" | key derivation                                        |
75 //   +-------+--------------------------------------------------------------+
76 //
77 // - use (Key Use)
78 //   The use field contains a single entry from the table below.
79 //   +-------+--------------------------------------------------------------+
80 //   | "sig"     | equivalent to key_ops of [sign, verify]                  |
81 //   | "enc"     | equivalent to key_ops of [encrypt, decrypt, wrapKey,     |
82 //   |           | unwrapKey, deriveKey, deriveBits]                        |
83 //   +-------+--------------------------------------------------------------+
84 //
85 //   NOTE: If both "use" and "key_ops" JWK members are present, the usages
86 //   specified by them MUST be consistent.  In particular, the "use" value
87 //   "sig" corresponds to "sign" and/or "verify".  The "use" value "enc"
88 //   corresponds to all other values defined above.  If "key_ops" values
89 //   corresponding to both "sig" and "enc" "use" values are present, the "use"
90 //   member SHOULD NOT be present, and if present, its value MUST NOT be
91 //   either "sig" or "enc".
92 //
93 // - ext (Key Exportability)
94 //   +-------+--------------------------------------------------------------+
95 //   | true  | Key may be exported from the trusted environment             |
96 //   | false | Key cannot exit the trusted environment                      |
97 //   +-------+--------------------------------------------------------------+
98 //
99 // - alg (Algorithm)
100 //   See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18
101 //   +--------------+-------------------------------------------------------+
102 //   | Digital Signature or MAC Algorithm                                   |
103 //   +--------------+-------------------------------------------------------+
104 //   | "HS1"        | HMAC using SHA-1 hash algorithm                       |
105 //   | "HS256"      | HMAC using SHA-256 hash algorithm                     |
106 //   | "HS384"      | HMAC using SHA-384 hash algorithm                     |
107 //   | "HS512"      | HMAC using SHA-512 hash algorithm                     |
108 //   | "RS1"        | RSASSA using SHA-1 hash algorithm
109 //   | "RS256"      | RSASSA using SHA-256 hash algorithm                   |
110 //   | "RS384"      | RSASSA using SHA-384 hash algorithm                   |
111 //   | "RS512"      | RSASSA using SHA-512 hash algorithm                   |
112 //   +--------------+-------------------------------------------------------|
113 //   | Key Management Algorithm                                             |
114 //   +--------------+-------------------------------------------------------+
115 //   | "RSA-OAEP"   | RSAES using Optimal Asymmetric Encryption Padding     |
116 //   |              | (OAEP) [RFC3447], with the default parameters         |
117 //   |              | specified by RFC3447 in Section A.2.1                 |
118 //   | "A128KW"     | Advanced Encryption Standard (AES) Key Wrap Algorithm |
119 //   |              | [RFC3394] using 128 bit keys                          |
120 //   | "A192KW"     | AES Key Wrap Algorithm using 192 bit keys             |
121 //   | "A256KW"     | AES Key Wrap Algorithm using 256 bit keys             |
122 //   | "A128GCM"    | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using |
123 //   |              | 128 bit keys                                          |
124 //   | "A192GCM"    | AES GCM using 192 bit keys                            |
125 //   | "A256GCM"    | AES GCM using 256 bit keys                            |
126 //   | "A128CBC"    | AES in Cipher Block Chaining Mode (CBC) with PKCS #5  |
127 //   |              | padding [NIST.800-38A]                                |
128 //   | "A192CBC"    | AES CBC using 192 bit keys                            |
129 //   | "A256CBC"    | AES CBC using 256 bit keys                            |
130 //   +--------------+-------------------------------------------------------+
131 //
132 // kty-specific parameters
133 // The value of kty determines the type and content of the keying material
134 // carried in the JWK to be imported.
135 // // - kty == "oct" (symmetric or other raw key)
136 //   +-------+--------------------------------------------------------------+
137 //   | "k"   | Contains the value of the symmetric (or other single-valued) |
138 //   |       | key.  It is represented as the base64url encoding of the     |
139 //   |       | octet sequence containing the key value.                     |
140 //   +-------+--------------------------------------------------------------+
141 // - kty == "RSA" (RSA public key)
142 //   +-------+--------------------------------------------------------------+
143 //   | "n"   | Contains the modulus value for the RSA public key.  It is    |
144 //   |       | represented as the base64url encoding of the value's         |
145 //   |       | unsigned big endian representation as an octet sequence.     |
146 //   +-------+--------------------------------------------------------------+
147 //   | "e"   | Contains the exponent value for the RSA public key.  It is   |
148 //   |       | represented as the base64url encoding of the value's         |
149 //   |       | unsigned big endian representation as an octet sequence.     |
150 //   +-------+--------------------------------------------------------------+
151 // - If key == "RSA" and the "d" parameter is present then it is a private key.
152 //   All the parameters above for public keys apply, as well as the following.
153 //   (Note that except for "d", all of these are optional):
154 //   +-------+--------------------------------------------------------------+
155 //   | "d"   | Contains the private exponent value for the RSA private key. |
156 //   |       | It is represented as the base64url encoding of the value's   |
157 //   |       | unsigned big endian representation as an octet sequence.     |
158 //   +-------+--------------------------------------------------------------+
159 //   | "p"   | Contains the first prime factor value for the RSA private    |
160 //   |       | key.  It is represented as the base64url encoding of the     |
161 //   |       | value's                                                      |
162 //   |       | unsigned big endian representation as an octet sequence.     |
163 //   +-------+--------------------------------------------------------------+
164 //   | "q"   | Contains the second prime factor value for the RSA private   |
165 //   |       | key.  It is represented as the base64url encoding of the     |
166 //   |       | value's unsigned big endian representation as an octet       |
167 //   |       | sequence.                                                    |
168 //   +-------+--------------------------------------------------------------+
169 //   | "dp"  | Contains the first factor CRT exponent value for the RSA     |
170 //   |       | private key.  It is represented as the base64url encoding of |
171 //   |       | the value's unsigned big endian representation as an octet   |
172 //   |       | sequence.                                                    |
173 //   +-------+--------------------------------------------------------------+
174 //   | "dq"  | Contains the second factor CRT exponent value for the RSA    |
175 //   |       | private key.  It is represented as the base64url encoding of |
176 //   |       | the value's unsigned big endian representation as an octet   |
177 //   |       | sequence.                                                    |
178 //   +-------+--------------------------------------------------------------+
179 //   | "dq"  | Contains the first CRT coefficient value for the RSA private |
180 //   |       | key.  It is represented as the base64url encoding of the     |
181 //   |       | value's unsigned big endian representation as an octet       |
182 //   |       | sequence.                                                    |
183 //   +-------+--------------------------------------------------------------+
184 //
185 // Consistency and conflict resolution
186 // The 'algorithm', 'extractable', and 'usage_mask' input parameters
187 // may be different than the corresponding values inside the JWK. The Web
188 // Crypto spec says that if a JWK value is present but is inconsistent with
189 // the input value, it is an error and the operation must fail. If no
190 // inconsistency is found then the input parameters are used.
191 //
192 // algorithm
193 //   If the JWK algorithm is provided, it must match the web crypto input
194 //   algorithm (both the algorithm ID and inner hash if applicable).
195 //
196 // extractable
197 //   If the JWK ext field is true but the input parameter is false, make the
198 //   Web Crypto Key non-extractable. Conversely, if the JWK ext field is
199 //   false but the input parameter is true, it is an inconsistency. If both
200 //   are true or both are false, use that value.
201 //
202 // usage_mask
203 //   The input usage_mask must be a strict subset of the interpreted JWK use
204 //   value, else it is judged inconsistent. In all cases the input usage_mask
205 //   is used as the final usage_mask.
206 //
207 
208 namespace content {
209 
210 namespace webcrypto {
211 
212 namespace {
213 
214 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'.
215 const blink::WebCryptoKeyUsageMask kJwkEncUsage =
216     blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt |
217     blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey |
218     blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits;
219 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'.
220 const blink::WebCryptoKeyUsageMask kJwkSigUsage =
221     blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
222 
223 class JwkWriter {
224  public:
JwkWriter(const std::string & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,const std::string & kty)225   JwkWriter(const std::string& algorithm,
226             bool extractable,
227             blink::WebCryptoKeyUsageMask usage_mask,
228             const std::string& kty) {
229     dict_.SetString("alg", algorithm);
230     dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usage_mask));
231     dict_.SetBoolean("ext", extractable);
232     dict_.SetString("kty", kty);
233   }
234 
Set(const std::string & key,const std::string & value)235   void Set(const std::string& key, const std::string& value) {
236     dict_.SetString(key, value);
237   }
238 
SetBase64Encoded(const std::string & key,const CryptoData & value)239   void SetBase64Encoded(const std::string& key, const CryptoData& value) {
240     dict_.SetString(key,
241                     Base64EncodeUrlSafe(base::StringPiece(
242                         reinterpret_cast<const char*>(value.bytes()),
243                         value.byte_length())));
244   }
245 
ToBytes(std::vector<uint8_t> * utf8_bytes)246   void ToBytes(std::vector<uint8_t>* utf8_bytes) {
247     std::string json;
248     base::JSONWriter::Write(&dict_, &json);
249     utf8_bytes->assign(json.begin(), json.end());
250   }
251 
252  private:
253   base::DictionaryValue dict_;
254 };
255 
256 // Extracts the required string property with key |path| from |dict| and saves
257 // the result to |*result|. If the property does not exist or is not a string,
258 // returns an error.
GetJwkString(base::DictionaryValue * dict,const std::string & path,std::string * result)259 Status GetJwkString(base::DictionaryValue* dict,
260                     const std::string& path,
261                     std::string* result) {
262   base::Value* value = NULL;
263   if (!dict->Get(path, &value))
264     return Status::ErrorJwkPropertyMissing(path);
265   if (!value->GetAsString(result))
266     return Status::ErrorJwkPropertyWrongType(path, "string");
267   return Status::Success();
268 }
269 
270 // Extracts the optional string property with key |path| from |dict| and saves
271 // the result to |*result| if it was found. If the property exists and is not a
272 // string, returns an error. Otherwise returns success, and sets
273 // |*property_exists| if it was found.
GetOptionalJwkString(base::DictionaryValue * dict,const std::string & path,std::string * result,bool * property_exists)274 Status GetOptionalJwkString(base::DictionaryValue* dict,
275                             const std::string& path,
276                             std::string* result,
277                             bool* property_exists) {
278   *property_exists = false;
279   base::Value* value = NULL;
280   if (!dict->Get(path, &value))
281     return Status::Success();
282 
283   if (!value->GetAsString(result))
284     return Status::ErrorJwkPropertyWrongType(path, "string");
285 
286   *property_exists = true;
287   return Status::Success();
288 }
289 
290 // Extracts the optional array property with key |path| from |dict| and saves
291 // the result to |*result| if it was found. If the property exists and is not an
292 // array, returns an error. Otherwise returns success, and sets
293 // |*property_exists| if it was found. Note that |*result| is owned by |dict|.
GetOptionalJwkList(base::DictionaryValue * dict,const std::string & path,base::ListValue ** result,bool * property_exists)294 Status GetOptionalJwkList(base::DictionaryValue* dict,
295                           const std::string& path,
296                           base::ListValue** result,
297                           bool* property_exists) {
298   *property_exists = false;
299   base::Value* value = NULL;
300   if (!dict->Get(path, &value))
301     return Status::Success();
302 
303   if (!value->GetAsList(result))
304     return Status::ErrorJwkPropertyWrongType(path, "list");
305 
306   *property_exists = true;
307   return Status::Success();
308 }
309 
310 // Extracts the required string property with key |path| from |dict| and saves
311 // the base64url-decoded bytes to |*result|. If the property does not exist or
312 // is not a string, or could not be base64url-decoded, returns an error.
GetJwkBytes(base::DictionaryValue * dict,const std::string & path,std::string * result)313 Status GetJwkBytes(base::DictionaryValue* dict,
314                    const std::string& path,
315                    std::string* result) {
316   std::string base64_string;
317   Status status = GetJwkString(dict, path, &base64_string);
318   if (status.IsError())
319     return status;
320 
321   if (!Base64DecodeUrlSafe(base64_string, result))
322     return Status::ErrorJwkBase64Decode(path);
323 
324   return Status::Success();
325 }
326 
327 // Extracts the required base64url property, which is interpreted as being a
328 // big-endian unsigned integer.
GetJwkBigInteger(base::DictionaryValue * dict,const std::string & path,std::string * result)329 Status GetJwkBigInteger(base::DictionaryValue* dict,
330                         const std::string& path,
331                         std::string* result) {
332   Status status = GetJwkBytes(dict, path, result);
333   if (status.IsError())
334     return status;
335 
336   if (result->empty())
337     return Status::ErrorJwkEmptyBigInteger(path);
338 
339   // The JWA spec says that "The octet sequence MUST utilize the minimum number
340   // of octets to represent the value." This means there shouldn't be any
341   // leading zeros.
342   if (result->size() > 1 && (*result)[0] == 0)
343     return Status::ErrorJwkBigIntegerHasLeadingZero(path);
344 
345   return Status::Success();
346 }
347 
348 // Extracts the optional boolean property with key |path| from |dict| and saves
349 // the result to |*result| if it was found. If the property exists and is not a
350 // boolean, returns an error. Otherwise returns success, and sets
351 // |*property_exists| if it was found.
GetOptionalJwkBool(base::DictionaryValue * dict,const std::string & path,bool * result,bool * property_exists)352 Status GetOptionalJwkBool(base::DictionaryValue* dict,
353                           const std::string& path,
354                           bool* result,
355                           bool* property_exists) {
356   *property_exists = false;
357   base::Value* value = NULL;
358   if (!dict->Get(path, &value))
359     return Status::Success();
360 
361   if (!value->GetAsBoolean(result))
362     return Status::ErrorJwkPropertyWrongType(path, "boolean");
363 
364   *property_exists = true;
365   return Status::Success();
366 }
367 
VerifyExt(base::DictionaryValue * dict,bool expected_extractable)368 Status VerifyExt(base::DictionaryValue* dict, bool expected_extractable) {
369   // JWK "ext" (optional) --> extractable parameter
370   bool jwk_ext_value = false;
371   bool has_jwk_ext;
372   Status status = GetOptionalJwkBool(dict, "ext", &jwk_ext_value, &has_jwk_ext);
373   if (status.IsError())
374     return status;
375   if (has_jwk_ext && expected_extractable && !jwk_ext_value)
376     return Status::ErrorJwkExtInconsistent();
377   return Status::Success();
378 }
379 
VerifyUsages(base::DictionaryValue * dict,blink::WebCryptoKeyUsageMask expected_usage_mask)380 Status VerifyUsages(base::DictionaryValue* dict,
381                     blink::WebCryptoKeyUsageMask expected_usage_mask) {
382   // JWK "key_ops" (optional) --> usage_mask parameter
383   base::ListValue* jwk_key_ops_value = NULL;
384   bool has_jwk_key_ops;
385   Status status =
386       GetOptionalJwkList(dict, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops);
387   if (status.IsError())
388     return status;
389   blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0;
390   if (has_jwk_key_ops) {
391     status =
392         GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask);
393     if (status.IsError())
394       return status;
395     // The input usage_mask must be a subset of jwk_key_ops_mask.
396     if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usage_mask))
397       return Status::ErrorJwkKeyopsInconsistent();
398   }
399 
400   // JWK "use" (optional) --> usage_mask parameter
401   std::string jwk_use_value;
402   bool has_jwk_use;
403   status = GetOptionalJwkString(dict, "use", &jwk_use_value, &has_jwk_use);
404   if (status.IsError())
405     return status;
406   blink::WebCryptoKeyUsageMask jwk_use_mask = 0;
407   if (has_jwk_use) {
408     if (jwk_use_value == "enc")
409       jwk_use_mask = kJwkEncUsage;
410     else if (jwk_use_value == "sig")
411       jwk_use_mask = kJwkSigUsage;
412     else
413       return Status::ErrorJwkUnrecognizedUse();
414     // The input usage_mask must be a subset of jwk_use_mask.
415     if (!ContainsKeyUsages(jwk_use_mask, expected_usage_mask))
416       return Status::ErrorJwkUseInconsistent();
417   }
418 
419   // If both 'key_ops' and 'use' are present, ensure they are consistent.
420   if (has_jwk_key_ops && has_jwk_use &&
421       !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask))
422     return Status::ErrorJwkUseAndKeyopsInconsistent();
423 
424   return Status::Success();
425 }
426 
VerifyAlg(base::DictionaryValue * dict,const std::string & expected_algorithm)427 Status VerifyAlg(base::DictionaryValue* dict,
428                  const std::string& expected_algorithm) {
429   // JWK "alg" --> algorithm parameter
430   bool has_jwk_alg;
431   std::string jwk_alg_value;
432   Status status =
433       GetOptionalJwkString(dict, "alg", &jwk_alg_value, &has_jwk_alg);
434   if (status.IsError())
435     return status;
436 
437   if (has_jwk_alg && jwk_alg_value != expected_algorithm)
438     return Status::ErrorJwkAlgorithmInconsistent();
439 
440   return Status::Success();
441 }
442 
ParseJwkCommon(const CryptoData & bytes,bool expected_extractable,blink::WebCryptoKeyUsageMask expected_usage_mask,std::string * kty,scoped_ptr<base::DictionaryValue> * dict)443 Status ParseJwkCommon(const CryptoData& bytes,
444                       bool expected_extractable,
445                       blink::WebCryptoKeyUsageMask expected_usage_mask,
446                       std::string* kty,
447                       scoped_ptr<base::DictionaryValue>* dict) {
448   // Parse the incoming JWK JSON.
449   base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()),
450                                 bytes.byte_length());
451 
452   scoped_ptr<base::Value> value(base::JSONReader::Read(json_string));
453   base::DictionaryValue* dict_value = NULL;
454 
455   if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value)
456     return Status::ErrorJwkNotDictionary();
457 
458   // Release |value|, as ownership will be transferred to |dict| via
459   // |dict_value|, which points to the same object as |value|.
460   ignore_result(value.release());
461   dict->reset(dict_value);
462 
463   // JWK "kty". Exit early if this required JWK parameter is missing.
464   Status status = GetJwkString(dict_value, "kty", kty);
465   if (status.IsError())
466     return status;
467 
468   status = VerifyExt(dict_value, expected_extractable);
469   if (status.IsError())
470     return status;
471 
472   status = VerifyUsages(dict_value, expected_usage_mask);
473   if (status.IsError())
474     return status;
475 
476   return Status::Success();
477 }
478 
ReadSecretKeyNoExpectedAlg(const CryptoData & key_data,bool expected_extractable,blink::WebCryptoKeyUsageMask expected_usage_mask,std::vector<uint8_t> * raw_key_data,scoped_ptr<base::DictionaryValue> * dict)479 Status ReadSecretKeyNoExpectedAlg(
480     const CryptoData& key_data,
481     bool expected_extractable,
482     blink::WebCryptoKeyUsageMask expected_usage_mask,
483     std::vector<uint8_t>* raw_key_data,
484     scoped_ptr<base::DictionaryValue>* dict) {
485   if (!key_data.byte_length())
486     return Status::ErrorImportEmptyKeyData();
487 
488   std::string kty;
489   Status status = ParseJwkCommon(
490       key_data, expected_extractable, expected_usage_mask, &kty, dict);
491   if (status.IsError())
492     return status;
493 
494   if (kty != "oct")
495     return Status::ErrorJwkUnexpectedKty("oct");
496 
497   std::string jwk_k_value;
498   status = GetJwkBytes(dict->get(), "k", &jwk_k_value);
499   if (status.IsError())
500     return status;
501   raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end());
502 
503   return Status::Success();
504 }
505 
506 }  // namespace
507 
WriteSecretKeyJwk(const CryptoData & raw_key_data,const std::string & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,std::vector<uint8_t> * jwk_key_data)508 void WriteSecretKeyJwk(const CryptoData& raw_key_data,
509                        const std::string& algorithm,
510                        bool extractable,
511                        blink::WebCryptoKeyUsageMask usage_mask,
512                        std::vector<uint8_t>* jwk_key_data) {
513   JwkWriter writer(algorithm, extractable, usage_mask, "oct");
514   writer.SetBase64Encoded("k", raw_key_data);
515   writer.ToBytes(jwk_key_data);
516 }
517 
ReadSecretKeyJwk(const CryptoData & key_data,const std::string & expected_algorithm,bool expected_extractable,blink::WebCryptoKeyUsageMask expected_usage_mask,std::vector<uint8_t> * raw_key_data)518 Status ReadSecretKeyJwk(const CryptoData& key_data,
519                         const std::string& expected_algorithm,
520                         bool expected_extractable,
521                         blink::WebCryptoKeyUsageMask expected_usage_mask,
522                         std::vector<uint8_t>* raw_key_data) {
523   scoped_ptr<base::DictionaryValue> dict;
524   Status status = ReadSecretKeyNoExpectedAlg(
525       key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict);
526   if (status.IsError())
527     return status;
528   return VerifyAlg(dict.get(), expected_algorithm);
529 }
530 
MakeJwkAesAlgorithmName(const std::string & suffix,unsigned int keylen_bytes)531 std::string MakeJwkAesAlgorithmName(const std::string& suffix,
532                                     unsigned int keylen_bytes) {
533   if (keylen_bytes == 16)
534     return std::string("A128") + suffix;
535   if (keylen_bytes == 24)
536     return std::string("A192") + suffix;
537   if (keylen_bytes == 32)
538     return std::string("A256") + suffix;
539   return std::string();
540 }
541 
ReadAesSecretKeyJwk(const CryptoData & key_data,const std::string & algorithm_name_suffix,bool expected_extractable,blink::WebCryptoKeyUsageMask expected_usage_mask,std::vector<uint8_t> * raw_key_data)542 Status ReadAesSecretKeyJwk(const CryptoData& key_data,
543                            const std::string& algorithm_name_suffix,
544                            bool expected_extractable,
545                            blink::WebCryptoKeyUsageMask expected_usage_mask,
546                            std::vector<uint8_t>* raw_key_data) {
547   scoped_ptr<base::DictionaryValue> dict;
548   Status status = ReadSecretKeyNoExpectedAlg(
549       key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict);
550   if (status.IsError())
551     return status;
552 
553   bool has_jwk_alg;
554   std::string jwk_alg;
555   status = GetOptionalJwkString(dict.get(), "alg", &jwk_alg, &has_jwk_alg);
556   if (status.IsError())
557     return status;
558 
559   if (has_jwk_alg) {
560     std::string expected_algorithm_name =
561         MakeJwkAesAlgorithmName(algorithm_name_suffix, raw_key_data->size());
562 
563     if (jwk_alg != expected_algorithm_name) {
564       // Give a different error message if the key length was wrong.
565       if (jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 16) ||
566           jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 24) ||
567           jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 32)) {
568         return Status::ErrorJwkIncorrectKeyLength();
569       }
570       return Status::ErrorJwkAlgorithmInconsistent();
571     }
572   }
573 
574   return Status::Success();
575 }
576 
577 // Writes an RSA public key to a JWK dictionary
WriteRsaPublicKeyJwk(const CryptoData & n,const CryptoData & e,const std::string & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,std::vector<uint8_t> * jwk_key_data)578 void WriteRsaPublicKeyJwk(const CryptoData& n,
579                           const CryptoData& e,
580                           const std::string& algorithm,
581                           bool extractable,
582                           blink::WebCryptoKeyUsageMask usage_mask,
583                           std::vector<uint8_t>* jwk_key_data) {
584   JwkWriter writer(algorithm, extractable, usage_mask, "RSA");
585   writer.SetBase64Encoded("n", n);
586   writer.SetBase64Encoded("e", e);
587   writer.ToBytes(jwk_key_data);
588 }
589 
590 // Writes an RSA private key to a JWK dictionary
WriteRsaPrivateKeyJwk(const CryptoData & n,const CryptoData & e,const CryptoData & d,const CryptoData & p,const CryptoData & q,const CryptoData & dp,const CryptoData & dq,const CryptoData & qi,const std::string & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,std::vector<uint8_t> * jwk_key_data)591 void WriteRsaPrivateKeyJwk(const CryptoData& n,
592                            const CryptoData& e,
593                            const CryptoData& d,
594                            const CryptoData& p,
595                            const CryptoData& q,
596                            const CryptoData& dp,
597                            const CryptoData& dq,
598                            const CryptoData& qi,
599                            const std::string& algorithm,
600                            bool extractable,
601                            blink::WebCryptoKeyUsageMask usage_mask,
602                            std::vector<uint8_t>* jwk_key_data) {
603   JwkWriter writer(algorithm, extractable, usage_mask, "RSA");
604 
605   writer.SetBase64Encoded("n", n);
606   writer.SetBase64Encoded("e", e);
607   writer.SetBase64Encoded("d", d);
608   // Although these are "optional" in the JWA, WebCrypto spec requires them to
609   // be emitted.
610   writer.SetBase64Encoded("p", p);
611   writer.SetBase64Encoded("q", q);
612   writer.SetBase64Encoded("dp", dp);
613   writer.SetBase64Encoded("dq", dq);
614   writer.SetBase64Encoded("qi", qi);
615   writer.ToBytes(jwk_key_data);
616 }
617 
JwkRsaInfo()618 JwkRsaInfo::JwkRsaInfo() : is_private_key(false) {
619 }
620 
~JwkRsaInfo()621 JwkRsaInfo::~JwkRsaInfo() {
622 }
623 
ReadRsaKeyJwk(const CryptoData & key_data,const std::string & expected_algorithm,bool expected_extractable,blink::WebCryptoKeyUsageMask expected_usage_mask,JwkRsaInfo * result)624 Status ReadRsaKeyJwk(const CryptoData& key_data,
625                      const std::string& expected_algorithm,
626                      bool expected_extractable,
627                      blink::WebCryptoKeyUsageMask expected_usage_mask,
628                      JwkRsaInfo* result) {
629   if (!key_data.byte_length())
630     return Status::ErrorImportEmptyKeyData();
631 
632   scoped_ptr<base::DictionaryValue> dict;
633   std::string kty;
634   Status status = ParseJwkCommon(
635       key_data, expected_extractable, expected_usage_mask, &kty, &dict);
636   if (status.IsError())
637     return status;
638 
639   status = VerifyAlg(dict.get(), expected_algorithm);
640   if (status.IsError())
641     return status;
642 
643   if (kty != "RSA")
644     return Status::ErrorJwkUnexpectedKty("RSA");
645 
646   // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry
647   // in the JWK, while an RSA private key must have those, plus at least a "d"
648   // (private exponent) entry.
649   // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18,
650   // section 6.3.
651   status = GetJwkBigInteger(dict.get(), "n", &result->n);
652   if (status.IsError())
653     return status;
654   status = GetJwkBigInteger(dict.get(), "e", &result->e);
655   if (status.IsError())
656     return status;
657 
658   result->is_private_key = dict->HasKey("d");
659   if (!result->is_private_key)
660     return Status::Success();
661 
662   status = GetJwkBigInteger(dict.get(), "d", &result->d);
663   if (status.IsError())
664     return status;
665 
666   // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA
667   // spec. However they are required by Chromium's WebCrypto implementation.
668 
669   status = GetJwkBigInteger(dict.get(), "p", &result->p);
670   if (status.IsError())
671     return status;
672 
673   status = GetJwkBigInteger(dict.get(), "q", &result->q);
674   if (status.IsError())
675     return status;
676 
677   status = GetJwkBigInteger(dict.get(), "dp", &result->dp);
678   if (status.IsError())
679     return status;
680 
681   status = GetJwkBigInteger(dict.get(), "dq", &result->dq);
682   if (status.IsError())
683     return status;
684 
685   status = GetJwkBigInteger(dict.get(), "qi", &result->qi);
686   if (status.IsError())
687     return status;
688 
689   return Status::Success();
690 }
691 
GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash)692 const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) {
693   switch (hash) {
694     case blink::WebCryptoAlgorithmIdSha1:
695       return "HS1";
696     case blink::WebCryptoAlgorithmIdSha256:
697       return "HS256";
698     case blink::WebCryptoAlgorithmIdSha384:
699       return "HS384";
700     case blink::WebCryptoAlgorithmIdSha512:
701       return "HS512";
702     default:
703       return NULL;
704   }
705 }
706 
707 // TODO(eroman): This accepts invalid inputs. http://crbug.com/378034
Base64DecodeUrlSafe(const std::string & input,std::string * output)708 bool Base64DecodeUrlSafe(const std::string& input, std::string* output) {
709   std::string base64_encoded_text(input);
710   std::replace(
711       base64_encoded_text.begin(), base64_encoded_text.end(), '-', '+');
712   std::replace(
713       base64_encoded_text.begin(), base64_encoded_text.end(), '_', '/');
714   base64_encoded_text.append((4 - base64_encoded_text.size() % 4) % 4, '=');
715   return base::Base64Decode(base64_encoded_text, output);
716 }
717 
Base64EncodeUrlSafe(const base::StringPiece & input)718 std::string Base64EncodeUrlSafe(const base::StringPiece& input) {
719   std::string output;
720   base::Base64Encode(input, &output);
721   std::replace(output.begin(), output.end(), '+', '-');
722   std::replace(output.begin(), output.end(), '/', '_');
723   output.erase(std::remove(output.begin(), output.end(), '='), output.end());
724   return output;
725 }
726 
Base64EncodeUrlSafe(const std::vector<uint8_t> & input)727 std::string Base64EncodeUrlSafe(const std::vector<uint8_t>& input) {
728   const base::StringPiece string_piece(
729       reinterpret_cast<const char*>(vector_as_array(&input)), input.size());
730   return Base64EncodeUrlSafe(string_piece);
731 }
732 
733 }  // namespace webcrypto
734 
735 }  // namespace content
736