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.
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
13 #include "base/lazy_instance.h"
14 #include "base/strings/string_piece.h"
15 #include "base/strings/stringprintf.h"
16 #include "content/child/webcrypto/crypto_data.h"
17 #include "content/child/webcrypto/platform_crypto.h"
18 #include "content/child/webcrypto/shared_crypto.h"
19 #include "content/child/webcrypto/status.h"
20 #include "content/child/webcrypto/webcrypto_util.h"
21 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
23 // JSON Web Key Format (JWK)
24 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21
26 // A JWK is a simple JSON dictionary with the following entries
27 // - "kty" (Key Type) Parameter, REQUIRED
28 // - <kty-specific parameters, see below>, REQUIRED
29 // - "use" (Key Use) Parameter, OPTIONAL
30 // - "key_ops" (Key Operations) Parameter, OPTIONAL
31 // - "alg" (Algorithm) Parameter, OPTIONAL
32 // - "ext" (Key Exportability), OPTIONAL
33 // (all other entries are ignored)
35 // OPTIONAL here means that this code does not require the entry to be present
36 // in the incoming JWK, because the method input parameters contain similar
37 // information. If the optional JWK entry is present, it will be validated
38 // against the corresponding input parameter for consistency and combined with
39 // it according to rules defined below.
41 // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK
42 // values are parsed out and combined with the method input parameters to
43 // build a Web Crypto Key:
44 // Web Crypto Key type <-- (deduced)
45 // Web Crypto Key extractable <-- JWK ext + input extractable
46 // Web Crypto Key algorithm <-- JWK alg + input algorithm
47 // Web Crypto Key keyUsage <-- JWK use, key_ops + input usage_mask
48 // Web Crypto Key keying material <-- kty-specific parameters
50 // Values for each JWK entry are case-sensitive and defined in
51 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18.
52 // Note that not all values specified by JOSE are handled by this code. Only
53 // handled values are listed.
55 // +-------+--------------------------------------------------------------+
56 // | "RSA" | RSA [RFC3447] |
57 // | "oct" | Octet sequence (used to represent symmetric keys) |
58 // +-------+--------------------------------------------------------------+
60 // - key_ops (Key Use Details)
61 // The key_ops field is an array that contains one or more strings from
62 // the table below, and describes the operations for which this key may be
64 // +-------+--------------------------------------------------------------+
65 // | "encrypt" | encrypt operations |
66 // | "decrypt" | decrypt operations |
67 // | "sign" | sign (MAC) operations |
68 // | "verify" | verify (MAC) operations |
69 // | "wrapKey" | key wrap |
70 // | "unwrapKey" | key unwrap |
71 // | "deriveKey" | key derivation |
72 // | "deriveBits" | key derivation |
73 // +-------+--------------------------------------------------------------+
76 // The use field contains a single entry from the table below.
77 // +-------+--------------------------------------------------------------+
78 // | "sig" | equivalent to key_ops of [sign, verify] |
79 // | "enc" | equivalent to key_ops of [encrypt, decrypt, wrapKey, |
80 // | | unwrapKey, deriveKey, deriveBits] |
81 // +-------+--------------------------------------------------------------+
83 // NOTE: If both "use" and "key_ops" JWK members are present, the usages
84 // specified by them MUST be consistent. In particular, the "use" value
85 // "sig" corresponds to "sign" and/or "verify". The "use" value "enc"
86 // corresponds to all other values defined above. If "key_ops" values
87 // corresponding to both "sig" and "enc" "use" values are present, the "use"
88 // member SHOULD NOT be present, and if present, its value MUST NOT be
89 // either "sig" or "enc".
91 // - ext (Key Exportability)
92 // +-------+--------------------------------------------------------------+
93 // | true | Key may be exported from the trusted environment |
94 // | false | Key cannot exit the trusted environment |
95 // +-------+--------------------------------------------------------------+
98 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18
99 // +--------------+-------------------------------------------------------+
100 // | Digital Signature or MAC Algorithm |
101 // +--------------+-------------------------------------------------------+
102 // | "HS1" | HMAC using SHA-1 hash algorithm |
103 // | "HS256" | HMAC using SHA-256 hash algorithm |
104 // | "HS384" | HMAC using SHA-384 hash algorithm |
105 // | "HS512" | HMAC using SHA-512 hash algorithm |
106 // | "RS1" | RSASSA using SHA-1 hash algorithm
107 // | "RS256" | RSASSA using SHA-256 hash algorithm |
108 // | "RS384" | RSASSA using SHA-384 hash algorithm |
109 // | "RS512" | RSASSA using SHA-512 hash algorithm |
110 // +--------------+-------------------------------------------------------|
111 // | Key Management Algorithm |
112 // +--------------+-------------------------------------------------------+
113 // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding |
114 // | | (OAEP) [RFC3447], with the default parameters |
115 // | | specified by RFC3447 in Section A.2.1 |
116 // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm |
117 // | | [RFC3394] using 128 bit keys |
118 // | "A192KW" | AES Key Wrap Algorithm using 192 bit keys |
119 // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys |
120 // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using |
121 // | | 128 bit keys |
122 // | "A192GCM" | AES GCM using 192 bit keys |
123 // | "A256GCM" | AES GCM using 256 bit keys |
124 // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 |
125 // | | padding [NIST.800-38A] |
126 // | "A192CBC" | AES CBC using 192 bit keys |
127 // | "A256CBC" | AES CBC using 256 bit keys |
128 // +--------------+-------------------------------------------------------+
130 // kty-specific parameters
131 // The value of kty determines the type and content of the keying material
132 // carried in the JWK to be imported.
133 // // - kty == "oct" (symmetric or other raw key)
134 // +-------+--------------------------------------------------------------+
135 // | "k" | Contains the value of the symmetric (or other single-valued) |
136 // | | key. It is represented as the base64url encoding of the |
137 // | | octet sequence containing the key value. |
138 // +-------+--------------------------------------------------------------+
139 // - kty == "RSA" (RSA public key)
140 // +-------+--------------------------------------------------------------+
141 // | "n" | Contains the modulus value for the RSA public key. It is |
142 // | | represented as the base64url encoding of the value's |
143 // | | unsigned big endian representation as an octet sequence. |
144 // +-------+--------------------------------------------------------------+
145 // | "e" | Contains the exponent value for the RSA public key. It is |
146 // | | represented as the base64url encoding of the value's |
147 // | | unsigned big endian representation as an octet sequence. |
148 // +-------+--------------------------------------------------------------+
149 // - If key == "RSA" and the "d" parameter is present then it is a private key.
150 // All the parameters above for public keys apply, as well as the following.
151 // (Note that except for "d", all of these are optional):
152 // +-------+--------------------------------------------------------------+
153 // | "d" | Contains the private exponent value for the RSA private key. |
154 // | | It is represented as the base64url encoding of the value's |
155 // | | unsigned big endian representation as an octet sequence. |
156 // +-------+--------------------------------------------------------------+
157 // | "p" | Contains the first prime factor value for the RSA private |
158 // | | key. It is represented as the base64url encoding of the |
160 // | | unsigned big endian representation as an octet sequence. |
161 // +-------+--------------------------------------------------------------+
162 // | "q" | Contains the second prime factor value for the RSA private |
163 // | | key. It is represented as the base64url encoding of the |
164 // | | value's unsigned big endian representation as an octet |
166 // +-------+--------------------------------------------------------------+
167 // | "dp" | Contains the first factor CRT exponent value for the RSA |
168 // | | private key. It is represented as the base64url encoding of |
169 // | | the value's unsigned big endian representation as an octet |
171 // +-------+--------------------------------------------------------------+
172 // | "dq" | Contains the second factor CRT exponent value for the RSA |
173 // | | private key. It is represented as the base64url encoding of |
174 // | | the value's unsigned big endian representation as an octet |
176 // +-------+--------------------------------------------------------------+
177 // | "dq" | Contains the first CRT coefficient value for the RSA private |
178 // | | key. It is represented as the base64url encoding of the |
179 // | | value's unsigned big endian representation as an octet |
181 // +-------+--------------------------------------------------------------+
183 // Consistency and conflict resolution
184 // The 'algorithm', 'extractable', and 'usage_mask' input parameters
185 // may be different than the corresponding values inside the JWK. The Web
186 // Crypto spec says that if a JWK value is present but is inconsistent with
187 // the input value, it is an error and the operation must fail. If no
188 // inconsistency is found then the input parameters are used.
191 // If the JWK algorithm is provided, it must match the web crypto input
192 // algorithm (both the algorithm ID and inner hash if applicable).
195 // If the JWK ext field is true but the input parameter is false, make the
196 // Web Crypto Key non-extractable. Conversely, if the JWK ext field is
197 // false but the input parameter is true, it is an inconsistency. If both
198 // are true or both are false, use that value.
201 // The input usage_mask must be a strict subset of the interpreted JWK use
202 // value, else it is judged inconsistent. In all cases the input usage_mask
203 // is used as the final usage_mask.
208 namespace webcrypto
{
212 // Creates an RSASSA-PKCS1-v1_5 algorithm. It is an error to call this with a
213 // hash_id that is not a SHA*.
214 blink::WebCryptoAlgorithm
CreateRsaSsaImportAlgorithm(
215 blink::WebCryptoAlgorithmId hash_id
) {
216 return CreateRsaHashedImportAlgorithm(
217 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5
, hash_id
);
220 // Creates an RSA-OAEP algorithm. It is an error to call this with a hash_id
221 // that is not a SHA*.
222 blink::WebCryptoAlgorithm
CreateRsaOaepImportAlgorithm(
223 blink::WebCryptoAlgorithmId hash_id
) {
224 return CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep
,
228 // Web Crypto equivalent usage mask for JWK 'use' = 'enc'.
229 const blink::WebCryptoKeyUsageMask kJwkEncUsage
=
230 blink::WebCryptoKeyUsageEncrypt
| blink::WebCryptoKeyUsageDecrypt
|
231 blink::WebCryptoKeyUsageWrapKey
| blink::WebCryptoKeyUsageUnwrapKey
|
232 blink::WebCryptoKeyUsageDeriveKey
| blink::WebCryptoKeyUsageDeriveBits
;
233 // Web Crypto equivalent usage mask for JWK 'use' = 'sig'.
234 const blink::WebCryptoKeyUsageMask kJwkSigUsage
=
235 blink::WebCryptoKeyUsageSign
| blink::WebCryptoKeyUsageVerify
;
237 typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc
)();
239 class JwkAlgorithmInfo
{
242 : creation_func_(NULL
),
243 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT
) {}
245 explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func
)
246 : creation_func_(algorithm_creation_func
),
247 required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT
) {}
249 JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func
,
250 unsigned int required_key_length_bits
)
251 : creation_func_(algorithm_creation_func
),
252 required_key_length_bytes_(required_key_length_bits
/ 8) {
253 DCHECK_EQ(0u, required_key_length_bits
% 8);
256 bool CreateImportAlgorithm(blink::WebCryptoAlgorithm
* algorithm
) const {
257 *algorithm
= creation_func_();
258 return !algorithm
->isNull();
261 bool IsInvalidKeyByteLength(size_t byte_length
) const {
262 if (required_key_length_bytes_
== NO_KEY_SIZE_REQUIREMENT
)
264 return required_key_length_bytes_
!= byte_length
;
268 static const unsigned int NO_KEY_SIZE_REQUIREMENT
= UINT_MAX
;
270 AlgorithmCreationFunc creation_func_
;
272 // The expected key size for the algorithm or NO_KEY_SIZE_REQUIREMENT.
273 unsigned int required_key_length_bytes_
;
276 typedef std::map
<std::string
, JwkAlgorithmInfo
> JwkAlgorithmInfoMap
;
278 class JwkAlgorithmRegistry
{
280 JwkAlgorithmRegistry() {
282 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20
283 // says HMAC with SHA-2 should have a key size at least as large as the
285 alg_to_info_
["HS1"] =
286 JwkAlgorithmInfo(&BindAlgorithmId
<CreateHmacImportAlgorithm
,
287 blink::WebCryptoAlgorithmIdSha1
>);
288 alg_to_info_
["HS256"] =
289 JwkAlgorithmInfo(&BindAlgorithmId
<CreateHmacImportAlgorithm
,
290 blink::WebCryptoAlgorithmIdSha256
>);
291 alg_to_info_
["HS384"] =
292 JwkAlgorithmInfo(&BindAlgorithmId
<CreateHmacImportAlgorithm
,
293 blink::WebCryptoAlgorithmIdSha384
>);
294 alg_to_info_
["HS512"] =
295 JwkAlgorithmInfo(&BindAlgorithmId
<CreateHmacImportAlgorithm
,
296 blink::WebCryptoAlgorithmIdSha512
>);
297 alg_to_info_
["RS1"] =
298 JwkAlgorithmInfo(&BindAlgorithmId
<CreateRsaSsaImportAlgorithm
,
299 blink::WebCryptoAlgorithmIdSha1
>);
300 alg_to_info_
["RS256"] =
301 JwkAlgorithmInfo(&BindAlgorithmId
<CreateRsaSsaImportAlgorithm
,
302 blink::WebCryptoAlgorithmIdSha256
>);
303 alg_to_info_
["RS384"] =
304 JwkAlgorithmInfo(&BindAlgorithmId
<CreateRsaSsaImportAlgorithm
,
305 blink::WebCryptoAlgorithmIdSha384
>);
306 alg_to_info_
["RS512"] =
307 JwkAlgorithmInfo(&BindAlgorithmId
<CreateRsaSsaImportAlgorithm
,
308 blink::WebCryptoAlgorithmIdSha512
>);
309 alg_to_info_
["RSA-OAEP"] =
310 JwkAlgorithmInfo(&BindAlgorithmId
<CreateRsaOaepImportAlgorithm
,
311 blink::WebCryptoAlgorithmIdSha1
>);
312 alg_to_info_
["RSA-OAEP-256"] =
313 JwkAlgorithmInfo(&BindAlgorithmId
<CreateRsaOaepImportAlgorithm
,
314 blink::WebCryptoAlgorithmIdSha256
>);
315 alg_to_info_
["RSA-OAEP-384"] =
316 JwkAlgorithmInfo(&BindAlgorithmId
<CreateRsaOaepImportAlgorithm
,
317 blink::WebCryptoAlgorithmIdSha384
>);
318 alg_to_info_
["RSA-OAEP-512"] =
319 JwkAlgorithmInfo(&BindAlgorithmId
<CreateRsaOaepImportAlgorithm
,
320 blink::WebCryptoAlgorithmIdSha512
>);
321 alg_to_info_
["A128KW"] = JwkAlgorithmInfo(
322 &BindAlgorithmId
<CreateAlgorithm
, blink::WebCryptoAlgorithmIdAesKw
>,
324 alg_to_info_
["A192KW"] = JwkAlgorithmInfo(
325 &BindAlgorithmId
<CreateAlgorithm
, blink::WebCryptoAlgorithmIdAesKw
>,
327 alg_to_info_
["A256KW"] = JwkAlgorithmInfo(
328 &BindAlgorithmId
<CreateAlgorithm
, blink::WebCryptoAlgorithmIdAesKw
>,
330 alg_to_info_
["A128GCM"] = JwkAlgorithmInfo(
331 &BindAlgorithmId
<CreateAlgorithm
, blink::WebCryptoAlgorithmIdAesGcm
>,
333 alg_to_info_
["A192GCM"] = JwkAlgorithmInfo(
334 &BindAlgorithmId
<CreateAlgorithm
, blink::WebCryptoAlgorithmIdAesGcm
>,
336 alg_to_info_
["A256GCM"] = JwkAlgorithmInfo(
337 &BindAlgorithmId
<CreateAlgorithm
, blink::WebCryptoAlgorithmIdAesGcm
>,
339 alg_to_info_
["A128CBC"] = JwkAlgorithmInfo(
340 &BindAlgorithmId
<CreateAlgorithm
, blink::WebCryptoAlgorithmIdAesCbc
>,
342 alg_to_info_
["A192CBC"] = JwkAlgorithmInfo(
343 &BindAlgorithmId
<CreateAlgorithm
, blink::WebCryptoAlgorithmIdAesCbc
>,
345 alg_to_info_
["A256CBC"] = JwkAlgorithmInfo(
346 &BindAlgorithmId
<CreateAlgorithm
, blink::WebCryptoAlgorithmIdAesCbc
>,
350 // Returns NULL if the algorithm name was not registered.
351 const JwkAlgorithmInfo
* GetAlgorithmInfo(const std::string
& jwk_alg
) const {
352 const JwkAlgorithmInfoMap::const_iterator pos
= alg_to_info_
.find(jwk_alg
);
353 if (pos
== alg_to_info_
.end())
359 // Binds a WebCryptoAlgorithmId value to a compatible factory function.
360 typedef blink::WebCryptoAlgorithm (*FuncWithWebCryptoAlgIdArg
)(
361 blink::WebCryptoAlgorithmId
);
362 template <FuncWithWebCryptoAlgIdArg func
,
363 blink::WebCryptoAlgorithmId algorithm_id
>
364 static blink::WebCryptoAlgorithm
BindAlgorithmId() {
365 return func(algorithm_id
);
368 JwkAlgorithmInfoMap alg_to_info_
;
371 base::LazyInstance
<JwkAlgorithmRegistry
> jwk_alg_registry
=
372 LAZY_INSTANCE_INITIALIZER
;
374 bool ImportAlgorithmsConsistent(const blink::WebCryptoAlgorithm
& alg1
,
375 const blink::WebCryptoAlgorithm
& alg2
) {
376 DCHECK(!alg1
.isNull());
377 DCHECK(!alg2
.isNull());
378 if (alg1
.id() != alg2
.id())
380 if (alg1
.paramsType() != alg2
.paramsType())
382 switch (alg1
.paramsType()) {
383 case blink::WebCryptoAlgorithmParamsTypeNone
:
385 case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams
:
386 return ImportAlgorithmsConsistent(alg1
.rsaHashedImportParams()->hash(),
387 alg2
.rsaHashedImportParams()->hash());
388 case blink::WebCryptoAlgorithmParamsTypeHmacImportParams
:
389 return ImportAlgorithmsConsistent(alg1
.hmacImportParams()->hash(),
390 alg2
.hmacImportParams()->hash());
396 // Extracts the required string property with key |path| from |dict| and saves
397 // the result to |*result|. If the property does not exist or is not a string,
399 Status
GetJwkString(base::DictionaryValue
* dict
,
400 const std::string
& path
,
401 std::string
* result
) {
402 base::Value
* value
= NULL
;
403 if (!dict
->Get(path
, &value
))
404 return Status::ErrorJwkPropertyMissing(path
);
405 if (!value
->GetAsString(result
))
406 return Status::ErrorJwkPropertyWrongType(path
, "string");
407 return Status::Success();
410 // Extracts the optional string property with key |path| from |dict| and saves
411 // the result to |*result| if it was found. If the property exists and is not a
412 // string, returns an error. Otherwise returns success, and sets
413 // |*property_exists| if it was found.
414 Status
GetOptionalJwkString(base::DictionaryValue
* dict
,
415 const std::string
& path
,
417 bool* property_exists
) {
418 *property_exists
= false;
419 base::Value
* value
= NULL
;
420 if (!dict
->Get(path
, &value
))
421 return Status::Success();
423 if (!value
->GetAsString(result
))
424 return Status::ErrorJwkPropertyWrongType(path
, "string");
426 *property_exists
= true;
427 return Status::Success();
430 // Extracts the optional array property with key |path| from |dict| and saves
431 // the result to |*result| if it was found. If the property exists and is not an
432 // array, returns an error. Otherwise returns success, and sets
433 // |*property_exists| if it was found. Note that |*result| is owned by |dict|.
434 Status
GetOptionalJwkList(base::DictionaryValue
* dict
,
435 const std::string
& path
,
436 base::ListValue
** result
,
437 bool* property_exists
) {
438 *property_exists
= false;
439 base::Value
* value
= NULL
;
440 if (!dict
->Get(path
, &value
))
441 return Status::Success();
443 if (!value
->GetAsList(result
))
444 return Status::ErrorJwkPropertyWrongType(path
, "list");
446 *property_exists
= true;
447 return Status::Success();
450 // Extracts the required string property with key |path| from |dict| and saves
451 // the base64url-decoded bytes to |*result|. If the property does not exist or
452 // is not a string, or could not be base64url-decoded, returns an error.
453 Status
GetJwkBytes(base::DictionaryValue
* dict
,
454 const std::string
& path
,
455 std::string
* result
) {
456 std::string base64_string
;
457 Status status
= GetJwkString(dict
, path
, &base64_string
);
458 if (status
.IsError())
461 if (!Base64DecodeUrlSafe(base64_string
, result
))
462 return Status::ErrorJwkBase64Decode(path
);
464 return Status::Success();
467 // Extracts the optional string property with key |path| from |dict| and saves
468 // the base64url-decoded bytes to |*result|. If the property exist and is not a
469 // string, or could not be base64url-decoded, returns an error. In the case
470 // where the property does not exist, |result| is guaranteed to be empty.
471 Status
GetOptionalJwkBytes(base::DictionaryValue
* dict
,
472 const std::string
& path
,
474 bool* property_exists
) {
475 std::string base64_string
;
477 GetOptionalJwkString(dict
, path
, &base64_string
, property_exists
);
478 if (status
.IsError())
481 if (!*property_exists
) {
483 return Status::Success();
486 if (!Base64DecodeUrlSafe(base64_string
, result
))
487 return Status::ErrorJwkBase64Decode(path
);
489 return Status::Success();
492 // Extracts the optional boolean property with key |path| from |dict| and saves
493 // the result to |*result| if it was found. If the property exists and is not a
494 // boolean, returns an error. Otherwise returns success, and sets
495 // |*property_exists| if it was found.
496 Status
GetOptionalJwkBool(base::DictionaryValue
* dict
,
497 const std::string
& path
,
499 bool* property_exists
) {
500 *property_exists
= false;
501 base::Value
* value
= NULL
;
502 if (!dict
->Get(path
, &value
))
503 return Status::Success();
505 if (!value
->GetAsBoolean(result
))
506 return Status::ErrorJwkPropertyWrongType(path
, "boolean");
508 *property_exists
= true;
509 return Status::Success();
512 // Writes a secret/symmetric key to a JWK dictionary.
513 void WriteSecretKey(const std::vector
<uint8
>& raw_key
,
514 base::DictionaryValue
* jwk_dict
) {
516 jwk_dict
->SetString("kty", "oct");
517 // For a secret/symmetric key, the only extra JWK field is 'k', containing the
518 // base64url encoding of the raw key.
519 const base::StringPiece
key_str(
520 reinterpret_cast<const char*>(Uint8VectorStart(raw_key
)), raw_key
.size());
521 jwk_dict
->SetString("k", Base64EncodeUrlSafe(key_str
));
524 // Writes an RSA public key to a JWK dictionary
525 void WriteRsaPublicKey(const std::vector
<uint8
>& modulus
,
526 const std::vector
<uint8
>& public_exponent
,
527 base::DictionaryValue
* jwk_dict
) {
529 DCHECK(modulus
.size());
530 DCHECK(public_exponent
.size());
531 jwk_dict
->SetString("kty", "RSA");
532 jwk_dict
->SetString("n", Base64EncodeUrlSafe(modulus
));
533 jwk_dict
->SetString("e", Base64EncodeUrlSafe(public_exponent
));
536 // Writes an RSA private key to a JWK dictionary
537 Status
ExportRsaPrivateKeyJwk(const blink::WebCryptoKey
& key
,
538 base::DictionaryValue
* jwk_dict
) {
539 platform::PrivateKey
* private_key
;
540 Status status
= ToPlatformPrivateKey(key
, &private_key
);
541 if (status
.IsError())
544 // TODO(eroman): Copying the key properties to temporary vectors is
545 // inefficient. Once there aren't two implementations of platform_crypto this
546 // and other code will be easier to streamline.
547 std::vector
<uint8
> modulus
;
548 std::vector
<uint8
> public_exponent
;
549 std::vector
<uint8
> private_exponent
;
550 std::vector
<uint8
> prime1
;
551 std::vector
<uint8
> prime2
;
552 std::vector
<uint8
> exponent1
;
553 std::vector
<uint8
> exponent2
;
554 std::vector
<uint8
> coefficient
;
556 status
= platform::ExportRsaPrivateKey(private_key
,
565 if (status
.IsError())
568 jwk_dict
->SetString("kty", "RSA");
569 jwk_dict
->SetString("n", Base64EncodeUrlSafe(modulus
));
570 jwk_dict
->SetString("e", Base64EncodeUrlSafe(public_exponent
));
571 jwk_dict
->SetString("d", Base64EncodeUrlSafe(private_exponent
));
572 // Although these are "optional" in the JWA, WebCrypto spec requires them to
574 jwk_dict
->SetString("p", Base64EncodeUrlSafe(prime1
));
575 jwk_dict
->SetString("q", Base64EncodeUrlSafe(prime2
));
576 jwk_dict
->SetString("dp", Base64EncodeUrlSafe(exponent1
));
577 jwk_dict
->SetString("dq", Base64EncodeUrlSafe(exponent2
));
578 jwk_dict
->SetString("qi", Base64EncodeUrlSafe(coefficient
));
580 return Status::Success();
583 // Writes a Web Crypto usage mask to a JWK dictionary.
584 void WriteKeyOps(blink::WebCryptoKeyUsageMask key_usages
,
585 base::DictionaryValue
* jwk_dict
) {
586 jwk_dict
->Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(key_usages
));
589 // Writes a Web Crypto extractable value to a JWK dictionary.
590 void WriteExt(bool extractable
, base::DictionaryValue
* jwk_dict
) {
591 jwk_dict
->SetBoolean("ext", extractable
);
594 // Writes a Web Crypto algorithm to a JWK dictionary.
595 Status
WriteAlg(const blink::WebCryptoKeyAlgorithm
& algorithm
,
596 base::DictionaryValue
* jwk_dict
) {
597 switch (algorithm
.paramsType()) {
598 case blink::WebCryptoKeyAlgorithmParamsTypeAes
: {
599 DCHECK(algorithm
.aesParams());
600 const char* aes_prefix
= "";
601 switch (algorithm
.aesParams()->lengthBits()) {
612 NOTREACHED(); // bad key length means algorithm was built improperly
613 return Status::ErrorUnexpected();
615 const char* aes_suffix
= "";
616 switch (algorithm
.id()) {
617 case blink::WebCryptoAlgorithmIdAesCbc
:
620 case blink::WebCryptoAlgorithmIdAesCtr
:
623 case blink::WebCryptoAlgorithmIdAesGcm
:
626 case blink::WebCryptoAlgorithmIdAesKw
:
630 return Status::ErrorUnsupported();
632 jwk_dict
->SetString("alg",
633 base::StringPrintf("%s%s", aes_prefix
, aes_suffix
));
636 case blink::WebCryptoKeyAlgorithmParamsTypeHmac
: {
637 DCHECK(algorithm
.hmacParams());
638 switch (algorithm
.hmacParams()->hash().id()) {
639 case blink::WebCryptoAlgorithmIdSha1
:
640 jwk_dict
->SetString("alg", "HS1");
642 case blink::WebCryptoAlgorithmIdSha256
:
643 jwk_dict
->SetString("alg", "HS256");
645 case blink::WebCryptoAlgorithmIdSha384
:
646 jwk_dict
->SetString("alg", "HS384");
648 case blink::WebCryptoAlgorithmIdSha512
:
649 jwk_dict
->SetString("alg", "HS512");
653 return Status::ErrorUnexpected();
657 case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed
:
658 switch (algorithm
.id()) {
659 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5
: {
660 switch (algorithm
.rsaHashedParams()->hash().id()) {
661 case blink::WebCryptoAlgorithmIdSha1
:
662 jwk_dict
->SetString("alg", "RS1");
664 case blink::WebCryptoAlgorithmIdSha256
:
665 jwk_dict
->SetString("alg", "RS256");
667 case blink::WebCryptoAlgorithmIdSha384
:
668 jwk_dict
->SetString("alg", "RS384");
670 case blink::WebCryptoAlgorithmIdSha512
:
671 jwk_dict
->SetString("alg", "RS512");
675 return Status::ErrorUnexpected();
679 case blink::WebCryptoAlgorithmIdRsaOaep
: {
680 switch (algorithm
.rsaHashedParams()->hash().id()) {
681 case blink::WebCryptoAlgorithmIdSha1
:
682 jwk_dict
->SetString("alg", "RSA-OAEP");
684 case blink::WebCryptoAlgorithmIdSha256
:
685 jwk_dict
->SetString("alg", "RSA-OAEP-256");
687 case blink::WebCryptoAlgorithmIdSha384
:
688 jwk_dict
->SetString("alg", "RSA-OAEP-384");
690 case blink::WebCryptoAlgorithmIdSha512
:
691 jwk_dict
->SetString("alg", "RSA-OAEP-512");
695 return Status::ErrorUnexpected();
701 return Status::ErrorUnexpected();
705 return Status::ErrorUnsupported();
707 return Status::Success();
710 bool IsRsaKey(const blink::WebCryptoKey
& key
) {
711 return IsAlgorithmRsa(key
.algorithm().id());
714 Status
ImportRsaKey(base::DictionaryValue
* dict
,
715 const blink::WebCryptoAlgorithm
& algorithm
,
717 blink::WebCryptoKeyUsageMask usage_mask
,
718 blink::WebCryptoKey
* key
) {
719 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry
720 // in the JWK, while an RSA private key must have those, plus at least a "d"
721 // (private exponent) entry.
722 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18,
724 std::string jwk_n_value
;
725 Status status
= GetJwkBytes(dict
, "n", &jwk_n_value
);
726 if (status
.IsError())
728 std::string jwk_e_value
;
729 status
= GetJwkBytes(dict
, "e", &jwk_e_value
);
730 if (status
.IsError())
733 bool is_public_key
= !dict
->HasKey("d");
735 // Now that the key type is known, do an additional check on the usages to
736 // make sure they are all applicable for this algorithm + key type.
737 status
= CheckKeyUsages(algorithm
.id(),
738 is_public_key
? blink::WebCryptoKeyTypePublic
739 : blink::WebCryptoKeyTypePrivate
,
742 if (status
.IsError())
746 return platform::ImportRsaPublicKey(algorithm
,
749 CryptoData(jwk_n_value
),
750 CryptoData(jwk_e_value
),
754 std::string jwk_d_value
;
755 status
= GetJwkBytes(dict
, "d", &jwk_d_value
);
756 if (status
.IsError())
759 // The "p", "q", "dp", "dq", and "qi" properties are optional. Treat these
760 // properties the same if they are unspecified, as if they were specified-but
761 // empty, since ImportRsaPrivateKey() doesn't do validation checks anyway.
763 std::string jwk_p_value
;
765 status
= GetOptionalJwkBytes(dict
, "p", &jwk_p_value
, &has_p
);
766 if (status
.IsError())
769 std::string jwk_q_value
;
771 status
= GetOptionalJwkBytes(dict
, "q", &jwk_q_value
, &has_q
);
772 if (status
.IsError())
775 std::string jwk_dp_value
;
777 status
= GetOptionalJwkBytes(dict
, "dp", &jwk_dp_value
, &has_dp
);
778 if (status
.IsError())
781 std::string jwk_dq_value
;
783 status
= GetOptionalJwkBytes(dict
, "dq", &jwk_dq_value
, &has_dq
);
784 if (status
.IsError())
787 std::string jwk_qi_value
;
789 status
= GetOptionalJwkBytes(dict
, "qi", &jwk_qi_value
, &has_qi
);
790 if (status
.IsError())
793 int num_optional_properties
= has_p
+ has_q
+ has_dp
+ has_dq
+ has_qi
;
794 if (num_optional_properties
!= 0 && num_optional_properties
!= 5)
795 return Status::ErrorJwkIncompleteOptionalRsaPrivateKey();
797 return platform::ImportRsaPrivateKey(
801 CryptoData(jwk_n_value
), // modulus
802 CryptoData(jwk_e_value
), // public_exponent
803 CryptoData(jwk_d_value
), // private_exponent
804 CryptoData(jwk_p_value
), // prime1
805 CryptoData(jwk_q_value
), // prime2
806 CryptoData(jwk_dp_value
), // exponent1
807 CryptoData(jwk_dq_value
), // exponent2
808 CryptoData(jwk_qi_value
), // coefficient
814 // TODO(eroman): Split this up into smaller functions.
815 Status
ImportKeyJwk(const CryptoData
& key_data
,
816 const blink::WebCryptoAlgorithm
& algorithm
,
818 blink::WebCryptoKeyUsageMask usage_mask
,
819 blink::WebCryptoKey
* key
) {
820 if (!key_data
.byte_length())
821 return Status::ErrorImportEmptyKeyData();
824 // Parse the incoming JWK JSON.
825 base::StringPiece
json_string(reinterpret_cast<const char*>(key_data
.bytes()),
826 key_data
.byte_length());
827 scoped_ptr
<base::Value
> value(base::JSONReader::Read(json_string
));
828 // Note, bare pointer dict_value is ok since it points into scoped value.
829 base::DictionaryValue
* dict_value
= NULL
;
830 if (!value
.get() || !value
->GetAsDictionary(&dict_value
) || !dict_value
)
831 return Status::ErrorJwkNotDictionary();
833 // JWK "kty". Exit early if this required JWK parameter is missing.
834 std::string jwk_kty_value
;
835 Status status
= GetJwkString(dict_value
, "kty", &jwk_kty_value
);
836 if (status
.IsError())
839 // JWK "ext" (optional) --> extractable parameter
841 bool jwk_ext_value
= false;
844 GetOptionalJwkBool(dict_value
, "ext", &jwk_ext_value
, &has_jwk_ext
);
845 if (status
.IsError())
847 if (has_jwk_ext
&& !jwk_ext_value
&& extractable
)
848 return Status::ErrorJwkExtInconsistent();
851 // JWK "alg" --> algorithm parameter
852 // 1. JWK alg present but unrecognized: error
853 // 2. JWK alg valid and inconsistent with input algorithm: error
854 // 3. JWK alg valid and consistent with input algorithm: use input value
855 // 4. JWK alg is missing: use input value
856 const JwkAlgorithmInfo
* algorithm_info
= NULL
;
857 std::string jwk_alg_value
;
860 GetOptionalJwkString(dict_value
, "alg", &jwk_alg_value
, &has_jwk_alg
);
861 if (status
.IsError())
867 // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can
868 // only be from the RSA family.
870 blink::WebCryptoAlgorithm jwk_algorithm
=
871 blink::WebCryptoAlgorithm::createNull();
872 algorithm_info
= jwk_alg_registry
.Get().GetAlgorithmInfo(jwk_alg_value
);
873 if (!algorithm_info
||
874 !algorithm_info
->CreateImportAlgorithm(&jwk_algorithm
))
875 return Status::ErrorJwkUnrecognizedAlgorithm();
877 if (!ImportAlgorithmsConsistent(jwk_algorithm
, algorithm
))
878 return Status::ErrorJwkAlgorithmInconsistent();
880 DCHECK(!algorithm
.isNull());
882 // JWK "key_ops" (optional) --> usage_mask parameter
883 base::ListValue
* jwk_key_ops_value
= NULL
;
884 bool has_jwk_key_ops
;
885 status
= GetOptionalJwkList(
886 dict_value
, "key_ops", &jwk_key_ops_value
, &has_jwk_key_ops
);
887 if (status
.IsError())
889 blink::WebCryptoKeyUsageMask jwk_key_ops_mask
= 0;
890 if (has_jwk_key_ops
) {
892 GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value
, &jwk_key_ops_mask
);
893 if (status
.IsError())
895 // The input usage_mask must be a subset of jwk_key_ops_mask.
896 if (!ContainsKeyUsages(jwk_key_ops_mask
, usage_mask
))
897 return Status::ErrorJwkKeyopsInconsistent();
900 // JWK "use" (optional) --> usage_mask parameter
901 std::string jwk_use_value
;
904 GetOptionalJwkString(dict_value
, "use", &jwk_use_value
, &has_jwk_use
);
905 if (status
.IsError())
907 blink::WebCryptoKeyUsageMask jwk_use_mask
= 0;
909 if (jwk_use_value
== "enc")
910 jwk_use_mask
= kJwkEncUsage
;
911 else if (jwk_use_value
== "sig")
912 jwk_use_mask
= kJwkSigUsage
;
914 return Status::ErrorJwkUnrecognizedUse();
915 // The input usage_mask must be a subset of jwk_use_mask.
916 if (!ContainsKeyUsages(jwk_use_mask
, usage_mask
))
917 return Status::ErrorJwkUseInconsistent();
920 // If both 'key_ops' and 'use' are present, ensure they are consistent.
921 if (has_jwk_key_ops
&& has_jwk_use
&&
922 !ContainsKeyUsages(jwk_use_mask
, jwk_key_ops_mask
))
923 return Status::ErrorJwkUseAndKeyopsInconsistent();
925 // JWK keying material --> ImportKeyInternal()
926 if (jwk_kty_value
== "oct") {
927 std::string jwk_k_value
;
928 status
= GetJwkBytes(dict_value
, "k", &jwk_k_value
);
929 if (status
.IsError())
932 // Some JWK alg ID's embed information about the key length in the alg ID
933 // string. For example "A128CBC" implies the JWK carries 128 bits
934 // of key material. For such keys validate that enough bytes were provided.
935 // If this validation is not done, then it would be possible to select a
936 // different algorithm by passing a different lengthed key, since that is
937 // how WebCrypto interprets things.
938 if (algorithm_info
&&
939 algorithm_info
->IsInvalidKeyByteLength(jwk_k_value
.size())) {
940 return Status::ErrorJwkIncorrectKeyLength();
943 return ImportKey(blink::WebCryptoKeyFormatRaw
,
944 CryptoData(jwk_k_value
),
951 if (jwk_kty_value
== "RSA")
952 return ImportRsaKey(dict_value
, algorithm
, extractable
, usage_mask
, key
);
954 return Status::ErrorJwkUnrecognizedKty();
957 Status
ExportKeyJwk(const blink::WebCryptoKey
& key
,
958 std::vector
<uint8
>* buffer
) {
959 DCHECK(key
.extractable());
960 base::DictionaryValue jwk_dict
;
961 Status status
= Status::OperationError();
963 switch (key
.type()) {
964 case blink::WebCryptoKeyTypeSecret
: {
965 std::vector
<uint8
> exported_key
;
966 status
= ExportKey(blink::WebCryptoKeyFormatRaw
, key
, &exported_key
);
967 if (status
.IsError())
969 WriteSecretKey(exported_key
, &jwk_dict
);
972 case blink::WebCryptoKeyTypePublic
: {
973 // TODO(eroman): Update when there are asymmetric keys other than RSA.
975 return Status::ErrorUnsupported();
976 platform::PublicKey
* public_key
;
977 status
= ToPlatformPublicKey(key
, &public_key
);
978 if (status
.IsError())
980 std::vector
<uint8
> modulus
;
981 std::vector
<uint8
> public_exponent
;
983 platform::ExportRsaPublicKey(public_key
, &modulus
, &public_exponent
);
984 if (status
.IsError())
986 WriteRsaPublicKey(modulus
, public_exponent
, &jwk_dict
);
989 case blink::WebCryptoKeyTypePrivate
: {
990 // TODO(eroman): Update when there are asymmetric keys other than RSA.
992 return Status::ErrorUnsupported();
994 status
= ExportRsaPrivateKeyJwk(key
, &jwk_dict
);
995 if (status
.IsError())
1001 return Status::ErrorUnsupported();
1004 WriteKeyOps(key
.usages(), &jwk_dict
);
1005 WriteExt(key
.extractable(), &jwk_dict
);
1006 status
= WriteAlg(key
.algorithm(), &jwk_dict
);
1007 if (status
.IsError())
1011 base::JSONWriter::Write(&jwk_dict
, &json
);
1012 buffer
->assign(json
.data(), json
.data() + json
.size());
1013 return Status::Success();
1016 } // namespace webcrypto
1018 } // namespace content