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.
5 #include "content/child/webcrypto/nss/rsa_hashed_algorithm_nss.h"
9 #include "base/logging.h"
10 #include "content/child/webcrypto/crypto_data.h"
11 #include "content/child/webcrypto/generate_key_result.h"
12 #include "content/child/webcrypto/jwk.h"
13 #include "content/child/webcrypto/nss/key_nss.h"
14 #include "content/child/webcrypto/nss/util_nss.h"
15 #include "content/child/webcrypto/status.h"
16 #include "content/child/webcrypto/webcrypto_util.h"
17 #include "crypto/scoped_nss_types.h"
18 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
19 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
27 #if defined(USE_NSS) && !defined(OS_CHROMEOS)
28 Status
ErrorRsaPrivateKeyImportNotSupported() {
29 return Status::ErrorUnsupported(
30 "NSS version must be at least 3.16.2 for RSA private key import. See "
31 "http://crbug.com/380424");
34 // Prior to NSS 3.16.2 RSA key parameters were not validated. This is
35 // a security problem for RSA private key import from JWK which uses a
36 // CKA_ID based on the public modulus to retrieve the private key.
37 Status
NssSupportsRsaPrivateKeyImport() {
38 if (!NSS_VersionCheck("3.16.2"))
39 return ErrorRsaPrivateKeyImportNotSupported();
41 // Also ensure that the version of Softoken is 3.16.2 or later.
42 crypto::ScopedPK11Slot
slot(PK11_GetInternalSlot());
43 CK_SLOT_INFO info
= {};
44 if (PK11_GetSlotInfo(slot
.get(), &info
) != SECSuccess
)
45 return ErrorRsaPrivateKeyImportNotSupported();
47 // CK_SLOT_INFO.hardwareVersion contains the major.minor
48 // version info for Softoken in the corresponding .major/.minor
49 // fields, and .firmwareVersion contains the patch.build
50 // version info (in the .major/.minor fields)
51 if ((info
.hardwareVersion
.major
> 3) ||
52 (info
.hardwareVersion
.major
== 3 &&
53 (info
.hardwareVersion
.minor
> 16 ||
54 (info
.hardwareVersion
.minor
== 16 &&
55 info
.firmwareVersion
.major
>= 2)))) {
56 return Status::Success();
59 return ErrorRsaPrivateKeyImportNotSupported();
62 Status
NssSupportsRsaPrivateKeyImport() {
63 return Status::Success();
67 bool CreateRsaHashedPublicKeyAlgorithm(
68 blink::WebCryptoAlgorithmId rsa_algorithm
,
69 blink::WebCryptoAlgorithmId hash_algorithm
,
71 blink::WebCryptoKeyAlgorithm
* key_algorithm
) {
72 // TODO(eroman): What about other key types rsaPss, rsaOaep.
73 if (!key
|| key
->keyType
!= rsaKey
)
76 unsigned int modulus_length_bits
= SECKEY_PublicKeyStrength(key
) * 8;
77 CryptoData
public_exponent(key
->u
.rsa
.publicExponent
.data
,
78 key
->u
.rsa
.publicExponent
.len
);
80 *key_algorithm
= blink::WebCryptoKeyAlgorithm::createRsaHashed(
81 rsa_algorithm
, modulus_length_bits
, public_exponent
.bytes(),
82 public_exponent
.byte_length(), hash_algorithm
);
86 bool CreateRsaHashedPrivateKeyAlgorithm(
87 blink::WebCryptoAlgorithmId rsa_algorithm
,
88 blink::WebCryptoAlgorithmId hash_algorithm
,
89 SECKEYPrivateKey
* key
,
90 blink::WebCryptoKeyAlgorithm
* key_algorithm
) {
91 crypto::ScopedSECKEYPublicKey
public_key(SECKEY_ConvertToPublicKey(key
));
94 return CreateRsaHashedPublicKeyAlgorithm(rsa_algorithm
, hash_algorithm
,
95 public_key
.get(), key_algorithm
);
98 // From PKCS#1 [http://tools.ietf.org/html/rfc3447]:
100 // RSAPrivateKey ::= SEQUENCE {
102 // modulus INTEGER, -- n
103 // publicExponent INTEGER, -- e
104 // privateExponent INTEGER, -- d
105 // prime1 INTEGER, -- p
106 // prime2 INTEGER, -- q
107 // exponent1 INTEGER, -- d mod (p-1)
108 // exponent2 INTEGER, -- d mod (q-1)
109 // coefficient INTEGER, -- (inverse of q) mod p
110 // otherPrimeInfos OtherPrimeInfos OPTIONAL
113 // Note that otherPrimeInfos is only applicable for version=1. Since NSS
114 // doesn't use multi-prime can safely use version=0.
115 struct RSAPrivateKey
{
118 SECItem public_exponent
;
119 SECItem private_exponent
;
127 // The system NSS library doesn't have the new PK11_ExportDERPrivateKeyInfo
128 // function yet (https://bugzilla.mozilla.org/show_bug.cgi?id=519255). So we
129 // provide a fallback implementation.
131 const SEC_ASN1Template RSAPrivateKeyTemplate
[] = {
132 {SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(RSAPrivateKey
)},
133 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, version
)},
134 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, modulus
)},
135 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, public_exponent
)},
136 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, private_exponent
)},
137 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, prime1
)},
138 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, prime2
)},
139 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, exponent1
)},
140 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, exponent2
)},
141 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, coefficient
)},
143 #endif // defined(USE_NSS)
145 // On success |value| will be filled with data which must be freed by
146 // SECITEM_FreeItem(value, PR_FALSE);
147 bool ReadUint(SECKEYPrivateKey
* key
,
148 CK_ATTRIBUTE_TYPE attribute
,
150 SECStatus rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, key
, attribute
, value
);
152 // PK11_ReadRawAttribute() returns items of type siBuffer. However in order
153 // for the ASN.1 encoding to be correct, the items must be of type
154 // siUnsignedInteger.
155 value
->type
= siUnsignedInteger
;
157 return rv
== SECSuccess
;
160 // Fills |out| with the RSA private key properties. Returns true on success.
161 // Regardless of the return value, the caller must invoke FreeRSAPrivateKey()
162 // to free up any allocated memory.
164 // The passed in RSAPrivateKey must be zero-initialized.
165 bool InitRSAPrivateKey(SECKEYPrivateKey
* key
, RSAPrivateKey
* out
) {
166 if (key
->keyType
!= rsaKey
)
169 // Everything should be zero-ed out. These are just some spot checks.
170 DCHECK(!out
->version
.data
);
171 DCHECK(!out
->version
.len
);
172 DCHECK(!out
->modulus
.data
);
173 DCHECK(!out
->modulus
.len
);
175 // Always use version=0 since not using multi-prime.
176 if (!SEC_ASN1EncodeInteger(NULL
, &out
->version
, 0))
179 if (!ReadUint(key
, CKA_MODULUS
, &out
->modulus
))
181 if (!ReadUint(key
, CKA_PUBLIC_EXPONENT
, &out
->public_exponent
))
183 if (!ReadUint(key
, CKA_PRIVATE_EXPONENT
, &out
->private_exponent
))
185 if (!ReadUint(key
, CKA_PRIME_1
, &out
->prime1
))
187 if (!ReadUint(key
, CKA_PRIME_2
, &out
->prime2
))
189 if (!ReadUint(key
, CKA_EXPONENT_1
, &out
->exponent1
))
191 if (!ReadUint(key
, CKA_EXPONENT_2
, &out
->exponent2
))
193 if (!ReadUint(key
, CKA_COEFFICIENT
, &out
->coefficient
))
199 struct FreeRsaPrivateKey
{
200 void operator()(RSAPrivateKey
* out
) {
201 SECITEM_FreeItem(&out
->version
, PR_FALSE
);
202 SECITEM_FreeItem(&out
->modulus
, PR_FALSE
);
203 SECITEM_FreeItem(&out
->public_exponent
, PR_FALSE
);
204 SECITEM_FreeItem(&out
->private_exponent
, PR_FALSE
);
205 SECITEM_FreeItem(&out
->prime1
, PR_FALSE
);
206 SECITEM_FreeItem(&out
->prime2
, PR_FALSE
);
207 SECITEM_FreeItem(&out
->exponent1
, PR_FALSE
);
208 SECITEM_FreeItem(&out
->exponent2
, PR_FALSE
);
209 SECITEM_FreeItem(&out
->coefficient
, PR_FALSE
);
213 typedef scoped_ptr
<CERTSubjectPublicKeyInfo
,
214 crypto::NSSDestroyer
<CERTSubjectPublicKeyInfo
,
215 SECKEY_DestroySubjectPublicKeyInfo
>>
216 ScopedCERTSubjectPublicKeyInfo
;
218 struct DestroyGenericObject
{
219 void operator()(PK11GenericObject
* o
) const {
221 PK11_DestroyGenericObject(o
);
225 typedef scoped_ptr
<PK11GenericObject
, DestroyGenericObject
>
226 ScopedPK11GenericObject
;
228 // Helper to add an attribute to a template.
229 void AddAttribute(CK_ATTRIBUTE_TYPE type
,
231 unsigned long length
,
232 std::vector
<CK_ATTRIBUTE
>* templ
) {
233 CK_ATTRIBUTE attribute
= {type
, value
, length
};
234 templ
->push_back(attribute
);
237 void AddAttribute(CK_ATTRIBUTE_TYPE type
,
238 const CryptoData
& data
,
239 std::vector
<CK_ATTRIBUTE
>* templ
) {
240 CK_ATTRIBUTE attribute
= {
241 type
, const_cast<unsigned char*>(data
.bytes()), data
.byte_length()};
242 templ
->push_back(attribute
);
245 void AddAttribute(CK_ATTRIBUTE_TYPE type
,
246 const std::string
& data
,
247 std::vector
<CK_ATTRIBUTE
>* templ
) {
248 AddAttribute(type
, CryptoData(data
), templ
);
251 Status
ExportKeyPkcs8Nss(SECKEYPrivateKey
* key
, std::vector
<uint8_t>* buffer
) {
252 if (key
->keyType
!= rsaKey
)
253 return Status::ErrorUnsupported();
255 // TODO(rsleevi): Implement OAEP support according to the spec.
258 // PK11_ExportDERPrivateKeyInfo isn't available. Use our fallback code.
259 const SECOidTag algorithm
= SEC_OID_PKCS1_RSA_ENCRYPTION
;
260 const int kPrivateKeyInfoVersion
= 0;
262 SECKEYPrivateKeyInfo private_key_info
= {};
263 RSAPrivateKey rsa_private_key
= {};
264 scoped_ptr
<RSAPrivateKey
, FreeRsaPrivateKey
> free_private_key(
267 // http://crbug.com/366427: the spec does not define any other failures for
268 // exporting, so none of the subsequent errors are spec compliant.
269 if (!InitRSAPrivateKey(key
, &rsa_private_key
))
270 return Status::OperationError();
272 crypto::ScopedPLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
274 return Status::OperationError();
276 if (!SEC_ASN1EncodeItem(arena
.get(), &private_key_info
.privateKey
,
277 &rsa_private_key
, RSAPrivateKeyTemplate
)) {
278 return Status::OperationError();
281 if (SECSuccess
!= SECOID_SetAlgorithmID(arena
.get(),
282 &private_key_info
.algorithm
,
284 return Status::OperationError();
287 if (!SEC_ASN1EncodeInteger(arena
.get(), &private_key_info
.version
,
288 kPrivateKeyInfoVersion
)) {
289 return Status::OperationError();
292 crypto::ScopedSECItem
encoded_key(
293 SEC_ASN1EncodeItem(NULL
, NULL
, &private_key_info
,
294 SEC_ASN1_GET(SECKEY_PrivateKeyInfoTemplate
)));
295 #else // defined(USE_NSS)
296 crypto::ScopedSECItem
encoded_key(PK11_ExportDERPrivateKeyInfo(key
, NULL
));
297 #endif // defined(USE_NSS)
299 if (!encoded_key
.get())
300 return Status::OperationError();
302 buffer
->assign(encoded_key
->data
, encoded_key
->data
+ encoded_key
->len
);
303 return Status::Success();
306 Status
ImportRsaPrivateKey(const blink::WebCryptoAlgorithm
& algorithm
,
308 blink::WebCryptoKeyUsageMask usages
,
309 const JwkRsaInfo
& params
,
310 blink::WebCryptoKey
* key
) {
311 Status status
= NssSupportsRsaPrivateKeyImport();
312 if (status
.IsError())
315 CK_OBJECT_CLASS obj_class
= CKO_PRIVATE_KEY
;
316 CK_KEY_TYPE key_type
= CKK_RSA
;
317 CK_BBOOL ck_false
= CK_FALSE
;
319 std::vector
<CK_ATTRIBUTE
> key_template
;
321 AddAttribute(CKA_CLASS
, &obj_class
, sizeof(obj_class
), &key_template
);
322 AddAttribute(CKA_KEY_TYPE
, &key_type
, sizeof(key_type
), &key_template
);
323 AddAttribute(CKA_TOKEN
, &ck_false
, sizeof(ck_false
), &key_template
);
324 AddAttribute(CKA_SENSITIVE
, &ck_false
, sizeof(ck_false
), &key_template
);
325 AddAttribute(CKA_PRIVATE
, &ck_false
, sizeof(ck_false
), &key_template
);
327 // Required properties by JWA.
328 AddAttribute(CKA_MODULUS
, params
.n
, &key_template
);
329 AddAttribute(CKA_PUBLIC_EXPONENT
, params
.e
, &key_template
);
330 AddAttribute(CKA_PRIVATE_EXPONENT
, params
.d
, &key_template
);
332 // Manufacture a CKA_ID so the created key can be retrieved later as a
333 // SECKEYPrivateKey using FindKeyByKeyID(). Unfortunately there isn't a more
334 // direct way to do this in NSS.
336 // For consistency with other NSS key creation methods, set the CKA_ID to
337 // PK11_MakeIDFromPubKey(). There are some problems with
340 // (1) Prior to NSS 3.16.2, there is no parameter validation when creating
341 // private keys. It is therefore possible to construct a key using the
342 // known public modulus, and where all the other parameters are bogus.
343 // FindKeyByKeyID() returns the first key matching the ID. So this would
344 // effectively allow an attacker to retrieve a private key of their
347 // (2) The ID space is shared by different key types. So theoretically
348 // possible to retrieve a key of the wrong type which has a matching
349 // CKA_ID. In practice I am told this is not likely except for small key
350 // sizes, since would require constructing keys with the same public
353 // (3) FindKeyByKeyID() doesn't necessarily return the object that was just
354 // created by CreateGenericObject. If the pre-existing key was
355 // provisioned with flags incompatible with WebCrypto (for instance
356 // marked sensitive) then this will break things.
357 SECItem modulus_item
= MakeSECItemForBuffer(CryptoData(params
.n
));
358 crypto::ScopedSECItem
object_id(PK11_MakeIDFromPubKey(&modulus_item
));
359 AddAttribute(CKA_ID
, CryptoData(object_id
->data
, object_id
->len
),
362 // Optional properties by JWA, however guaranteed to be present by Chromium's
364 AddAttribute(CKA_PRIME_1
, params
.p
, &key_template
);
365 AddAttribute(CKA_PRIME_2
, params
.q
, &key_template
);
366 AddAttribute(CKA_EXPONENT_1
, params
.dp
, &key_template
);
367 AddAttribute(CKA_EXPONENT_2
, params
.dq
, &key_template
);
368 AddAttribute(CKA_COEFFICIENT
, params
.qi
, &key_template
);
370 crypto::ScopedPK11Slot
slot(PK11_GetInternalSlot());
372 ScopedPK11GenericObject
key_object(PK11_CreateGenericObject(
373 slot
.get(), &key_template
[0], key_template
.size(), PR_FALSE
));
376 return Status::OperationError();
378 crypto::ScopedSECKEYPrivateKey
private_key_tmp(
379 PK11_FindKeyByKeyID(slot
.get(), object_id
.get(), NULL
));
381 // PK11_FindKeyByKeyID() may return a handle to an existing key, rather than
382 // the object created by PK11_CreateGenericObject().
383 crypto::ScopedSECKEYPrivateKey
private_key(
384 SECKEY_CopyPrivateKey(private_key_tmp
.get()));
387 return Status::OperationError();
389 blink::WebCryptoKeyAlgorithm key_algorithm
;
390 if (!CreateRsaHashedPrivateKeyAlgorithm(
391 algorithm
.id(), algorithm
.rsaHashedImportParams()->hash().id(),
392 private_key
.get(), &key_algorithm
)) {
393 return Status::ErrorUnexpected();
396 std::vector
<uint8_t> pkcs8_data
;
397 status
= ExportKeyPkcs8Nss(private_key
.get(), &pkcs8_data
);
398 if (status
.IsError())
401 scoped_ptr
<PrivateKeyNss
> key_handle(
402 new PrivateKeyNss(private_key
.Pass(), CryptoData(pkcs8_data
)));
404 *key
= blink::WebCryptoKey::create(key_handle
.release(),
405 blink::WebCryptoKeyTypePrivate
,
406 extractable
, key_algorithm
, usages
);
407 return Status::Success();
410 Status
ExportKeySpkiNss(SECKEYPublicKey
* key
, std::vector
<uint8_t>* buffer
) {
411 const crypto::ScopedSECItem
spki_der(
412 SECKEY_EncodeDERSubjectPublicKeyInfo(key
));
414 return Status::OperationError();
416 buffer
->assign(spki_der
->data
, spki_der
->data
+ spki_der
->len
);
417 return Status::Success();
420 Status
ImportRsaPublicKey(const blink::WebCryptoAlgorithm
& algorithm
,
422 blink::WebCryptoKeyUsageMask usages
,
423 const CryptoData
& modulus_data
,
424 const CryptoData
& exponent_data
,
425 blink::WebCryptoKey
* key
) {
426 if (!modulus_data
.byte_length())
427 return Status::ErrorImportRsaEmptyModulus();
429 if (!exponent_data
.byte_length())
430 return Status::ErrorImportRsaEmptyExponent();
432 DCHECK(modulus_data
.bytes());
433 DCHECK(exponent_data
.bytes());
435 // NSS does not provide a way to create an RSA public key directly from the
436 // modulus and exponent values, but it can import an DER-encoded ASN.1 blob
437 // with these values and create the public key from that. The code below
438 // follows the recommendation described in
439 // https://developer.mozilla.org/en-US/docs/NSS/NSS_Tech_Notes/nss_tech_note7
441 // Pack the input values into a struct compatible with NSS ASN.1 encoding, and
442 // set up an ASN.1 encoder template for it.
443 struct RsaPublicKeyData
{
447 const RsaPublicKeyData pubkey_in
= {
449 const_cast<unsigned char*>(modulus_data
.bytes()),
450 modulus_data
.byte_length()},
452 const_cast<unsigned char*>(exponent_data
.bytes()),
453 exponent_data
.byte_length()}};
454 const SEC_ASN1Template rsa_public_key_template
[] = {
455 {SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(RsaPublicKeyData
)},
457 SEC_ASN1_INTEGER
, offsetof(RsaPublicKeyData
, modulus
),
460 SEC_ASN1_INTEGER
, offsetof(RsaPublicKeyData
, exponent
),
465 // DER-encode the public key.
466 crypto::ScopedSECItem
pubkey_der(
467 SEC_ASN1EncodeItem(NULL
, NULL
, &pubkey_in
, rsa_public_key_template
));
469 return Status::OperationError();
471 // Import the DER-encoded public key to create an RSA SECKEYPublicKey.
472 crypto::ScopedSECKEYPublicKey
pubkey(
473 SECKEY_ImportDERPublicKey(pubkey_der
.get(), CKK_RSA
));
475 return Status::OperationError();
477 blink::WebCryptoKeyAlgorithm key_algorithm
;
478 if (!CreateRsaHashedPublicKeyAlgorithm(
479 algorithm
.id(), algorithm
.rsaHashedImportParams()->hash().id(),
480 pubkey
.get(), &key_algorithm
)) {
481 return Status::ErrorUnexpected();
484 std::vector
<uint8_t> spki_data
;
485 Status status
= ExportKeySpkiNss(pubkey
.get(), &spki_data
);
486 if (status
.IsError())
489 scoped_ptr
<PublicKeyNss
> key_handle(
490 new PublicKeyNss(pubkey
.Pass(), CryptoData(spki_data
)));
492 *key
= blink::WebCryptoKey::create(key_handle
.release(),
493 blink::WebCryptoKeyTypePublic
, extractable
,
494 key_algorithm
, usages
);
495 return Status::Success();
500 Status
RsaHashedAlgorithm::GenerateKey(
501 const blink::WebCryptoAlgorithm
& algorithm
,
503 blink::WebCryptoKeyUsageMask combined_usages
,
504 GenerateKeyResult
* result
) const {
505 blink::WebCryptoKeyUsageMask public_usages
= 0;
506 blink::WebCryptoKeyUsageMask private_usages
= 0;
508 Status status
= GetUsagesForGenerateAsymmetricKey(
509 combined_usages
, all_public_key_usages_
, all_private_key_usages_
,
510 &public_usages
, &private_usages
);
511 if (status
.IsError())
514 unsigned int public_exponent
= 0;
515 unsigned int modulus_length_bits
= 0;
516 status
= GetRsaKeyGenParameters(algorithm
.rsaHashedKeyGenParams(),
517 &public_exponent
, &modulus_length_bits
);
518 if (status
.IsError())
521 crypto::ScopedPK11Slot
slot(PK11_GetInternalKeySlot());
523 return Status::OperationError();
525 PK11RSAGenParams rsa_gen_params
;
526 rsa_gen_params
.keySizeInBits
= modulus_length_bits
;
527 rsa_gen_params
.pe
= public_exponent
;
529 // The usages are enforced at the WebCrypto layer, so it isn't necessary to
530 // create keys with limited usages.
531 const CK_FLAGS operation_flags_mask
= kAllOperationFlags
;
533 // The private key must be marked as insensitive and extractable, otherwise it
534 // cannot later be exported in unencrypted form or structured-cloned.
535 const PK11AttrFlags attribute_flags
=
536 PK11_ATTR_INSENSITIVE
| PK11_ATTR_EXTRACTABLE
;
538 // Note: NSS does not generate an sec_public_key if the call below fails,
539 // so there is no danger of a leaked sec_public_key.
540 SECKEYPublicKey
* sec_public_key
;
541 crypto::ScopedSECKEYPrivateKey
scoped_sec_private_key(
542 PK11_GenerateKeyPairWithOpFlags(slot
.get(), CKM_RSA_PKCS_KEY_PAIR_GEN
,
543 &rsa_gen_params
, &sec_public_key
,
544 attribute_flags
, generate_flags_
,
545 operation_flags_mask
, NULL
));
546 if (!scoped_sec_private_key
)
547 return Status::OperationError();
549 blink::WebCryptoKeyAlgorithm key_algorithm
;
550 if (!CreateRsaHashedPublicKeyAlgorithm(
551 algorithm
.id(), algorithm
.rsaHashedKeyGenParams()->hash().id(),
552 sec_public_key
, &key_algorithm
)) {
553 return Status::ErrorUnexpected();
556 std::vector
<uint8_t> spki_data
;
557 status
= ExportKeySpkiNss(sec_public_key
, &spki_data
);
558 if (status
.IsError())
561 scoped_ptr
<PublicKeyNss
> public_key_handle(new PublicKeyNss(
562 crypto::ScopedSECKEYPublicKey(sec_public_key
), CryptoData(spki_data
)));
564 std::vector
<uint8_t> pkcs8_data
;
565 status
= ExportKeyPkcs8Nss(scoped_sec_private_key
.get(), &pkcs8_data
);
566 if (status
.IsError())
569 scoped_ptr
<PrivateKeyNss
> private_key_handle(
570 new PrivateKeyNss(scoped_sec_private_key
.Pass(), CryptoData(pkcs8_data
)));
572 blink::WebCryptoKey public_key
= blink::WebCryptoKey::create(
573 public_key_handle
.release(), blink::WebCryptoKeyTypePublic
, true,
574 key_algorithm
, public_usages
);
576 blink::WebCryptoKey private_key
= blink::WebCryptoKey::create(
577 private_key_handle
.release(), blink::WebCryptoKeyTypePrivate
, extractable
,
578 key_algorithm
, private_usages
);
580 result
->AssignKeyPair(public_key
, private_key
);
581 return Status::Success();
584 Status
RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(
585 blink::WebCryptoKeyFormat format
,
586 blink::WebCryptoKeyUsageMask usages
) const {
587 return VerifyUsagesBeforeImportAsymmetricKey(format
, all_public_key_usages_
,
588 all_private_key_usages_
, usages
);
591 Status
RsaHashedAlgorithm::ImportKeyPkcs8(
592 const CryptoData
& key_data
,
593 const blink::WebCryptoAlgorithm
& algorithm
,
595 blink::WebCryptoKeyUsageMask usages
,
596 blink::WebCryptoKey
* key
) const {
597 Status status
= NssSupportsRsaPrivateKeyImport();
598 if (status
.IsError())
601 crypto::ScopedPLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
603 return Status::OperationError();
605 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 PKCS#8
606 // private key info object. Excess data is illegal, but NSS silently accepts
607 // it, so first ensure that 'key_data' consists of a single ASN.1 element.
608 SECItem key_item
= MakeSECItemForBuffer(key_data
);
610 if (SEC_QuickDERDecodeItem(arena
.get(), &pki_der
,
611 SEC_ASN1_GET(SEC_AnyTemplate
),
612 &key_item
) != SECSuccess
) {
613 return Status::DataError();
616 SECKEYPrivateKey
* seckey_private_key
= NULL
;
617 crypto::ScopedPK11Slot
slot(PK11_GetInternalSlot());
618 if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot
.get(), &pki_der
,
625 NULL
) != SECSuccess
) {
626 return Status::DataError();
628 DCHECK(seckey_private_key
);
629 crypto::ScopedSECKEYPrivateKey
private_key(seckey_private_key
);
631 const KeyType sec_key_type
= SECKEY_GetPrivateKeyType(private_key
.get());
632 if (sec_key_type
!= rsaKey
)
633 return Status::DataError();
635 blink::WebCryptoKeyAlgorithm key_algorithm
;
636 if (!CreateRsaHashedPrivateKeyAlgorithm(
637 algorithm
.id(), algorithm
.rsaHashedImportParams()->hash().id(),
638 private_key
.get(), &key_algorithm
)) {
639 return Status::ErrorUnexpected();
642 // TODO(eroman): This is probably going to be the same as the input.
643 std::vector
<uint8_t> pkcs8_data
;
644 status
= ExportKeyPkcs8Nss(private_key
.get(), &pkcs8_data
);
645 if (status
.IsError())
648 scoped_ptr
<PrivateKeyNss
> key_handle(
649 new PrivateKeyNss(private_key
.Pass(), CryptoData(pkcs8_data
)));
651 *key
= blink::WebCryptoKey::create(key_handle
.release(),
652 blink::WebCryptoKeyTypePrivate
,
653 extractable
, key_algorithm
, usages
);
655 return Status::Success();
658 Status
RsaHashedAlgorithm::ImportKeySpki(
659 const CryptoData
& key_data
,
660 const blink::WebCryptoAlgorithm
& algorithm
,
662 blink::WebCryptoKeyUsageMask usages
,
663 blink::WebCryptoKey
* key
) const {
664 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject
665 // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo.
666 SECItem spki_item
= MakeSECItemForBuffer(key_data
);
667 const ScopedCERTSubjectPublicKeyInfo
spki(
668 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item
));
670 return Status::DataError();
672 crypto::ScopedSECKEYPublicKey
sec_public_key(
673 SECKEY_ExtractPublicKey(spki
.get()));
675 return Status::DataError();
677 const KeyType sec_key_type
= SECKEY_GetPublicKeyType(sec_public_key
.get());
678 if (sec_key_type
!= rsaKey
)
679 return Status::DataError();
681 blink::WebCryptoKeyAlgorithm key_algorithm
;
682 if (!CreateRsaHashedPublicKeyAlgorithm(
683 algorithm
.id(), algorithm
.rsaHashedImportParams()->hash().id(),
684 sec_public_key
.get(), &key_algorithm
)) {
685 return Status::ErrorUnexpected();
688 // TODO(eroman): This is probably going to be the same as the input.
689 std::vector
<uint8_t> spki_data
;
690 Status status
= ExportKeySpkiNss(sec_public_key
.get(), &spki_data
);
691 if (status
.IsError())
694 scoped_ptr
<PublicKeyNss
> key_handle(
695 new PublicKeyNss(sec_public_key
.Pass(), CryptoData(spki_data
)));
697 *key
= blink::WebCryptoKey::create(key_handle
.release(),
698 blink::WebCryptoKeyTypePublic
, extractable
,
699 key_algorithm
, usages
);
701 return Status::Success();
704 Status
RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey
& key
,
705 std::vector
<uint8_t>* buffer
) const {
706 if (key
.type() != blink::WebCryptoKeyTypePrivate
)
707 return Status::ErrorUnexpectedKeyType();
708 *buffer
= PrivateKeyNss::Cast(key
)->pkcs8_data();
709 return Status::Success();
712 Status
RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey
& key
,
713 std::vector
<uint8_t>* buffer
) const {
714 if (key
.type() != blink::WebCryptoKeyTypePublic
)
715 return Status::ErrorUnexpectedKeyType();
716 *buffer
= PublicKeyNss::Cast(key
)->spki_data();
717 return Status::Success();
720 Status
RsaHashedAlgorithm::ImportKeyJwk(
721 const CryptoData
& key_data
,
722 const blink::WebCryptoAlgorithm
& algorithm
,
724 blink::WebCryptoKeyUsageMask usages
,
725 blink::WebCryptoKey
* key
) const {
726 const char* jwk_algorithm
=
727 GetJwkAlgorithm(algorithm
.rsaHashedImportParams()->hash().id());
730 return Status::ErrorUnexpected();
734 ReadRsaKeyJwk(key_data
, jwk_algorithm
, extractable
, usages
, &jwk
);
735 if (status
.IsError())
738 // Once the key type is known, verify the usages.
739 status
= CheckKeyCreationUsages(
740 jwk
.is_private_key
? all_private_key_usages_
: all_public_key_usages_
,
741 usages
, !jwk
.is_private_key
);
742 if (status
.IsError())
745 return jwk
.is_private_key
746 ? ImportRsaPrivateKey(algorithm
, extractable
, usages
, jwk
, key
)
747 : ImportRsaPublicKey(algorithm
, extractable
, usages
,
748 CryptoData(jwk
.n
), CryptoData(jwk
.e
), key
);
751 Status
RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey
& key
,
752 std::vector
<uint8_t>* buffer
) const {
753 const char* jwk_algorithm
=
754 GetJwkAlgorithm(key
.algorithm().rsaHashedParams()->hash().id());
757 return Status::ErrorUnexpected();
759 switch (key
.type()) {
760 case blink::WebCryptoKeyTypePublic
: {
761 SECKEYPublicKey
* nss_key
= PublicKeyNss::Cast(key
)->key();
762 if (nss_key
->keyType
!= rsaKey
)
763 return Status::ErrorUnsupported();
765 WriteRsaPublicKeyJwk(SECItemToCryptoData(nss_key
->u
.rsa
.modulus
),
766 SECItemToCryptoData(nss_key
->u
.rsa
.publicExponent
),
767 jwk_algorithm
, key
.extractable(), key
.usages(),
770 return Status::Success();
773 case blink::WebCryptoKeyTypePrivate
: {
774 SECKEYPrivateKey
* nss_key
= PrivateKeyNss::Cast(key
)->key();
775 RSAPrivateKey key_props
= {};
776 scoped_ptr
<RSAPrivateKey
, FreeRsaPrivateKey
> free_private_key(&key_props
);
778 if (!InitRSAPrivateKey(nss_key
, &key_props
))
779 return Status::OperationError();
781 WriteRsaPrivateKeyJwk(SECItemToCryptoData(key_props
.modulus
),
782 SECItemToCryptoData(key_props
.public_exponent
),
783 SECItemToCryptoData(key_props
.private_exponent
),
784 SECItemToCryptoData(key_props
.prime1
),
785 SECItemToCryptoData(key_props
.prime2
),
786 SECItemToCryptoData(key_props
.exponent1
),
787 SECItemToCryptoData(key_props
.exponent2
),
788 SECItemToCryptoData(key_props
.coefficient
),
789 jwk_algorithm
, key
.extractable(), key
.usages(),
792 return Status::Success();
795 return Status::ErrorUnexpected();
799 Status
RsaHashedAlgorithm::SerializeKeyForClone(
800 const blink::WebCryptoKey
& key
,
801 blink::WebVector
<uint8_t>* key_data
) const {
802 key_data
->assign(static_cast<KeyNss
*>(key
.handle())->serialized_key_data());
803 return Status::Success();
806 // TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763
807 Status
RsaHashedAlgorithm::DeserializeKeyForClone(
808 const blink::WebCryptoKeyAlgorithm
& algorithm
,
809 blink::WebCryptoKeyType type
,
811 blink::WebCryptoKeyUsageMask usages
,
812 const CryptoData
& key_data
,
813 blink::WebCryptoKey
* key
) const {
814 blink::WebCryptoAlgorithm import_algorithm
= CreateRsaHashedImportAlgorithm(
815 algorithm
.id(), algorithm
.rsaHashedParams()->hash().id());
820 case blink::WebCryptoKeyTypePublic
:
822 ImportKeySpki(key_data
, import_algorithm
, extractable
, usages
, key
);
824 case blink::WebCryptoKeyTypePrivate
:
826 ImportKeyPkcs8(key_data
, import_algorithm
, extractable
, usages
, key
);
829 return Status::ErrorUnexpected();
832 // There is some duplicated information in the serialized format used by
833 // structured clone (since the KeyAlgorithm is serialized separately from the
834 // key data). Use this extra information to further validate what was
835 // deserialized from the key data.
837 if (algorithm
.id() != key
->algorithm().id())
838 return Status::ErrorUnexpected();
840 if (key
->type() != type
)
841 return Status::ErrorUnexpected();
843 if (algorithm
.rsaHashedParams()->modulusLengthBits() !=
844 key
->algorithm().rsaHashedParams()->modulusLengthBits()) {
845 return Status::ErrorUnexpected();
848 if (algorithm
.rsaHashedParams()->publicExponent().size() !=
849 key
->algorithm().rsaHashedParams()->publicExponent().size() ||
851 memcmp(algorithm
.rsaHashedParams()->publicExponent().data(),
852 key
->algorithm().rsaHashedParams()->publicExponent().data(),
853 key
->algorithm().rsaHashedParams()->publicExponent().size())) {
854 return Status::ErrorUnexpected();
857 return Status::Success();
860 } // namespace webcrypto
862 } // namespace content