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.
5 #include "content/renderer/webcrypto/webcrypto_impl.h"
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"
25 bool IsAlgorithmAsymmetric(const blink::WebCryptoAlgorithm
& algorithm
) {
26 // TODO(padolph): include all other asymmetric algorithms once they are
27 // defined, e.g. EC and DH.
28 return (algorithm
.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5
||
29 algorithm
.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5
||
30 algorithm
.id() == blink::WebCryptoAlgorithmIdRsaOaep
);
33 // Binds a specific key length value to a compatible factory function.
34 typedef blink::WebCryptoAlgorithm (*AlgFactoryFuncWithOneShortArg
)(
36 template <AlgFactoryFuncWithOneShortArg func
, unsigned short key_length
>
37 blink::WebCryptoAlgorithm
BindAlgFactoryWithKeyLen() {
38 return func(key_length
);
41 // Binds a WebCryptoAlgorithmId value to a compatible factory function.
42 typedef blink::WebCryptoAlgorithm (*AlgFactoryFuncWithWebCryptoAlgIdArg
)(
43 blink::WebCryptoAlgorithmId
);
44 template <AlgFactoryFuncWithWebCryptoAlgIdArg func
,
45 blink::WebCryptoAlgorithmId algorithm_id
>
46 blink::WebCryptoAlgorithm
BindAlgFactoryAlgorithmId() {
47 return func(algorithm_id
);
50 // Defines a map between a JWK 'alg' string ID and a corresponding Web Crypto
52 typedef blink::WebCryptoAlgorithm (*AlgFactoryFuncNoArgs
)();
53 typedef std::map
<std::string
, AlgFactoryFuncNoArgs
> JwkAlgFactoryMap
;
55 class JwkAlgorithmFactoryMap
{
57 JwkAlgorithmFactoryMap() {
59 &BindAlgFactoryAlgorithmId
<webcrypto::CreateHmacAlgorithmByHashId
,
60 blink::WebCryptoAlgorithmIdSha256
>;
62 &BindAlgFactoryAlgorithmId
<webcrypto::CreateHmacAlgorithmByHashId
,
63 blink::WebCryptoAlgorithmIdSha384
>;
65 &BindAlgFactoryAlgorithmId
<webcrypto::CreateHmacAlgorithmByHashId
,
66 blink::WebCryptoAlgorithmIdSha512
>;
68 &BindAlgFactoryAlgorithmId
<webcrypto::CreateRsaSsaAlgorithm
,
69 blink::WebCryptoAlgorithmIdSha256
>;
71 &BindAlgFactoryAlgorithmId
<webcrypto::CreateRsaSsaAlgorithm
,
72 blink::WebCryptoAlgorithmIdSha384
>;
74 &BindAlgFactoryAlgorithmId
<webcrypto::CreateRsaSsaAlgorithm
,
75 blink::WebCryptoAlgorithmIdSha512
>;
77 &BindAlgFactoryAlgorithmId
<webcrypto::CreateAlgorithm
,
78 blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5
>;
80 &BindAlgFactoryAlgorithmId
<webcrypto::CreateRsaOaepAlgorithm
,
81 blink::WebCryptoAlgorithmIdSha1
>;
82 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 128 yet
83 map_
["A128KW"] = &blink::WebCryptoAlgorithm::createNull
;
84 // TODO(padolph): The Web Crypto spec does not enumerate AES-KW 256 yet
85 map_
["A256KW"] = &blink::WebCryptoAlgorithm::createNull
;
87 &BindAlgFactoryAlgorithmId
<webcrypto::CreateAlgorithm
,
88 blink::WebCryptoAlgorithmIdAesGcm
>;
90 &BindAlgFactoryAlgorithmId
<webcrypto::CreateAlgorithm
,
91 blink::WebCryptoAlgorithmIdAesGcm
>;
93 &BindAlgFactoryAlgorithmId
<webcrypto::CreateAlgorithm
,
94 blink::WebCryptoAlgorithmIdAesCbc
>;
96 &BindAlgFactoryAlgorithmId
<webcrypto::CreateAlgorithm
,
97 blink::WebCryptoAlgorithmIdAesCbc
>;
99 &BindAlgFactoryAlgorithmId
<webcrypto::CreateAlgorithm
,
100 blink::WebCryptoAlgorithmIdAesCbc
>;
103 blink::WebCryptoAlgorithm
CreateAlgorithmFromName(const std::string
& alg_id
)
105 const JwkAlgFactoryMap::const_iterator pos
= map_
.find(alg_id
);
106 if (pos
== map_
.end())
107 return blink::WebCryptoAlgorithm::createNull();
108 return pos
->second();
112 JwkAlgFactoryMap map_
;
115 base::LazyInstance
<JwkAlgorithmFactoryMap
> jwk_alg_factory
=
116 LAZY_INSTANCE_INITIALIZER
;
118 bool WebCryptoAlgorithmsConsistent(const blink::WebCryptoAlgorithm
& alg1
,
119 const blink::WebCryptoAlgorithm
& alg2
) {
120 DCHECK(!alg1
.isNull());
121 DCHECK(!alg2
.isNull());
122 if (alg1
.id() != alg2
.id())
125 case blink::WebCryptoAlgorithmIdHmac
:
126 case blink::WebCryptoAlgorithmIdRsaOaep
:
127 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5
:
128 if (WebCryptoAlgorithmsConsistent(
129 webcrypto::GetInnerHashAlgorithm(alg1
),
130 webcrypto::GetInnerHashAlgorithm(alg2
))) {
134 case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5
:
135 case blink::WebCryptoAlgorithmIdSha1
:
136 case blink::WebCryptoAlgorithmIdSha224
:
137 case blink::WebCryptoAlgorithmIdSha256
:
138 case blink::WebCryptoAlgorithmIdSha384
:
139 case blink::WebCryptoAlgorithmIdSha512
:
140 case blink::WebCryptoAlgorithmIdAesCbc
:
141 case blink::WebCryptoAlgorithmIdAesGcm
:
142 case blink::WebCryptoAlgorithmIdAesCtr
:
145 NOTREACHED(); // Not a supported algorithm.
151 bool GetDecodedUrl64ValueByKey(
152 const base::DictionaryValue
& dict
,
153 const std::string
& key
,
154 std::string
* decoded
) {
155 std::string value_url64
;
156 if (!dict
.GetString(key
, &value_url64
) ||
157 !webcrypto::Base64DecodeUrlSafe(value_url64
, decoded
) ||
166 WebCryptoImpl::WebCryptoImpl() {
170 void WebCryptoImpl::encrypt(
171 const blink::WebCryptoAlgorithm
& algorithm
,
172 const blink::WebCryptoKey
& key
,
173 const unsigned char* data
,
175 blink::WebCryptoResult result
) {
176 DCHECK(!algorithm
.isNull());
177 blink::WebArrayBuffer buffer
;
178 if (!EncryptInternal(algorithm
, key
, data
, data_size
, &buffer
)) {
179 result
.completeWithError();
181 result
.completeWithBuffer(buffer
);
185 void WebCryptoImpl::decrypt(
186 const blink::WebCryptoAlgorithm
& algorithm
,
187 const blink::WebCryptoKey
& key
,
188 const unsigned char* data
,
190 blink::WebCryptoResult result
) {
191 DCHECK(!algorithm
.isNull());
192 blink::WebArrayBuffer buffer
;
193 if (!DecryptInternal(algorithm
, key
, data
, data_size
, &buffer
)) {
194 result
.completeWithError();
196 result
.completeWithBuffer(buffer
);
200 void WebCryptoImpl::digest(
201 const blink::WebCryptoAlgorithm
& algorithm
,
202 const unsigned char* data
,
204 blink::WebCryptoResult result
) {
205 DCHECK(!algorithm
.isNull());
206 blink::WebArrayBuffer buffer
;
207 if (!DigestInternal(algorithm
, data
, data_size
, &buffer
)) {
208 result
.completeWithError();
210 result
.completeWithBuffer(buffer
);
214 void WebCryptoImpl::generateKey(
215 const blink::WebCryptoAlgorithm
& algorithm
,
217 blink::WebCryptoKeyUsageMask usage_mask
,
218 blink::WebCryptoResult result
) {
219 DCHECK(!algorithm
.isNull());
220 if (IsAlgorithmAsymmetric(algorithm
)) {
221 blink::WebCryptoKey public_key
= blink::WebCryptoKey::createNull();
222 blink::WebCryptoKey private_key
= blink::WebCryptoKey::createNull();
223 if (!GenerateKeyPairInternal(
224 algorithm
, extractable
, usage_mask
, &public_key
, &private_key
)) {
225 result
.completeWithError();
227 DCHECK(public_key
.handle());
228 DCHECK(private_key
.handle());
229 DCHECK_EQ(algorithm
.id(), public_key
.algorithm().id());
230 DCHECK_EQ(algorithm
.id(), private_key
.algorithm().id());
231 DCHECK_EQ(true, public_key
.extractable());
232 DCHECK_EQ(extractable
, private_key
.extractable());
233 DCHECK_EQ(usage_mask
, public_key
.usages());
234 DCHECK_EQ(usage_mask
, private_key
.usages());
235 result
.completeWithKeyPair(public_key
, private_key
);
238 blink::WebCryptoKey key
= blink::WebCryptoKey::createNull();
239 if (!GenerateKeyInternal(algorithm
, extractable
, usage_mask
, &key
)) {
240 result
.completeWithError();
242 DCHECK(key
.handle());
243 DCHECK_EQ(algorithm
.id(), key
.algorithm().id());
244 DCHECK_EQ(extractable
, key
.extractable());
245 DCHECK_EQ(usage_mask
, key
.usages());
246 result
.completeWithKey(key
);
251 void WebCryptoImpl::importKey(
252 blink::WebCryptoKeyFormat format
,
253 const unsigned char* key_data
,
254 unsigned key_data_size
,
255 const blink::WebCryptoAlgorithm
& algorithm_or_null
,
257 blink::WebCryptoKeyUsageMask usage_mask
,
258 blink::WebCryptoResult result
) {
259 blink::WebCryptoKey key
= blink::WebCryptoKey::createNull();
260 if (format
== blink::WebCryptoKeyFormatJwk
) {
261 if (!ImportKeyJwk(key_data
,
267 result
.completeWithError();
271 if (!ImportKeyInternal(format
,
278 result
.completeWithError();
282 DCHECK(key
.handle());
283 DCHECK(!key
.algorithm().isNull());
284 DCHECK_EQ(extractable
, key
.extractable());
285 result
.completeWithKey(key
);
288 void WebCryptoImpl::exportKey(
289 blink::WebCryptoKeyFormat format
,
290 const blink::WebCryptoKey
& key
,
291 blink::WebCryptoResult result
) {
292 blink::WebArrayBuffer buffer
;
293 if (!ExportKeyInternal(format
, key
, &buffer
)) {
294 result
.completeWithError();
297 result
.completeWithBuffer(buffer
);
300 void WebCryptoImpl::sign(
301 const blink::WebCryptoAlgorithm
& algorithm
,
302 const blink::WebCryptoKey
& key
,
303 const unsigned char* data
,
305 blink::WebCryptoResult result
) {
306 DCHECK(!algorithm
.isNull());
307 blink::WebArrayBuffer buffer
;
308 if (!SignInternal(algorithm
, key
, data
, data_size
, &buffer
)) {
309 result
.completeWithError();
311 result
.completeWithBuffer(buffer
);
315 void WebCryptoImpl::verifySignature(
316 const blink::WebCryptoAlgorithm
& algorithm
,
317 const blink::WebCryptoKey
& key
,
318 const unsigned char* signature
,
319 unsigned signature_size
,
320 const unsigned char* data
,
322 blink::WebCryptoResult result
) {
323 DCHECK(!algorithm
.isNull());
324 bool signature_match
= false;
325 if (!VerifySignatureInternal(algorithm
,
332 result
.completeWithError();
334 result
.completeWithBoolean(signature_match
);
338 bool WebCryptoImpl::ImportKeyJwk(
339 const unsigned char* key_data
,
340 unsigned key_data_size
,
341 const blink::WebCryptoAlgorithm
& algorithm_or_null
,
343 blink::WebCryptoKeyUsageMask usage_mask
,
344 blink::WebCryptoKey
* key
) {
346 // The goal of this method is to extract key material and meta data from the
347 // incoming JWK, combine them with the input parameters, and ultimately import
350 // JSON Web Key Format (JWK)
351 // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-16
352 // TODO(padolph): Not all possible values are handled by this code right now
354 // A JWK is a simple JSON dictionary with the following entries
355 // - "kty" (Key Type) Parameter, REQUIRED
356 // - <kty-specific parameters, see below>, REQUIRED
357 // - "use" (Key Use) Parameter, OPTIONAL
358 // - "alg" (Algorithm) Parameter, OPTIONAL
359 // - "extractable" (Key Exportability), OPTIONAL [NOTE: not yet part of JOSE,
360 // see https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796]
361 // (all other entries are ignored)
363 // OPTIONAL here means that this code does not require the entry to be present
364 // in the incoming JWK, because the method input parameters contain similar
365 // information. If the optional JWK entry is present, it will be validated
366 // against the corresponding input parameter for consistency and combined with
367 // it according to rules defined below. A special case is that the method
368 // input parameter 'algorithm' is also optional. If it is null, the JWK 'alg'
369 // value (if present) is used as a fallback.
371 // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK
372 // values are parsed out and combined with the method input parameters to
373 // build a Web Crypto Key:
374 // Web Crypto Key type <-- (deduced)
375 // Web Crypto Key extractable <-- JWK extractable + input extractable
376 // Web Crypto Key algorithm <-- JWK alg + input algorithm
377 // Web Crypto Key keyUsage <-- JWK use + input usage_mask
378 // Web Crypto Key keying material <-- kty-specific parameters
380 // Values for each JWK entry are case-sensitive and defined in
381 // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16.
382 // Note that not all values specified by JOSE are handled by this code. Only
383 // handled values are listed.
385 // +-------+--------------------------------------------------------------+
386 // | "RSA" | RSA [RFC3447] |
387 // | "oct" | Octet sequence (used to represent symmetric keys) |
388 // +-------+--------------------------------------------------------------+
390 // +-------+--------------------------------------------------------------+
391 // | "enc" | encrypt and decrypt operations |
392 // | "sig" | sign and verify (MAC) operations |
393 // | "wrap"| key wrap and unwrap [not yet part of JOSE] |
394 // +-------+--------------------------------------------------------------+
395 // - extractable (Key Exportability)
396 // +-------+--------------------------------------------------------------+
397 // | true | Key may be exported from the trusted environment |
398 // | false | Key cannot exit the trusted environment |
399 // +-------+--------------------------------------------------------------+
401 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16
402 // +--------------+-------------------------------------------------------+
403 // | Digital Signature or MAC Algorithm |
404 // +--------------+-------------------------------------------------------+
405 // | "HS256" | HMAC using SHA-256 hash algorithm |
406 // | "HS384" | HMAC using SHA-384 hash algorithm |
407 // | "HS512" | HMAC using SHA-512 hash algorithm |
408 // | "RS256" | RSASSA using SHA-256 hash algorithm |
409 // | "RS384" | RSASSA using SHA-384 hash algorithm |
410 // | "RS512" | RSASSA using SHA-512 hash algorithm |
411 // +--------------+-------------------------------------------------------|
412 // | Key Management Algorithm |
413 // +--------------+-------------------------------------------------------+
414 // | "RSA1_5" | RSAES-PKCS1-V1_5 [RFC3447] |
415 // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding |
416 // | | (OAEP) [RFC3447], with the default parameters |
417 // | | specified by RFC3447 in Section A.2.1 |
418 // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm |
419 // | | [RFC3394] using 128 bit keys |
420 // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys |
421 // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using |
422 // | | 128 bit keys |
423 // | "A256GCM" | AES GCM using 256 bit keys |
424 // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 |
425 // | | padding [NIST.800-38A] [not yet part of JOSE, see |
426 // | | https://www.w3.org/Bugs/Public/show_bug.cgi?id=23796 |
427 // | "A192CBC" | AES CBC using 192 bit keys [not yet part of JOSE] |
428 // | "A256CBC" | AES CBC using 256 bit keys [not yet part of JOSE] |
429 // +--------------+-------------------------------------------------------+
431 // kty-specific parameters
432 // The value of kty determines the type and content of the keying material
433 // carried in the JWK to be imported. Currently only two possibilities are
434 // supported: a raw key or an RSA public key. RSA private keys are not
435 // supported because typical applications seldom need to import a private key,
436 // and the large number of JWK parameters required to describe one.
437 // - kty == "oct" (symmetric or other raw key)
438 // +-------+--------------------------------------------------------------+
439 // | "k" | Contains the value of the symmetric (or other single-valued) |
440 // | | key. It is represented as the base64url encoding of the |
441 // | | octet sequence containing the key value. |
442 // +-------+--------------------------------------------------------------+
443 // - kty == "RSA" (RSA public key)
444 // +-------+--------------------------------------------------------------+
445 // | "n" | Contains the modulus value for the RSA public key. It is |
446 // | | represented as the base64url encoding of the value's |
447 // | | unsigned big endian representation as an octet sequence. |
448 // +-------+--------------------------------------------------------------+
449 // | "e" | Contains the exponent value for the RSA public key. It is |
450 // | | represented as the base64url encoding of the value's |
451 // | | unsigned big endian representation as an octet sequence. |
452 // +-------+--------------------------------------------------------------+
454 // Consistency and conflict resolution
455 // The 'algorithm_or_null', 'extractable', and 'usage_mask' input parameters
456 // may be different than the corresponding values inside the JWK. The Web
457 // Crypto spec says that if a JWK value is present but is inconsistent with
458 // the input value, it is an error and the operation must fail. If no
459 // inconsistency is found, the input and JWK values are combined as follows:
462 // If an algorithm is provided by both the input parameter and the JWK,
463 // consistency between the two is based only on algorithm ID's (including an
464 // inner hash algorithm if present). In this case if the consistency
465 // check is passed, the input algorithm is used. If only one of either the
466 // input algorithm and JWK alg is provided, it is used as the final
470 // If the JWK extractable is true but the input parameter is false, make the
471 // Web Crypto Key non-extractable. Conversely, if the JWK extractable is
472 // false but the input parameter is true, it is an inconsistency. If both
473 // are true or both are false, use that value.
476 // The input usage_mask must be a strict subset of the interpreted JWK use
477 // value, else it is judged inconsistent. In all cases the input usage_mask
478 // is used as the final usage_mask.
485 // Parse the incoming JWK JSON.
486 base::StringPiece
json_string(reinterpret_cast<const char*>(key_data
),
488 scoped_ptr
<base::Value
> value(base::JSONReader::Read(json_string
));
489 // Note, bare pointer dict_value is ok since it points into scoped value.
490 base::DictionaryValue
* dict_value
= NULL
;
491 if (!value
.get() || !value
->GetAsDictionary(&dict_value
) || !dict_value
)
494 // JWK "kty". Exit early if this required JWK parameter is missing.
495 std::string jwk_kty_value
;
496 if (!dict_value
->GetString("kty", &jwk_kty_value
))
499 // JWK "extractable" (optional) --> extractable parameter
501 bool jwk_extractable_value
;
502 if (dict_value
->GetBoolean("extractable", &jwk_extractable_value
)) {
503 if (!jwk_extractable_value
&& extractable
)
505 extractable
= extractable
&& jwk_extractable_value
;
509 // JWK "alg" (optional) --> algorithm parameter
510 // Note: input algorithm is also optional, so we have six cases to handle.
511 // 1. JWK alg present but unrecognized: error
512 // 2. JWK alg valid AND input algorithm isNull: use JWK value
513 // 3. JWK alg valid AND input algorithm specified, but JWK value
514 // inconsistent with input: error
515 // 4. JWK alg valid AND input algorithm specified, both consistent: use
516 // input value (because it has potentially more details)
517 // 5. JWK alg missing AND input algorithm isNull: error
518 // 6. JWK alg missing AND input algorithm specified: use input value
519 blink::WebCryptoAlgorithm algorithm
= blink::WebCryptoAlgorithm::createNull();
520 std::string jwk_alg_value
;
521 if (dict_value
->GetString("alg", &jwk_alg_value
)) {
524 // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can
525 // only be from the RSA family.
527 const blink::WebCryptoAlgorithm jwk_algorithm
=
528 jwk_alg_factory
.Get().CreateAlgorithmFromName(jwk_alg_value
);
529 if (jwk_algorithm
.isNull()) {
530 // JWK alg unrecognized
531 return false; // case 1
534 if (algorithm_or_null
.isNull()) {
535 // input algorithm not specified
536 algorithm
= jwk_algorithm
; // case 2
538 // input algorithm specified
539 if (!WebCryptoAlgorithmsConsistent(jwk_algorithm
, algorithm_or_null
))
540 return false; // case 3
541 algorithm
= algorithm_or_null
; // case 4
545 if (algorithm_or_null
.isNull())
546 return false; // case 5
547 algorithm
= algorithm_or_null
; // case 6
549 DCHECK(!algorithm
.isNull());
551 // JWK "use" (optional) --> usage_mask parameter
552 std::string jwk_use_value
;
553 if (dict_value
->GetString("use", &jwk_use_value
)) {
554 blink::WebCryptoKeyUsageMask jwk_usage_mask
= 0;
555 if (jwk_use_value
== "enc") {
557 blink::WebCryptoKeyUsageEncrypt
| blink::WebCryptoKeyUsageDecrypt
;
558 } else if (jwk_use_value
== "sig") {
560 blink::WebCryptoKeyUsageSign
| blink::WebCryptoKeyUsageVerify
;
561 } else if (jwk_use_value
== "wrap") {
563 blink::WebCryptoKeyUsageWrapKey
| blink::WebCryptoKeyUsageUnwrapKey
;
567 if ((jwk_usage_mask
& usage_mask
) != usage_mask
) {
568 // A usage_mask must be a subset of jwk_usage_mask.
573 // JWK keying material --> ImportKeyInternal()
574 if (jwk_kty_value
== "oct") {
576 std::string jwk_k_value
;
577 if (!GetDecodedUrl64ValueByKey(*dict_value
, "k", &jwk_k_value
))
580 // TODO(padolph): Some JWK alg ID's embed information about the key length
581 // in the alg ID string. For example "A128" implies the JWK carries 128 bits
582 // of key material, and "HS512" implies the JWK carries _at least_ 512 bits
583 // of key material. For such keys validate the actual key length against the
586 return ImportKeyInternal(blink::WebCryptoKeyFormatRaw
,
587 reinterpret_cast<const uint8
*>(jwk_k_value
.data()),
593 } else if (jwk_kty_value
== "RSA") {
595 // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry
596 // in the JWK, while an RSA private key must have those, plus at least a "d"
597 // (private exponent) entry.
598 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18,
601 // RSA private key import is not currently supported, so fail here if a "d"
603 // TODO(padolph): Support RSA private key import.
604 if (dict_value
->HasKey("d"))
607 std::string jwk_n_value
;
608 if (!GetDecodedUrl64ValueByKey(*dict_value
, "n", &jwk_n_value
))
610 std::string jwk_e_value
;
611 if (!GetDecodedUrl64ValueByKey(*dict_value
, "e", &jwk_e_value
))
614 return ImportRsaPublicKeyInternal(
615 reinterpret_cast<const uint8
*>(jwk_n_value
.data()),
617 reinterpret_cast<const uint8
*>(jwk_e_value
.data()),
631 } // namespace content