Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / renderer / webcrypto / webcrypto_impl.cc
1 // Copyright (c) 2013 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/renderer/webcrypto/webcrypto_impl.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <map>
10 #include "base/json/json_reader.h"
11 #include "base/lazy_instance.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/string_piece.h"
15 #include "base/values.h"
16 #include "content/renderer/webcrypto/webcrypto_util.h"
17 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
18 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
19 #include "third_party/WebKit/public/platform/WebCryptoKey.h"
20 #include "third_party/WebKit/public/platform/WebString.h"
21
22 namespace content {
23
24 using webcrypto::Status;
25
26 namespace {
27
28 void CompleteWithError(const Status& status, blink::WebCryptoResult* result) {
29   DCHECK(status.IsError());
30   if (status.HasErrorDetails())
31     result->completeWithError(blink::WebString::fromUTF8(status.ToString()));
32   else
33     result->completeWithError();
34 }
35
36 bool IsAlgorithmAsymmetric(const blink::WebCryptoAlgorithm& algorithm) {
37   // TODO(padolph): include all other asymmetric algorithms once they are
38   // defined, e.g. EC and DH.
39   return (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 ||
40           algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 ||
41           algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep);
42 }
43
44 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)();
45
46 class JwkAlgorithmInfo {
47  public:
48   JwkAlgorithmInfo()
49       : creation_func_(NULL),
50         required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {
51
52   }
53
54   explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func)
55       : creation_func_(algorithm_creation_func),
56         required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {
57   }
58
59   JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func,
60                    unsigned int required_key_length_bits)
61       : creation_func_(algorithm_creation_func),
62         required_key_length_bytes_(required_key_length_bits / 8) {
63     DCHECK((required_key_length_bits % 8) == 0);
64   }
65
66   bool CreateAlgorithm(blink::WebCryptoAlgorithm* algorithm) const {
67     *algorithm = creation_func_();
68     return !algorithm->isNull();
69   }
70
71   bool IsInvalidKeyByteLength(size_t byte_length) const {
72     if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT)
73       return false;
74     return required_key_length_bytes_  != byte_length;
75   }
76
77  private:
78   enum { NO_KEY_SIZE_REQUIREMENT = UINT_MAX };
79
80   AlgorithmCreationFunc creation_func_;
81
82   // The expected key size for the algorithm or NO_KEY_SIZE_REQUIREMENT.
83   unsigned int required_key_length_bytes_;
84
85 };
86
87 typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap;
88
89 class JwkAlgorithmRegistry {
90  public:
91   JwkAlgorithmRegistry() {
92     // TODO(eroman):
93     // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20
94     // says HMAC with SHA-2 should have a key size at least as large as the
95     // hash output.
96     alg_to_info_["HS256"] = JwkAlgorithmInfo(
97         &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId,
98                         blink::WebCryptoAlgorithmIdSha256>);
99     alg_to_info_["HS384"] = JwkAlgorithmInfo(
100         &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId,
101                         blink::WebCryptoAlgorithmIdSha384>);
102     alg_to_info_["HS512"] = JwkAlgorithmInfo(
103         &BindAlgorithmId<webcrypto::CreateHmacAlgorithmByHashId,
104                          blink::WebCryptoAlgorithmIdSha512>);
105     alg_to_info_["RS256"] = JwkAlgorithmInfo(
106         &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm,
107                          blink::WebCryptoAlgorithmIdSha256>);
108     alg_to_info_["RS384"] = JwkAlgorithmInfo(
109         &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm,
110                          blink::WebCryptoAlgorithmIdSha384>);
111     alg_to_info_["RS512"] = JwkAlgorithmInfo(
112         &BindAlgorithmId<webcrypto::CreateRsaSsaAlgorithm,
113                          blink::WebCryptoAlgorithmIdSha512>);
114     alg_to_info_["RSA1_5"] = JwkAlgorithmInfo(
115         &BindAlgorithmId<webcrypto::CreateAlgorithm,
116                          blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5>);
117     alg_to_info_["RSA-OAEP"] = JwkAlgorithmInfo(
118         &BindAlgorithmId<webcrypto::CreateRsaOaepAlgorithm,
119                          blink::WebCryptoAlgorithmIdSha1>);
120     // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 128 yet
121     alg_to_info_["A128KW"] =
122         JwkAlgorithmInfo(&blink::WebCryptoAlgorithm::createNull, 128);
123     // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 256 yet
124     alg_to_info_["A256KW"] =
125         JwkAlgorithmInfo(&blink::WebCryptoAlgorithm::createNull, 256);
126     alg_to_info_["A128GCM"] = JwkAlgorithmInfo(
127         &BindAlgorithmId<webcrypto::CreateAlgorithm,
128                          blink::WebCryptoAlgorithmIdAesGcm>, 128);
129     alg_to_info_["A256GCM"] = JwkAlgorithmInfo(
130         &BindAlgorithmId<webcrypto::CreateAlgorithm,
131                          blink::WebCryptoAlgorithmIdAesGcm>, 256);
132     alg_to_info_["A128CBC"] = JwkAlgorithmInfo(
133         &BindAlgorithmId<webcrypto::CreateAlgorithm,
134                          blink::WebCryptoAlgorithmIdAesCbc>, 128);
135     alg_to_info_["A192CBC"] = JwkAlgorithmInfo(
136         &BindAlgorithmId<webcrypto::CreateAlgorithm,
137                          blink::WebCryptoAlgorithmIdAesCbc>, 192);
138     alg_to_info_["A256CBC"] = JwkAlgorithmInfo(
139         &BindAlgorithmId<webcrypto::CreateAlgorithm,
140                          blink::WebCryptoAlgorithmIdAesCbc>, 256);
141   }
142
143   // Returns NULL if the algorithm name was not registered.
144   const JwkAlgorithmInfo* GetAlgorithmInfo(const std::string& jwk_alg) const {
145     const JwkAlgorithmInfoMap::const_iterator pos = alg_to_info_.find(jwk_alg);
146     if (pos == alg_to_info_.end())
147       return NULL;
148     return &pos->second;
149   }
150
151  private:
152   // Binds a WebCryptoAlgorithmId value to a compatible factory function.
153   typedef blink::WebCryptoAlgorithm (*FuncWithWebCryptoAlgIdArg)(
154       blink::WebCryptoAlgorithmId);
155   template <FuncWithWebCryptoAlgIdArg func,
156             blink::WebCryptoAlgorithmId algorithm_id>
157   static blink::WebCryptoAlgorithm BindAlgorithmId() {
158     return func(algorithm_id);
159   }
160
161   JwkAlgorithmInfoMap alg_to_info_;
162 };
163
164 base::LazyInstance<JwkAlgorithmRegistry> jwk_alg_registry =
165     LAZY_INSTANCE_INITIALIZER;
166
167 bool WebCryptoAlgorithmsConsistent(const blink::WebCryptoAlgorithm& alg1,
168                                    const blink::WebCryptoAlgorithm& alg2) {
169   DCHECK(!alg1.isNull());
170   DCHECK(!alg2.isNull());
171   if (alg1.id() != alg2.id())
172     return false;
173   switch (alg1.id()) {
174     case blink::WebCryptoAlgorithmIdHmac:
175     case blink::WebCryptoAlgorithmIdRsaOaep:
176     case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
177       if (WebCryptoAlgorithmsConsistent(
178               webcrypto::GetInnerHashAlgorithm(alg1),
179               webcrypto::GetInnerHashAlgorithm(alg2))) {
180         return true;
181       }
182       break;
183     case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
184     case blink::WebCryptoAlgorithmIdSha1:
185     case blink::WebCryptoAlgorithmIdSha224:
186     case blink::WebCryptoAlgorithmIdSha256:
187     case blink::WebCryptoAlgorithmIdSha384:
188     case blink::WebCryptoAlgorithmIdSha512:
189     case blink::WebCryptoAlgorithmIdAesCbc:
190     case blink::WebCryptoAlgorithmIdAesGcm:
191     case blink::WebCryptoAlgorithmIdAesCtr:
192       return true;
193     default:
194       NOTREACHED();  // Not a supported algorithm.
195       break;
196   }
197   return false;
198 }
199
200 // Extracts the required string property with key |path| from |dict| and saves
201 // the result to |*result|. If the property does not exist or is not a string,
202 // returns an error.
203 Status GetJwkString(base::DictionaryValue* dict,
204                     const std::string& path,
205                     std::string* result) {
206   base::Value* value = NULL;
207   if (!dict->Get(path, &value))
208     return Status::ErrorJwkPropertyMissing(path);
209   if (!value->GetAsString(result))
210     return Status::ErrorJwkPropertyWrongType(path, "string");
211   return Status::Success();
212 }
213
214 // Extracts the optional string property with key |path| from |dict| and saves
215 // the result to |*result| if it was found. If the property exists and is not a
216 // string, returns an error. Otherwise returns success, and sets
217 // |*property_exists| if it was found.
218 Status GetOptionalJwkString(base::DictionaryValue* dict,
219                             const std::string& path,
220                             std::string* result,
221                             bool* property_exists) {
222   *property_exists = false;
223   base::Value* value = NULL;
224   if (!dict->Get(path, &value))
225     return Status::Success();
226
227   if (!value->GetAsString(result))
228     return Status::ErrorJwkPropertyWrongType(path, "string");
229
230   *property_exists = true;
231   return Status::Success();
232 }
233
234 // Extracts the required string property with key |path| from |dict| and saves
235 // the base64-decoded bytes to |*result|. If the property does not exist or is
236 // not a string, or could not be base64-decoded, returns an error.
237 Status GetJwkBytes(base::DictionaryValue* dict,
238                    const std::string& path,
239                    std::string* result) {
240   std::string base64_string;
241   Status status = GetJwkString(dict, path, &base64_string);
242   if (status.IsError())
243     return status;
244
245   if (!webcrypto::Base64DecodeUrlSafe(base64_string, result))
246     return Status::ErrorJwkBase64Decode(path);
247
248   return Status::Success();
249 }
250
251 // Extracts the optional boolean property with key |path| from |dict| and saves
252 // the result to |*result| if it was found. If the property exists and is not a
253 // boolean, returns an error. Otherwise returns success, and sets
254 // |*property_exists| if it was found.
255 Status GetOptionalJwkBool(base::DictionaryValue* dict,
256                           const std::string& path,
257                           bool* result,
258                           bool* property_exists) {
259   *property_exists = false;
260   base::Value* value = NULL;
261   if (!dict->Get(path, &value))
262     return Status::Success();
263
264   if (!value->GetAsBoolean(result))
265     return Status::ErrorJwkPropertyWrongType(path, "boolean");
266
267   *property_exists = true;
268   return Status::Success();
269 }
270
271 }  // namespace
272
273 WebCryptoImpl::WebCryptoImpl() {
274   Init();
275 }
276
277 void WebCryptoImpl::encrypt(
278     const blink::WebCryptoAlgorithm& algorithm,
279     const blink::WebCryptoKey& key,
280     const unsigned char* data,
281     unsigned int data_size,
282     blink::WebCryptoResult result) {
283   DCHECK(!algorithm.isNull());
284   blink::WebArrayBuffer buffer;
285   Status status = EncryptInternal(algorithm, key, data, data_size, &buffer);
286   if (status.IsError())
287     CompleteWithError(status, &result);
288   else
289     result.completeWithBuffer(buffer);
290 }
291
292 void WebCryptoImpl::decrypt(
293     const blink::WebCryptoAlgorithm& algorithm,
294     const blink::WebCryptoKey& key,
295     const unsigned char* data,
296     unsigned int data_size,
297     blink::WebCryptoResult result) {
298   DCHECK(!algorithm.isNull());
299   blink::WebArrayBuffer buffer;
300   Status status = DecryptInternal(algorithm, key, data, data_size, &buffer);
301   if (status.IsError())
302     CompleteWithError(status, &result);
303   else
304     result.completeWithBuffer(buffer);
305 }
306
307 void WebCryptoImpl::digest(
308     const blink::WebCryptoAlgorithm& algorithm,
309     const unsigned char* data,
310     unsigned int data_size,
311     blink::WebCryptoResult result) {
312   DCHECK(!algorithm.isNull());
313   blink::WebArrayBuffer buffer;
314   Status status = DigestInternal(algorithm, data, data_size, &buffer);
315   if (status.IsError())
316     CompleteWithError(status, &result);
317   else
318     result.completeWithBuffer(buffer);
319 }
320
321 void WebCryptoImpl::generateKey(
322     const blink::WebCryptoAlgorithm& algorithm,
323     bool extractable,
324     blink::WebCryptoKeyUsageMask usage_mask,
325     blink::WebCryptoResult result) {
326   DCHECK(!algorithm.isNull());
327   if (IsAlgorithmAsymmetric(algorithm)) {
328     blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull();
329     blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull();
330     Status status = GenerateKeyPairInternal(
331         algorithm, extractable, usage_mask, &public_key, &private_key);
332     if (status.IsError()) {
333       CompleteWithError(status, &result);
334     } else {
335       DCHECK(public_key.handle());
336       DCHECK(private_key.handle());
337       DCHECK_EQ(algorithm.id(), public_key.algorithm().id());
338       DCHECK_EQ(algorithm.id(), private_key.algorithm().id());
339       DCHECK_EQ(true, public_key.extractable());
340       DCHECK_EQ(extractable, private_key.extractable());
341       DCHECK_EQ(usage_mask, public_key.usages());
342       DCHECK_EQ(usage_mask, private_key.usages());
343       result.completeWithKeyPair(public_key, private_key);
344     }
345   } else {
346     blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
347     Status status = GenerateSecretKeyInternal(
348         algorithm, extractable, usage_mask, &key);
349     if (status.IsError()) {
350       CompleteWithError(status, &result);
351     } else {
352       DCHECK(key.handle());
353       DCHECK_EQ(algorithm.id(), key.algorithm().id());
354       DCHECK_EQ(extractable, key.extractable());
355       DCHECK_EQ(usage_mask, key.usages());
356       result.completeWithKey(key);
357     }
358   }
359 }
360
361 void WebCryptoImpl::importKey(
362     blink::WebCryptoKeyFormat format,
363     const unsigned char* key_data,
364     unsigned int key_data_size,
365     const blink::WebCryptoAlgorithm& algorithm_or_null,
366     bool extractable,
367     blink::WebCryptoKeyUsageMask usage_mask,
368     blink::WebCryptoResult result) {
369   blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
370   Status status = Status::Error();
371   if (format == blink::WebCryptoKeyFormatJwk) {
372     status = ImportKeyJwk(key_data,
373                           key_data_size,
374                           algorithm_or_null,
375                           extractable,
376                           usage_mask,
377                           &key);
378   } else {
379     status = ImportKeyInternal(format,
380                                key_data,
381                                key_data_size,
382                                algorithm_or_null,
383                                extractable,
384                                usage_mask,
385                                &key);
386   }
387   if (status.IsError()) {
388     CompleteWithError(status, &result);
389   } else {
390     DCHECK(key.handle());
391     DCHECK(!key.algorithm().isNull());
392     DCHECK_EQ(extractable, key.extractable());
393     result.completeWithKey(key);
394   }
395 }
396
397 void WebCryptoImpl::exportKey(
398     blink::WebCryptoKeyFormat format,
399     const blink::WebCryptoKey& key,
400     blink::WebCryptoResult result) {
401   blink::WebArrayBuffer buffer;
402   Status status = ExportKeyInternal(format, key, &buffer);
403   if (status.IsError())
404     CompleteWithError(status, &result);
405   else
406     result.completeWithBuffer(buffer);
407 }
408
409 void WebCryptoImpl::sign(
410     const blink::WebCryptoAlgorithm& algorithm,
411     const blink::WebCryptoKey& key,
412     const unsigned char* data,
413     unsigned int data_size,
414     blink::WebCryptoResult result) {
415   DCHECK(!algorithm.isNull());
416   blink::WebArrayBuffer buffer;
417   Status status = SignInternal(algorithm, key, data, data_size, &buffer);
418   if (status.IsError())
419     CompleteWithError(status, &result);
420   else
421     result.completeWithBuffer(buffer);
422 }
423
424 void WebCryptoImpl::verifySignature(
425     const blink::WebCryptoAlgorithm& algorithm,
426     const blink::WebCryptoKey& key,
427     const unsigned char* signature,
428     unsigned int signature_size,
429     const unsigned char* data,
430     unsigned int data_size,
431     blink::WebCryptoResult result) {
432   DCHECK(!algorithm.isNull());
433   bool signature_match = false;
434   Status status = VerifySignatureInternal(algorithm,
435                                           key,
436                                           signature,
437                                           signature_size,
438                                           data,
439                                           data_size,
440                                           &signature_match);
441   if (status.IsError())
442     CompleteWithError(status, &result);
443   else
444     result.completeWithBoolean(signature_match);
445 }
446
447 Status WebCryptoImpl::ImportKeyJwk(
448     const unsigned char* key_data,
449     unsigned int key_data_size,
450     const blink::WebCryptoAlgorithm& algorithm_or_null,
451     bool extractable,
452     blink::WebCryptoKeyUsageMask usage_mask,
453     blink::WebCryptoKey* key) {
454
455   // The goal of this method is to extract key material and meta data from the
456   // incoming JWK, combine them with the input parameters, and ultimately import
457   // a Web Crypto Key.
458   //
459   // JSON Web Key Format (JWK)
460   // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-16
461   // TODO(padolph): Not all possible values are handled by this code right now
462   //
463   // A JWK is a simple JSON dictionary with the following entries
464   // - "kty" (Key Type) Parameter, REQUIRED
465   // - <kty-specific parameters, see below>, REQUIRED
466   // - "use" (Key Use) Parameter, OPTIONAL
467   // - "alg" (Algorithm) Parameter, OPTIONAL
468   // - "extractable" (Key Exportability), OPTIONAL [NOTE: not yet part of JOSE,
469   //   see https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796]
470   // (all other entries are ignored)
471   //
472   // OPTIONAL here means that this code does not require the entry to be present
473   // in the incoming JWK, because the method input parameters contain similar
474   // information. If the optional JWK entry is present, it will be validated
475   // against the corresponding input parameter for consistency and combined with
476   // it according to rules defined below. A special case is that the method
477   // input parameter 'algorithm' is also optional. If it is null, the JWK 'alg'
478   // value (if present) is used as a fallback.
479   //
480   // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK
481   // values are parsed out and combined with the method input parameters to
482   // build a Web Crypto Key:
483   // Web Crypto Key type            <-- (deduced)
484   // Web Crypto Key extractable     <-- JWK extractable + input extractable
485   // Web Crypto Key algorithm       <-- JWK alg + input algorithm
486   // Web Crypto Key keyUsage        <-- JWK use + input usage_mask
487   // Web Crypto Key keying material <-- kty-specific parameters
488   //
489   // Values for each JWK entry are case-sensitive and defined in
490   // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16.
491   // Note that not all values specified by JOSE are handled by this code. Only
492   // handled values are listed.
493   // - kty (Key Type)
494   //   +-------+--------------------------------------------------------------+
495   //   | "RSA" | RSA [RFC3447]                                                |
496   //   | "oct" | Octet sequence (used to represent symmetric keys)            |
497   //   +-------+--------------------------------------------------------------+
498   // - use (Key Use)
499   //   +-------+--------------------------------------------------------------+
500   //   | "enc" | encrypt and decrypt operations                               |
501   //   | "sig" | sign and verify (MAC) operations                             |
502   //   | "wrap"| key wrap and unwrap [not yet part of JOSE]                   |
503   //   +-------+--------------------------------------------------------------+
504   // - extractable (Key Exportability)
505   //   +-------+--------------------------------------------------------------+
506   //   | true  | Key may be exported from the trusted environment             |
507   //   | false | Key cannot exit the trusted environment                      |
508   //   +-------+--------------------------------------------------------------+
509   // - alg (Algorithm)
510   //   See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16
511   //   +--------------+-------------------------------------------------------+
512   //   | Digital Signature or MAC Algorithm                                   |
513   //   +--------------+-------------------------------------------------------+
514   //   | "HS256"      | HMAC using SHA-256 hash algorithm                     |
515   //   | "HS384"      | HMAC using SHA-384 hash algorithm                     |
516   //   | "HS512"      | HMAC using SHA-512 hash algorithm                     |
517   //   | "RS256"      | RSASSA using SHA-256 hash algorithm                   |
518   //   | "RS384"      | RSASSA using SHA-384 hash algorithm                   |
519   //   | "RS512"      | RSASSA using SHA-512 hash algorithm                   |
520   //   +--------------+-------------------------------------------------------|
521   //   | Key Management Algorithm                                             |
522   //   +--------------+-------------------------------------------------------+
523   //   | "RSA1_5"     | RSAES-PKCS1-V1_5 [RFC3447]                            |
524   //   | "RSA-OAEP"   | RSAES using Optimal Asymmetric Encryption Padding     |
525   //   |              | (OAEP) [RFC3447], with the default parameters         |
526   //   |              | specified by RFC3447 in Section A.2.1                 |
527   //   | "A128KW"     | Advanced Encryption Standard (AES) Key Wrap Algorithm |
528   //   |              | [RFC3394] using 128 bit keys                          |
529   //   | "A256KW"     | AES Key Wrap Algorithm using 256 bit keys             |
530   //   | "A128GCM"    | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using |
531   //   |              | 128 bit keys                                          |
532   //   | "A256GCM"    | AES GCM using 256 bit keys                            |
533   //   | "A128CBC"    | AES in Cipher Block Chaining Mode (CBC) with PKCS #5  |
534   //   |              | padding [NIST.800-38A] [not yet part of JOSE, see     |
535   //   |              | https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796  |
536   //   | "A192CBC"    | AES CBC using 192 bit keys [not yet part of JOSE]     |
537   //   | "A256CBC"    | AES CBC using 256 bit keys [not yet part of JOSE]     |
538   //   +--------------+-------------------------------------------------------+
539   //
540   // kty-specific parameters
541   // The value of kty determines the type and content of the keying material
542   // carried in the JWK to be imported. Currently only two possibilities are
543   // supported: a raw key or an RSA public key. RSA private keys are not
544   // supported because typical applications seldom need to import a private key,
545   // and the large number of JWK parameters required to describe one.
546   // - kty == "oct" (symmetric or other raw key)
547   //   +-------+--------------------------------------------------------------+
548   //   | "k"   | Contains the value of the symmetric (or other single-valued) |
549   //   |       | key.  It is represented as the base64url encoding of the     |
550   //   |       | octet sequence containing the key value.                     |
551   //   +-------+--------------------------------------------------------------+
552   // - kty == "RSA" (RSA public key)
553   //   +-------+--------------------------------------------------------------+
554   //   | "n"   | Contains the modulus value for the RSA public key.  It is    |
555   //   |       | represented as the base64url encoding of the value's         |
556   //   |       | unsigned big endian representation as an octet sequence.     |
557   //   +-------+--------------------------------------------------------------+
558   //   | "e"   | Contains the exponent value for the RSA public key.  It is   |
559   //   |       | represented as the base64url encoding of the value's         |
560   //   |       | unsigned big endian representation as an octet sequence.     |
561   //   +-------+--------------------------------------------------------------+
562   //
563   // Consistency and conflict resolution
564   // The 'algorithm_or_null', 'extractable', and 'usage_mask' input parameters
565   // may be different than the corresponding values inside the JWK. The Web
566   // Crypto spec says that if a JWK value is present but is inconsistent with
567   // the input value, it is an error and the operation must fail. If no
568   // inconsistency is found, the input and JWK values are combined as follows:
569   //
570   // algorithm
571   //   If an algorithm is provided by both the input parameter and the JWK,
572   //   consistency between the two is based only on algorithm ID's (including an
573   //   inner hash algorithm if present). In this case if the consistency
574   //   check is passed, the input algorithm is used. If only one of either the
575   //   input algorithm and JWK alg is provided, it is used as the final
576   //   algorithm.
577   //
578   // extractable
579   //   If the JWK extractable is true but the input parameter is false, make the
580   //   Web Crypto Key non-extractable. Conversely, if the JWK extractable is
581   //   false but the input parameter is true, it is an inconsistency. If both
582   //   are true or both are false, use that value.
583   //
584   // usage_mask
585   //   The input usage_mask must be a strict subset of the interpreted JWK use
586   //   value, else it is judged inconsistent. In all cases the input usage_mask
587   //   is used as the final usage_mask.
588   //
589
590   if (!key_data_size)
591     return Status::ErrorImportEmptyKeyData();
592   DCHECK(key);
593
594   // Parse the incoming JWK JSON.
595   base::StringPiece json_string(reinterpret_cast<const char*>(key_data),
596                                 key_data_size);
597   scoped_ptr<base::Value> value(base::JSONReader::Read(json_string));
598   // Note, bare pointer dict_value is ok since it points into scoped value.
599   base::DictionaryValue* dict_value = NULL;
600   if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value)
601     return Status::ErrorJwkNotDictionary();
602
603   // JWK "kty". Exit early if this required JWK parameter is missing.
604   std::string jwk_kty_value;
605   Status status = GetJwkString(dict_value, "kty", &jwk_kty_value);
606   if (status.IsError())
607     return status;
608
609   // JWK "extractable" (optional) --> extractable parameter
610   {
611     bool jwk_extractable_value = false;
612     bool has_jwk_extractable;
613     status = GetOptionalJwkBool(dict_value,
614                                 "extractable",
615                                 &jwk_extractable_value,
616                                 &has_jwk_extractable);
617     if (status.IsError())
618       return status;
619     if (has_jwk_extractable && !jwk_extractable_value && extractable)
620       return Status::ErrorJwkExtractableInconsistent();
621   }
622
623   // JWK "alg" (optional) --> algorithm parameter
624   // Note: input algorithm is also optional, so we have six cases to handle.
625   // 1. JWK alg present but unrecognized: error
626   // 2. JWK alg valid AND input algorithm isNull: use JWK value
627   // 3. JWK alg valid AND input algorithm specified, but JWK value
628   //      inconsistent with input: error
629   // 4. JWK alg valid AND input algorithm specified, both consistent: use
630   //      input value (because it has potentially more details)
631   // 5. JWK alg missing AND input algorithm isNull: error
632   // 6. JWK alg missing AND input algorithm specified: use input value
633   blink::WebCryptoAlgorithm algorithm = blink::WebCryptoAlgorithm::createNull();
634   const JwkAlgorithmInfo* algorithm_info = NULL;
635   std::string jwk_alg_value;
636   bool has_jwk_alg;
637   status =
638       GetOptionalJwkString(dict_value, "alg", &jwk_alg_value, &has_jwk_alg);
639   if (status.IsError())
640     return status;
641
642   if (has_jwk_alg) {
643     // JWK alg present
644
645     // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can
646     // only be from the RSA family.
647
648     blink::WebCryptoAlgorithm jwk_algorithm =
649         blink::WebCryptoAlgorithm::createNull();
650     algorithm_info = jwk_alg_registry.Get().GetAlgorithmInfo(jwk_alg_value);
651     if (!algorithm_info || !algorithm_info->CreateAlgorithm(&jwk_algorithm))
652       return Status::ErrorJwkUnrecognizedAlgorithm();  // case 1
653
654     // JWK alg valid
655     if (algorithm_or_null.isNull()) {
656       // input algorithm not specified
657       algorithm = jwk_algorithm;  // case 2
658     } else {
659       // input algorithm specified
660       if (!WebCryptoAlgorithmsConsistent(jwk_algorithm, algorithm_or_null))
661         return Status::ErrorJwkAlgorithmInconsistent();  // case 3
662       algorithm = algorithm_or_null;  // case 4
663     }
664   } else {
665     // JWK alg missing
666     if (algorithm_or_null.isNull())
667       return Status::ErrorJwkAlgorithmMissing();  // case 5
668     algorithm = algorithm_or_null;  // case 6
669   }
670   DCHECK(!algorithm.isNull());
671
672   // JWK "use" (optional) --> usage_mask parameter
673   std::string jwk_use_value;
674   bool has_jwk_use;
675   status =
676       GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use);
677   if (status.IsError())
678     return status;
679   if (has_jwk_use) {
680     blink::WebCryptoKeyUsageMask jwk_usage_mask = 0;
681     if (jwk_use_value == "enc") {
682       jwk_usage_mask =
683           blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt;
684     } else if (jwk_use_value == "sig") {
685       jwk_usage_mask =
686           blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
687     } else if (jwk_use_value == "wrap") {
688       jwk_usage_mask =
689           blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey;
690     } else {
691       return Status::ErrorJwkUnrecognizedUsage();
692     }
693     if ((jwk_usage_mask & usage_mask) != usage_mask) {
694       // A usage_mask must be a subset of jwk_usage_mask.
695       return Status::ErrorJwkUsageInconsistent();
696     }
697   }
698
699   // JWK keying material --> ImportKeyInternal()
700   if (jwk_kty_value == "oct") {
701
702     std::string jwk_k_value;
703     status = GetJwkBytes(dict_value, "k", &jwk_k_value);
704     if (status.IsError())
705       return status;
706
707     // Some JWK alg ID's embed information about the key length in the alg ID
708     // string. For example "A128CBC" implies the JWK carries 128 bits
709     // of key material. For such keys validate that enough bytes were provided.
710     // If this validation is not done, then it would be possible to select a
711     // different algorithm by passing a different lengthed key, since that is
712     // how WebCrypto interprets things.
713     if (algorithm_info &&
714         algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) {
715       return Status::ErrorJwkIncorrectKeyLength();
716     }
717
718     return ImportKeyInternal(blink::WebCryptoKeyFormatRaw,
719                              reinterpret_cast<const uint8*>(jwk_k_value.data()),
720                              jwk_k_value.size(),
721                              algorithm,
722                              extractable,
723                              usage_mask,
724                              key);
725   } else if (jwk_kty_value == "RSA") {
726
727     // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry
728     // in the JWK, while an RSA private key must have those, plus at least a "d"
729     // (private exponent) entry.
730     // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18,
731     // section 6.3.
732
733     // RSA private key import is not currently supported, so fail here if a "d"
734     // entry is found.
735     // TODO(padolph): Support RSA private key import.
736     if (dict_value->HasKey("d"))
737       return Status::ErrorJwkRsaPrivateKeyUnsupported();
738
739     std::string jwk_n_value;
740     status = GetJwkBytes(dict_value, "n", &jwk_n_value);
741     if (status.IsError())
742       return status;
743     std::string jwk_e_value;
744     status = GetJwkBytes(dict_value, "e", &jwk_e_value);
745     if (status.IsError())
746       return status;
747
748     return ImportRsaPublicKeyInternal(
749         reinterpret_cast<const uint8*>(jwk_n_value.data()),
750         jwk_n_value.size(),
751         reinterpret_cast<const uint8*>(jwk_e_value.data()),
752         jwk_e_value.size(),
753         algorithm,
754         extractable,
755         usage_mask,
756         key);
757
758   } else {
759     return Status::ErrorJwkUnrecognizedKty();
760   }
761
762   return Status::Success();
763 }
764
765 }  // namespace content