mandoline: Reland "Sandbox mojo:browser and mojo:omnibox."
[chromium-blink-merge.git] / components / webcrypto / nss / rsa_hashed_algorithm_nss.cc
blob1b7e4a2df4e7bc21c8a08371e0d3bc0038ea2537
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"
7 #include <secasn1.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"
21 namespace webcrypto {
23 namespace {
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();
59 #else
60 Status NssSupportsRsaPrivateKeyImport() {
61 return Status::Success();
63 #endif
65 bool CreateRsaHashedPublicKeyAlgorithm(
66 blink::WebCryptoAlgorithmId rsa_algorithm,
67 blink::WebCryptoAlgorithmId hash_algorithm,
68 SECKEYPublicKey* key,
69 blink::WebCryptoKeyAlgorithm* key_algorithm) {
70 // TODO(eroman): What about other key types rsaPss, rsaOaep.
71 if (!key || key->keyType != rsaKey)
72 return false;
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);
81 return true;
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));
90 if (!public_key)
91 return false;
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 {
99 // version Version,
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
109 // }
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 {
114 SECItem version;
115 SECItem modulus;
116 SECItem public_exponent;
117 SECItem private_exponent;
118 SECItem prime1;
119 SECItem prime2;
120 SECItem exponent1;
121 SECItem exponent2;
122 SECItem coefficient;
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)},
140 {0}};
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,
147 SECItem* value) {
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)
165 return false;
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))
175 return false;
177 if (!ReadUint(key, CKA_MODULUS, &out->modulus))
178 return false;
179 if (!ReadUint(key, CKA_PUBLIC_EXPONENT, &out->public_exponent))
180 return false;
181 if (!ReadUint(key, CKA_PRIVATE_EXPONENT, &out->private_exponent))
182 return false;
183 if (!ReadUint(key, CKA_PRIME_1, &out->prime1))
184 return false;
185 if (!ReadUint(key, CKA_PRIME_2, &out->prime2))
186 return false;
187 if (!ReadUint(key, CKA_EXPONENT_1, &out->exponent1))
188 return false;
189 if (!ReadUint(key, CKA_EXPONENT_2, &out->exponent2))
190 return false;
191 if (!ReadUint(key, CKA_COEFFICIENT, &out->coefficient))
192 return false;
194 return true;
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 {
218 if (o)
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,
228 void* value,
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(
263 &rsa_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));
271 if (!arena.get())
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,
281 algorithm, NULL)) {
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,
305 bool extractable,
306 blink::WebCryptoKeyUsageMask usages,
307 const JwkRsaInfo& params,
308 blink::WebCryptoKey* key) {
309 Status status = NssSupportsRsaPrivateKeyImport();
310 if (status.IsError())
311 return status;
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
336 // this approach:
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
343 // choice.
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
349 // data.
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),
358 &key_template);
360 // Optional properties by JWA, however guaranteed to be present by Chromium's
361 // implementation.
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));
373 if (!key_object)
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()));
384 if (!private_key)
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())
397 return status;
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));
411 if (!spki_der)
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,
419 bool extractable,
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 {
442 SECItem modulus;
443 SECItem exponent;
445 const RsaPublicKeyData pubkey_in = {
446 {siUnsignedInteger,
447 const_cast<unsigned char*>(modulus_data.bytes()),
448 modulus_data.byte_length()},
449 {siUnsignedInteger,
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),
461 0, }};
463 // DER-encode the public key.
464 crypto::ScopedSECItem pubkey_der(
465 SEC_ASN1EncodeItem(NULL, NULL, &pubkey_in, rsa_public_key_template));
466 if (!pubkey_der)
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));
472 if (!pubkey)
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())
485 return status;
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();
496 } // namespace
498 Status RsaHashedAlgorithm::GenerateKey(
499 const blink::WebCryptoAlgorithm& algorithm,
500 bool extractable,
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())
510 return status;
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())
517 return status;
519 crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot());
520 if (!slot)
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())
557 return status;
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())
565 return status;
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,
592 bool extractable,
593 blink::WebCryptoKeyUsageMask usages,
594 blink::WebCryptoKey* key) const {
595 Status status = NssSupportsRsaPrivateKeyImport();
596 if (status.IsError())
597 return status;
599 crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
600 if (!arena.get())
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);
607 SECItem pki_der;
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,
617 NULL, // nickname
618 NULL, // publicValue
619 false, // isPerm
620 false, // isPrivate
621 KU_ALL, // usage
622 &seckey_private_key,
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())
644 return status;
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,
659 bool extractable,
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));
667 if (!spki)
668 return Status::DataError();
670 crypto::ScopedSECKEYPublicKey sec_public_key(
671 SECKEY_ExtractPublicKey(spki.get()));
672 if (!sec_public_key)
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())
690 return status;
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,
721 bool extractable,
722 blink::WebCryptoKeyUsageMask usages,
723 blink::WebCryptoKey* key) const {
724 const char* jwk_algorithm =
725 GetJwkAlgorithm(algorithm.rsaHashedImportParams()->hash().id());
727 if (!jwk_algorithm)
728 return Status::ErrorUnexpected();
730 JwkRsaInfo jwk;
731 Status status =
732 ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usages, &jwk);
733 if (status.IsError())
734 return status;
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())
741 return status;
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());
754 if (!jwk_algorithm)
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(),
766 buffer);
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(),
788 buffer);
790 return Status::Success();
792 default:
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,
808 bool extractable,
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());
815 Status status;
817 switch (type) {
818 case blink::WebCryptoKeyTypePublic:
819 status =
820 ImportKeySpki(key_data, import_algorithm, extractable, usages, key);
821 break;
822 case blink::WebCryptoKeyTypePrivate:
823 status =
824 ImportKeyPkcs8(key_data, import_algorithm, extractable, usages, key);
825 break;
826 default:
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() ||
848 0 !=
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