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 "components/webcrypto/nss/rsa_hashed_algorithm_nss.h"
9 #include "base/logging.h"
10 #include "components/webcrypto/crypto_data.h"
11 #include "components/webcrypto/generate_key_result.h"
12 #include "components/webcrypto/jwk.h"
13 #include "components/webcrypto/nss/key_nss.h"
14 #include "components/webcrypto/nss/util_nss.h"
15 #include "components/webcrypto/status.h"
16 #include "components/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"
25 #if defined(USE_NSS_CERTS) && !defined(OS_CHROMEOS)
26 Status
ErrorRsaPrivateKeyImportNotSupported() {
27 return Status::ErrorUnsupported(
28 "NSS version must be at least 3.16.2 for RSA private key import. See "
29 "http://crbug.com/380424");
32 // Prior to NSS 3.16.2 RSA key parameters were not validated. This is
33 // a security problem for RSA private key import from JWK which uses a
34 // CKA_ID based on the public modulus to retrieve the private key.
35 Status
NssSupportsRsaPrivateKeyImport() {
36 if (!NSS_VersionCheck("3.16.2"))
37 return ErrorRsaPrivateKeyImportNotSupported();
39 // Also ensure that the version of Softoken is 3.16.2 or later.
40 crypto::ScopedPK11Slot
slot(PK11_GetInternalSlot());
41 CK_SLOT_INFO info
= {};
42 if (PK11_GetSlotInfo(slot
.get(), &info
) != SECSuccess
)
43 return ErrorRsaPrivateKeyImportNotSupported();
45 // CK_SLOT_INFO.hardwareVersion contains the major.minor
46 // version info for Softoken in the corresponding .major/.minor
47 // fields, and .firmwareVersion contains the patch.build
48 // version info (in the .major/.minor fields)
49 if ((info
.hardwareVersion
.major
> 3) ||
50 (info
.hardwareVersion
.major
== 3 &&
51 (info
.hardwareVersion
.minor
> 16 ||
52 (info
.hardwareVersion
.minor
== 16 &&
53 info
.firmwareVersion
.major
>= 2)))) {
54 return Status::Success();
57 return ErrorRsaPrivateKeyImportNotSupported();
60 Status
NssSupportsRsaPrivateKeyImport() {
61 return Status::Success();
65 bool CreateRsaHashedPublicKeyAlgorithm(
66 blink::WebCryptoAlgorithmId rsa_algorithm
,
67 blink::WebCryptoAlgorithmId hash_algorithm
,
69 blink::WebCryptoKeyAlgorithm
* key_algorithm
) {
70 // TODO(eroman): What about other key types rsaPss, rsaOaep.
71 if (!key
|| key
->keyType
!= rsaKey
)
74 unsigned int modulus_length_bits
= SECKEY_PublicKeyStrength(key
) * 8;
75 CryptoData
public_exponent(key
->u
.rsa
.publicExponent
.data
,
76 key
->u
.rsa
.publicExponent
.len
);
78 *key_algorithm
= blink::WebCryptoKeyAlgorithm::createRsaHashed(
79 rsa_algorithm
, modulus_length_bits
, public_exponent
.bytes(),
80 public_exponent
.byte_length(), hash_algorithm
);
84 bool CreateRsaHashedPrivateKeyAlgorithm(
85 blink::WebCryptoAlgorithmId rsa_algorithm
,
86 blink::WebCryptoAlgorithmId hash_algorithm
,
87 SECKEYPrivateKey
* key
,
88 blink::WebCryptoKeyAlgorithm
* key_algorithm
) {
89 crypto::ScopedSECKEYPublicKey
public_key(SECKEY_ConvertToPublicKey(key
));
92 return CreateRsaHashedPublicKeyAlgorithm(rsa_algorithm
, hash_algorithm
,
93 public_key
.get(), key_algorithm
);
96 // From PKCS#1 [http://tools.ietf.org/html/rfc3447]:
98 // RSAPrivateKey ::= SEQUENCE {
100 // modulus INTEGER, -- n
101 // publicExponent INTEGER, -- e
102 // privateExponent INTEGER, -- d
103 // prime1 INTEGER, -- p
104 // prime2 INTEGER, -- q
105 // exponent1 INTEGER, -- d mod (p-1)
106 // exponent2 INTEGER, -- d mod (q-1)
107 // coefficient INTEGER, -- (inverse of q) mod p
108 // otherPrimeInfos OtherPrimeInfos OPTIONAL
111 // Note that otherPrimeInfos is only applicable for version=1. Since NSS
112 // doesn't use multi-prime can safely use version=0.
113 struct RSAPrivateKey
{
116 SECItem public_exponent
;
117 SECItem private_exponent
;
125 // The system NSS library doesn't have the new PK11_ExportDERPrivateKeyInfo
126 // function yet (https://bugzilla.mozilla.org/show_bug.cgi?id=519255). So we
127 // provide a fallback implementation.
128 #if defined(USE_NSS_CERTS)
129 const SEC_ASN1Template RSAPrivateKeyTemplate
[] = {
130 {SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(RSAPrivateKey
)},
131 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, version
)},
132 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, modulus
)},
133 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, public_exponent
)},
134 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, private_exponent
)},
135 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, prime1
)},
136 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, prime2
)},
137 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, exponent1
)},
138 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, exponent2
)},
139 {SEC_ASN1_INTEGER
, offsetof(RSAPrivateKey
, coefficient
)},
141 #endif // defined(USE_NSS_CERTS)
143 // On success |value| will be filled with data which must be freed by
144 // SECITEM_FreeItem(value, PR_FALSE);
145 bool ReadUint(SECKEYPrivateKey
* key
,
146 CK_ATTRIBUTE_TYPE attribute
,
148 SECStatus rv
= PK11_ReadRawAttribute(PK11_TypePrivKey
, key
, attribute
, value
);
150 // PK11_ReadRawAttribute() returns items of type siBuffer. However in order
151 // for the ASN.1 encoding to be correct, the items must be of type
152 // siUnsignedInteger.
153 value
->type
= siUnsignedInteger
;
155 return rv
== SECSuccess
;
158 // Fills |out| with the RSA private key properties. Returns true on success.
159 // Regardless of the return value, the caller must invoke FreeRSAPrivateKey()
160 // to free up any allocated memory.
162 // The passed in RSAPrivateKey must be zero-initialized.
163 bool InitRSAPrivateKey(SECKEYPrivateKey
* key
, RSAPrivateKey
* out
) {
164 if (key
->keyType
!= rsaKey
)
167 // Everything should be zero-ed out. These are just some spot checks.
168 DCHECK(!out
->version
.data
);
169 DCHECK(!out
->version
.len
);
170 DCHECK(!out
->modulus
.data
);
171 DCHECK(!out
->modulus
.len
);
173 // Always use version=0 since not using multi-prime.
174 if (!SEC_ASN1EncodeInteger(NULL
, &out
->version
, 0))
177 if (!ReadUint(key
, CKA_MODULUS
, &out
->modulus
))
179 if (!ReadUint(key
, CKA_PUBLIC_EXPONENT
, &out
->public_exponent
))
181 if (!ReadUint(key
, CKA_PRIVATE_EXPONENT
, &out
->private_exponent
))
183 if (!ReadUint(key
, CKA_PRIME_1
, &out
->prime1
))
185 if (!ReadUint(key
, CKA_PRIME_2
, &out
->prime2
))
187 if (!ReadUint(key
, CKA_EXPONENT_1
, &out
->exponent1
))
189 if (!ReadUint(key
, CKA_EXPONENT_2
, &out
->exponent2
))
191 if (!ReadUint(key
, CKA_COEFFICIENT
, &out
->coefficient
))
197 struct FreeRsaPrivateKey
{
198 void operator()(RSAPrivateKey
* out
) {
199 SECITEM_FreeItem(&out
->version
, PR_FALSE
);
200 SECITEM_FreeItem(&out
->modulus
, PR_FALSE
);
201 SECITEM_FreeItem(&out
->public_exponent
, PR_FALSE
);
202 SECITEM_FreeItem(&out
->private_exponent
, PR_FALSE
);
203 SECITEM_FreeItem(&out
->prime1
, PR_FALSE
);
204 SECITEM_FreeItem(&out
->prime2
, PR_FALSE
);
205 SECITEM_FreeItem(&out
->exponent1
, PR_FALSE
);
206 SECITEM_FreeItem(&out
->exponent2
, PR_FALSE
);
207 SECITEM_FreeItem(&out
->coefficient
, PR_FALSE
);
211 typedef scoped_ptr
<CERTSubjectPublicKeyInfo
,
212 crypto::NSSDestroyer
<CERTSubjectPublicKeyInfo
,
213 SECKEY_DestroySubjectPublicKeyInfo
>>
214 ScopedCERTSubjectPublicKeyInfo
;
216 struct DestroyGenericObject
{
217 void operator()(PK11GenericObject
* o
) const {
219 PK11_DestroyGenericObject(o
);
223 typedef scoped_ptr
<PK11GenericObject
, DestroyGenericObject
>
224 ScopedPK11GenericObject
;
226 // Helper to add an attribute to a template.
227 void AddAttribute(CK_ATTRIBUTE_TYPE type
,
229 unsigned long length
,
230 std::vector
<CK_ATTRIBUTE
>* templ
) {
231 CK_ATTRIBUTE attribute
= {type
, value
, length
};
232 templ
->push_back(attribute
);
235 void AddAttribute(CK_ATTRIBUTE_TYPE type
,
236 const CryptoData
& data
,
237 std::vector
<CK_ATTRIBUTE
>* templ
) {
238 CK_ATTRIBUTE attribute
= {
239 type
, const_cast<unsigned char*>(data
.bytes()), data
.byte_length()};
240 templ
->push_back(attribute
);
243 void AddAttribute(CK_ATTRIBUTE_TYPE type
,
244 const std::string
& data
,
245 std::vector
<CK_ATTRIBUTE
>* templ
) {
246 AddAttribute(type
, CryptoData(data
), templ
);
249 Status
ExportKeyPkcs8Nss(SECKEYPrivateKey
* key
, std::vector
<uint8_t>* buffer
) {
250 if (key
->keyType
!= rsaKey
)
251 return Status::ErrorUnsupported();
253 // TODO(rsleevi): Implement OAEP support according to the spec.
255 #if defined(USE_NSS_CERTS)
256 // PK11_ExportDERPrivateKeyInfo isn't available. Use our fallback code.
257 const SECOidTag algorithm
= SEC_OID_PKCS1_RSA_ENCRYPTION
;
258 const int kPrivateKeyInfoVersion
= 0;
260 SECKEYPrivateKeyInfo private_key_info
= {};
261 RSAPrivateKey rsa_private_key
= {};
262 scoped_ptr
<RSAPrivateKey
, FreeRsaPrivateKey
> free_private_key(
265 // http://crbug.com/366427: the spec does not define any other failures for
266 // exporting, so none of the subsequent errors are spec compliant.
267 if (!InitRSAPrivateKey(key
, &rsa_private_key
))
268 return Status::OperationError();
270 crypto::ScopedPLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
272 return Status::OperationError();
274 if (!SEC_ASN1EncodeItem(arena
.get(), &private_key_info
.privateKey
,
275 &rsa_private_key
, RSAPrivateKeyTemplate
)) {
276 return Status::OperationError();
279 if (SECSuccess
!= SECOID_SetAlgorithmID(arena
.get(),
280 &private_key_info
.algorithm
,
282 return Status::OperationError();
285 if (!SEC_ASN1EncodeInteger(arena
.get(), &private_key_info
.version
,
286 kPrivateKeyInfoVersion
)) {
287 return Status::OperationError();
290 crypto::ScopedSECItem
encoded_key(
291 SEC_ASN1EncodeItem(NULL
, NULL
, &private_key_info
,
292 SEC_ASN1_GET(SECKEY_PrivateKeyInfoTemplate
)));
293 #else // defined(USE_NSS_CERTS)
294 crypto::ScopedSECItem
encoded_key(PK11_ExportDERPrivateKeyInfo(key
, NULL
));
295 #endif // defined(USE_NSS_CERTS)
297 if (!encoded_key
.get())
298 return Status::OperationError();
300 buffer
->assign(encoded_key
->data
, encoded_key
->data
+ encoded_key
->len
);
301 return Status::Success();
304 Status
ImportRsaPrivateKey(const blink::WebCryptoAlgorithm
& algorithm
,
306 blink::WebCryptoKeyUsageMask usages
,
307 const JwkRsaInfo
& params
,
308 blink::WebCryptoKey
* key
) {
309 Status status
= NssSupportsRsaPrivateKeyImport();
310 if (status
.IsError())
313 CK_OBJECT_CLASS obj_class
= CKO_PRIVATE_KEY
;
314 CK_KEY_TYPE key_type
= CKK_RSA
;
315 CK_BBOOL ck_false
= CK_FALSE
;
317 std::vector
<CK_ATTRIBUTE
> key_template
;
319 AddAttribute(CKA_CLASS
, &obj_class
, sizeof(obj_class
), &key_template
);
320 AddAttribute(CKA_KEY_TYPE
, &key_type
, sizeof(key_type
), &key_template
);
321 AddAttribute(CKA_TOKEN
, &ck_false
, sizeof(ck_false
), &key_template
);
322 AddAttribute(CKA_SENSITIVE
, &ck_false
, sizeof(ck_false
), &key_template
);
323 AddAttribute(CKA_PRIVATE
, &ck_false
, sizeof(ck_false
), &key_template
);
325 // Required properties by JWA.
326 AddAttribute(CKA_MODULUS
, params
.n
, &key_template
);
327 AddAttribute(CKA_PUBLIC_EXPONENT
, params
.e
, &key_template
);
328 AddAttribute(CKA_PRIVATE_EXPONENT
, params
.d
, &key_template
);
330 // Manufacture a CKA_ID so the created key can be retrieved later as a
331 // SECKEYPrivateKey using FindKeyByKeyID(). Unfortunately there isn't a more
332 // direct way to do this in NSS.
334 // For consistency with other NSS key creation methods, set the CKA_ID to
335 // PK11_MakeIDFromPubKey(). There are some problems with
338 // (1) Prior to NSS 3.16.2, there is no parameter validation when creating
339 // private keys. It is therefore possible to construct a key using the
340 // known public modulus, and where all the other parameters are bogus.
341 // FindKeyByKeyID() returns the first key matching the ID. So this would
342 // effectively allow an attacker to retrieve a private key of their
345 // (2) The ID space is shared by different key types. So theoretically
346 // possible to retrieve a key of the wrong type which has a matching
347 // CKA_ID. In practice I am told this is not likely except for small key
348 // sizes, since would require constructing keys with the same public
351 // (3) FindKeyByKeyID() doesn't necessarily return the object that was just
352 // created by CreateGenericObject. If the pre-existing key was
353 // provisioned with flags incompatible with WebCrypto (for instance
354 // marked sensitive) then this will break things.
355 SECItem modulus_item
= MakeSECItemForBuffer(CryptoData(params
.n
));
356 crypto::ScopedSECItem
object_id(PK11_MakeIDFromPubKey(&modulus_item
));
357 AddAttribute(CKA_ID
, CryptoData(object_id
->data
, object_id
->len
),
360 // Optional properties by JWA, however guaranteed to be present by Chromium's
362 AddAttribute(CKA_PRIME_1
, params
.p
, &key_template
);
363 AddAttribute(CKA_PRIME_2
, params
.q
, &key_template
);
364 AddAttribute(CKA_EXPONENT_1
, params
.dp
, &key_template
);
365 AddAttribute(CKA_EXPONENT_2
, params
.dq
, &key_template
);
366 AddAttribute(CKA_COEFFICIENT
, params
.qi
, &key_template
);
368 crypto::ScopedPK11Slot
slot(PK11_GetInternalSlot());
370 ScopedPK11GenericObject
key_object(PK11_CreateGenericObject(
371 slot
.get(), &key_template
[0], key_template
.size(), PR_FALSE
));
374 return Status::OperationError();
376 crypto::ScopedSECKEYPrivateKey
private_key_tmp(
377 PK11_FindKeyByKeyID(slot
.get(), object_id
.get(), NULL
));
379 // PK11_FindKeyByKeyID() may return a handle to an existing key, rather than
380 // the object created by PK11_CreateGenericObject().
381 crypto::ScopedSECKEYPrivateKey
private_key(
382 SECKEY_CopyPrivateKey(private_key_tmp
.get()));
385 return Status::OperationError();
387 blink::WebCryptoKeyAlgorithm key_algorithm
;
388 if (!CreateRsaHashedPrivateKeyAlgorithm(
389 algorithm
.id(), algorithm
.rsaHashedImportParams()->hash().id(),
390 private_key
.get(), &key_algorithm
)) {
391 return Status::ErrorUnexpected();
394 std::vector
<uint8_t> pkcs8_data
;
395 status
= ExportKeyPkcs8Nss(private_key
.get(), &pkcs8_data
);
396 if (status
.IsError())
399 scoped_ptr
<PrivateKeyNss
> key_handle(
400 new PrivateKeyNss(private_key
.Pass(), CryptoData(pkcs8_data
)));
402 *key
= blink::WebCryptoKey::create(key_handle
.release(),
403 blink::WebCryptoKeyTypePrivate
,
404 extractable
, key_algorithm
, usages
);
405 return Status::Success();
408 Status
ExportKeySpkiNss(SECKEYPublicKey
* key
, std::vector
<uint8_t>* buffer
) {
409 const crypto::ScopedSECItem
spki_der(
410 SECKEY_EncodeDERSubjectPublicKeyInfo(key
));
412 return Status::OperationError();
414 buffer
->assign(spki_der
->data
, spki_der
->data
+ spki_der
->len
);
415 return Status::Success();
418 Status
ImportRsaPublicKey(const blink::WebCryptoAlgorithm
& algorithm
,
420 blink::WebCryptoKeyUsageMask usages
,
421 const CryptoData
& modulus_data
,
422 const CryptoData
& exponent_data
,
423 blink::WebCryptoKey
* key
) {
424 if (!modulus_data
.byte_length())
425 return Status::ErrorImportRsaEmptyModulus();
427 if (!exponent_data
.byte_length())
428 return Status::ErrorImportRsaEmptyExponent();
430 DCHECK(modulus_data
.bytes());
431 DCHECK(exponent_data
.bytes());
433 // NSS does not provide a way to create an RSA public key directly from the
434 // modulus and exponent values, but it can import an DER-encoded ASN.1 blob
435 // with these values and create the public key from that. The code below
436 // follows the recommendation described in
437 // https://developer.mozilla.org/en-US/docs/NSS/NSS_Tech_Notes/nss_tech_note7
439 // Pack the input values into a struct compatible with NSS ASN.1 encoding, and
440 // set up an ASN.1 encoder template for it.
441 struct RsaPublicKeyData
{
445 const RsaPublicKeyData pubkey_in
= {
447 const_cast<unsigned char*>(modulus_data
.bytes()),
448 modulus_data
.byte_length()},
450 const_cast<unsigned char*>(exponent_data
.bytes()),
451 exponent_data
.byte_length()}};
452 const SEC_ASN1Template rsa_public_key_template
[] = {
453 {SEC_ASN1_SEQUENCE
, 0, NULL
, sizeof(RsaPublicKeyData
)},
455 SEC_ASN1_INTEGER
, offsetof(RsaPublicKeyData
, modulus
),
458 SEC_ASN1_INTEGER
, offsetof(RsaPublicKeyData
, exponent
),
463 // DER-encode the public key.
464 crypto::ScopedSECItem
pubkey_der(
465 SEC_ASN1EncodeItem(NULL
, NULL
, &pubkey_in
, rsa_public_key_template
));
467 return Status::OperationError();
469 // Import the DER-encoded public key to create an RSA SECKEYPublicKey.
470 crypto::ScopedSECKEYPublicKey
pubkey(
471 SECKEY_ImportDERPublicKey(pubkey_der
.get(), CKK_RSA
));
473 return Status::OperationError();
475 blink::WebCryptoKeyAlgorithm key_algorithm
;
476 if (!CreateRsaHashedPublicKeyAlgorithm(
477 algorithm
.id(), algorithm
.rsaHashedImportParams()->hash().id(),
478 pubkey
.get(), &key_algorithm
)) {
479 return Status::ErrorUnexpected();
482 std::vector
<uint8_t> spki_data
;
483 Status status
= ExportKeySpkiNss(pubkey
.get(), &spki_data
);
484 if (status
.IsError())
487 scoped_ptr
<PublicKeyNss
> key_handle(
488 new PublicKeyNss(pubkey
.Pass(), CryptoData(spki_data
)));
490 *key
= blink::WebCryptoKey::create(key_handle
.release(),
491 blink::WebCryptoKeyTypePublic
, extractable
,
492 key_algorithm
, usages
);
493 return Status::Success();
498 Status
RsaHashedAlgorithm::GenerateKey(
499 const blink::WebCryptoAlgorithm
& algorithm
,
501 blink::WebCryptoKeyUsageMask combined_usages
,
502 GenerateKeyResult
* result
) const {
503 blink::WebCryptoKeyUsageMask public_usages
= 0;
504 blink::WebCryptoKeyUsageMask private_usages
= 0;
506 Status status
= GetUsagesForGenerateAsymmetricKey(
507 combined_usages
, all_public_key_usages_
, all_private_key_usages_
,
508 &public_usages
, &private_usages
);
509 if (status
.IsError())
512 unsigned int public_exponent
= 0;
513 unsigned int modulus_length_bits
= 0;
514 status
= GetRsaKeyGenParameters(algorithm
.rsaHashedKeyGenParams(),
515 &public_exponent
, &modulus_length_bits
);
516 if (status
.IsError())
519 crypto::ScopedPK11Slot
slot(PK11_GetInternalKeySlot());
521 return Status::OperationError();
523 PK11RSAGenParams rsa_gen_params
;
524 rsa_gen_params
.keySizeInBits
= modulus_length_bits
;
525 rsa_gen_params
.pe
= public_exponent
;
527 // The usages are enforced at the WebCrypto layer, so it isn't necessary to
528 // create keys with limited usages.
529 const CK_FLAGS operation_flags_mask
= kAllOperationFlags
;
531 // The private key must be marked as insensitive and extractable, otherwise it
532 // cannot later be exported in unencrypted form or structured-cloned.
533 const PK11AttrFlags attribute_flags
=
534 PK11_ATTR_INSENSITIVE
| PK11_ATTR_EXTRACTABLE
;
536 // Note: NSS does not generate an sec_public_key if the call below fails,
537 // so there is no danger of a leaked sec_public_key.
538 SECKEYPublicKey
* sec_public_key
;
539 crypto::ScopedSECKEYPrivateKey
scoped_sec_private_key(
540 PK11_GenerateKeyPairWithOpFlags(slot
.get(), CKM_RSA_PKCS_KEY_PAIR_GEN
,
541 &rsa_gen_params
, &sec_public_key
,
542 attribute_flags
, generate_flags_
,
543 operation_flags_mask
, NULL
));
544 if (!scoped_sec_private_key
)
545 return Status::OperationError();
547 blink::WebCryptoKeyAlgorithm key_algorithm
;
548 if (!CreateRsaHashedPublicKeyAlgorithm(
549 algorithm
.id(), algorithm
.rsaHashedKeyGenParams()->hash().id(),
550 sec_public_key
, &key_algorithm
)) {
551 return Status::ErrorUnexpected();
554 std::vector
<uint8_t> spki_data
;
555 status
= ExportKeySpkiNss(sec_public_key
, &spki_data
);
556 if (status
.IsError())
559 scoped_ptr
<PublicKeyNss
> public_key_handle(new PublicKeyNss(
560 crypto::ScopedSECKEYPublicKey(sec_public_key
), CryptoData(spki_data
)));
562 std::vector
<uint8_t> pkcs8_data
;
563 status
= ExportKeyPkcs8Nss(scoped_sec_private_key
.get(), &pkcs8_data
);
564 if (status
.IsError())
567 scoped_ptr
<PrivateKeyNss
> private_key_handle(
568 new PrivateKeyNss(scoped_sec_private_key
.Pass(), CryptoData(pkcs8_data
)));
570 blink::WebCryptoKey public_key
= blink::WebCryptoKey::create(
571 public_key_handle
.release(), blink::WebCryptoKeyTypePublic
, true,
572 key_algorithm
, public_usages
);
574 blink::WebCryptoKey private_key
= blink::WebCryptoKey::create(
575 private_key_handle
.release(), blink::WebCryptoKeyTypePrivate
, extractable
,
576 key_algorithm
, private_usages
);
578 result
->AssignKeyPair(public_key
, private_key
);
579 return Status::Success();
582 Status
RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(
583 blink::WebCryptoKeyFormat format
,
584 blink::WebCryptoKeyUsageMask usages
) const {
585 return VerifyUsagesBeforeImportAsymmetricKey(format
, all_public_key_usages_
,
586 all_private_key_usages_
, usages
);
589 Status
RsaHashedAlgorithm::ImportKeyPkcs8(
590 const CryptoData
& key_data
,
591 const blink::WebCryptoAlgorithm
& algorithm
,
593 blink::WebCryptoKeyUsageMask usages
,
594 blink::WebCryptoKey
* key
) const {
595 Status status
= NssSupportsRsaPrivateKeyImport();
596 if (status
.IsError())
599 crypto::ScopedPLArenaPool
arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE
));
601 return Status::OperationError();
603 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 PKCS#8
604 // private key info object. Excess data is illegal, but NSS silently accepts
605 // it, so first ensure that 'key_data' consists of a single ASN.1 element.
606 SECItem key_item
= MakeSECItemForBuffer(key_data
);
608 if (SEC_QuickDERDecodeItem(arena
.get(), &pki_der
,
609 SEC_ASN1_GET(SEC_AnyTemplate
),
610 &key_item
) != SECSuccess
) {
611 return Status::DataError();
614 SECKEYPrivateKey
* seckey_private_key
= NULL
;
615 crypto::ScopedPK11Slot
slot(PK11_GetInternalSlot());
616 if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot
.get(), &pki_der
,
623 NULL
) != SECSuccess
) {
624 return Status::DataError();
626 DCHECK(seckey_private_key
);
627 crypto::ScopedSECKEYPrivateKey
private_key(seckey_private_key
);
629 const KeyType sec_key_type
= SECKEY_GetPrivateKeyType(private_key
.get());
630 if (sec_key_type
!= rsaKey
)
631 return Status::DataError();
633 blink::WebCryptoKeyAlgorithm key_algorithm
;
634 if (!CreateRsaHashedPrivateKeyAlgorithm(
635 algorithm
.id(), algorithm
.rsaHashedImportParams()->hash().id(),
636 private_key
.get(), &key_algorithm
)) {
637 return Status::ErrorUnexpected();
640 // TODO(eroman): This is probably going to be the same as the input.
641 std::vector
<uint8_t> pkcs8_data
;
642 status
= ExportKeyPkcs8Nss(private_key
.get(), &pkcs8_data
);
643 if (status
.IsError())
646 scoped_ptr
<PrivateKeyNss
> key_handle(
647 new PrivateKeyNss(private_key
.Pass(), CryptoData(pkcs8_data
)));
649 *key
= blink::WebCryptoKey::create(key_handle
.release(),
650 blink::WebCryptoKeyTypePrivate
,
651 extractable
, key_algorithm
, usages
);
653 return Status::Success();
656 Status
RsaHashedAlgorithm::ImportKeySpki(
657 const CryptoData
& key_data
,
658 const blink::WebCryptoAlgorithm
& algorithm
,
660 blink::WebCryptoKeyUsageMask usages
,
661 blink::WebCryptoKey
* key
) const {
662 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject
663 // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo.
664 SECItem spki_item
= MakeSECItemForBuffer(key_data
);
665 const ScopedCERTSubjectPublicKeyInfo
spki(
666 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item
));
668 return Status::DataError();
670 crypto::ScopedSECKEYPublicKey
sec_public_key(
671 SECKEY_ExtractPublicKey(spki
.get()));
673 return Status::DataError();
675 const KeyType sec_key_type
= SECKEY_GetPublicKeyType(sec_public_key
.get());
676 if (sec_key_type
!= rsaKey
)
677 return Status::DataError();
679 blink::WebCryptoKeyAlgorithm key_algorithm
;
680 if (!CreateRsaHashedPublicKeyAlgorithm(
681 algorithm
.id(), algorithm
.rsaHashedImportParams()->hash().id(),
682 sec_public_key
.get(), &key_algorithm
)) {
683 return Status::ErrorUnexpected();
686 // TODO(eroman): This is probably going to be the same as the input.
687 std::vector
<uint8_t> spki_data
;
688 Status status
= ExportKeySpkiNss(sec_public_key
.get(), &spki_data
);
689 if (status
.IsError())
692 scoped_ptr
<PublicKeyNss
> key_handle(
693 new PublicKeyNss(sec_public_key
.Pass(), CryptoData(spki_data
)));
695 *key
= blink::WebCryptoKey::create(key_handle
.release(),
696 blink::WebCryptoKeyTypePublic
, extractable
,
697 key_algorithm
, usages
);
699 return Status::Success();
702 Status
RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey
& key
,
703 std::vector
<uint8_t>* buffer
) const {
704 if (key
.type() != blink::WebCryptoKeyTypePrivate
)
705 return Status::ErrorUnexpectedKeyType();
706 *buffer
= PrivateKeyNss::Cast(key
)->pkcs8_data();
707 return Status::Success();
710 Status
RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey
& key
,
711 std::vector
<uint8_t>* buffer
) const {
712 if (key
.type() != blink::WebCryptoKeyTypePublic
)
713 return Status::ErrorUnexpectedKeyType();
714 *buffer
= PublicKeyNss::Cast(key
)->spki_data();
715 return Status::Success();
718 Status
RsaHashedAlgorithm::ImportKeyJwk(
719 const CryptoData
& key_data
,
720 const blink::WebCryptoAlgorithm
& algorithm
,
722 blink::WebCryptoKeyUsageMask usages
,
723 blink::WebCryptoKey
* key
) const {
724 const char* jwk_algorithm
=
725 GetJwkAlgorithm(algorithm
.rsaHashedImportParams()->hash().id());
728 return Status::ErrorUnexpected();
732 ReadRsaKeyJwk(key_data
, jwk_algorithm
, extractable
, usages
, &jwk
);
733 if (status
.IsError())
736 // Once the key type is known, verify the usages.
737 status
= CheckKeyCreationUsages(
738 jwk
.is_private_key
? all_private_key_usages_
: all_public_key_usages_
,
739 usages
, !jwk
.is_private_key
);
740 if (status
.IsError())
743 return jwk
.is_private_key
744 ? ImportRsaPrivateKey(algorithm
, extractable
, usages
, jwk
, key
)
745 : ImportRsaPublicKey(algorithm
, extractable
, usages
,
746 CryptoData(jwk
.n
), CryptoData(jwk
.e
), key
);
749 Status
RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey
& key
,
750 std::vector
<uint8_t>* buffer
) const {
751 const char* jwk_algorithm
=
752 GetJwkAlgorithm(key
.algorithm().rsaHashedParams()->hash().id());
755 return Status::ErrorUnexpected();
757 switch (key
.type()) {
758 case blink::WebCryptoKeyTypePublic
: {
759 SECKEYPublicKey
* nss_key
= PublicKeyNss::Cast(key
)->key();
760 if (nss_key
->keyType
!= rsaKey
)
761 return Status::ErrorUnsupported();
763 WriteRsaPublicKeyJwk(SECItemToCryptoData(nss_key
->u
.rsa
.modulus
),
764 SECItemToCryptoData(nss_key
->u
.rsa
.publicExponent
),
765 jwk_algorithm
, key
.extractable(), key
.usages(),
768 return Status::Success();
771 case blink::WebCryptoKeyTypePrivate
: {
772 SECKEYPrivateKey
* nss_key
= PrivateKeyNss::Cast(key
)->key();
773 RSAPrivateKey key_props
= {};
774 scoped_ptr
<RSAPrivateKey
, FreeRsaPrivateKey
> free_private_key(&key_props
);
776 if (!InitRSAPrivateKey(nss_key
, &key_props
))
777 return Status::OperationError();
779 WriteRsaPrivateKeyJwk(SECItemToCryptoData(key_props
.modulus
),
780 SECItemToCryptoData(key_props
.public_exponent
),
781 SECItemToCryptoData(key_props
.private_exponent
),
782 SECItemToCryptoData(key_props
.prime1
),
783 SECItemToCryptoData(key_props
.prime2
),
784 SECItemToCryptoData(key_props
.exponent1
),
785 SECItemToCryptoData(key_props
.exponent2
),
786 SECItemToCryptoData(key_props
.coefficient
),
787 jwk_algorithm
, key
.extractable(), key
.usages(),
790 return Status::Success();
793 return Status::ErrorUnexpected();
797 Status
RsaHashedAlgorithm::SerializeKeyForClone(
798 const blink::WebCryptoKey
& key
,
799 blink::WebVector
<uint8_t>* key_data
) const {
800 key_data
->assign(static_cast<KeyNss
*>(key
.handle())->serialized_key_data());
801 return Status::Success();
804 // TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763
805 Status
RsaHashedAlgorithm::DeserializeKeyForClone(
806 const blink::WebCryptoKeyAlgorithm
& algorithm
,
807 blink::WebCryptoKeyType type
,
809 blink::WebCryptoKeyUsageMask usages
,
810 const CryptoData
& key_data
,
811 blink::WebCryptoKey
* key
) const {
812 blink::WebCryptoAlgorithm import_algorithm
= CreateRsaHashedImportAlgorithm(
813 algorithm
.id(), algorithm
.rsaHashedParams()->hash().id());
818 case blink::WebCryptoKeyTypePublic
:
820 ImportKeySpki(key_data
, import_algorithm
, extractable
, usages
, key
);
822 case blink::WebCryptoKeyTypePrivate
:
824 ImportKeyPkcs8(key_data
, import_algorithm
, extractable
, usages
, key
);
827 return Status::ErrorUnexpected();
830 // There is some duplicated information in the serialized format used by
831 // structured clone (since the KeyAlgorithm is serialized separately from the
832 // key data). Use this extra information to further validate what was
833 // deserialized from the key data.
835 if (algorithm
.id() != key
->algorithm().id())
836 return Status::ErrorUnexpected();
838 if (key
->type() != type
)
839 return Status::ErrorUnexpected();
841 if (algorithm
.rsaHashedParams()->modulusLengthBits() !=
842 key
->algorithm().rsaHashedParams()->modulusLengthBits()) {
843 return Status::ErrorUnexpected();
846 if (algorithm
.rsaHashedParams()->publicExponent().size() !=
847 key
->algorithm().rsaHashedParams()->publicExponent().size() ||
849 memcmp(algorithm
.rsaHashedParams()->publicExponent().data(),
850 key
->algorithm().rsaHashedParams()->publicExponent().data(),
851 key
->algorithm().rsaHashedParams()->publicExponent().size())) {
852 return Status::ErrorUnexpected();
855 return Status::Success();
858 } // namespace webcrypto