• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/child/webcrypto/nss/rsa_key_nss.h"
6 
7 #include "base/logging.h"
8 #include "content/child/webcrypto/crypto_data.h"
9 #include "content/child/webcrypto/jwk.h"
10 #include "content/child/webcrypto/nss/key_nss.h"
11 #include "content/child/webcrypto/nss/util_nss.h"
12 #include "content/child/webcrypto/status.h"
13 #include "content/child/webcrypto/webcrypto_util.h"
14 #include "crypto/scoped_nss_types.h"
15 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
16 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
17 
18 namespace content {
19 
20 namespace webcrypto {
21 
22 namespace {
23 
24 #if defined(USE_NSS) && !defined(OS_CHROMEOS)
ErrorRsaPrivateKeyImportNotSupported()25 Status ErrorRsaPrivateKeyImportNotSupported() {
26   return Status::ErrorUnsupported(
27       "NSS version must be at least 3.16.2 for RSA private key import. See "
28       "http://crbug.com/380424");
29 }
30 
31 // Prior to NSS 3.16.2 RSA key parameters were not validated. This is
32 // a security problem for RSA private key import from JWK which uses a
33 // CKA_ID based on the public modulus to retrieve the private key.
NssSupportsRsaPrivateKeyImport()34 Status NssSupportsRsaPrivateKeyImport() {
35   if (!NSS_VersionCheck("3.16.2"))
36     return ErrorRsaPrivateKeyImportNotSupported();
37 
38   // Also ensure that the version of Softoken is 3.16.2 or later.
39   crypto::ScopedPK11Slot slot(PK11_GetInternalSlot());
40   CK_SLOT_INFO info = {};
41   if (PK11_GetSlotInfo(slot.get(), &info) != SECSuccess)
42     return ErrorRsaPrivateKeyImportNotSupported();
43 
44   // CK_SLOT_INFO.hardwareVersion contains the major.minor
45   // version info for Softoken in the corresponding .major/.minor
46   // fields, and .firmwareVersion contains the patch.build
47   // version info (in the .major/.minor fields)
48   if ((info.hardwareVersion.major > 3) ||
49       (info.hardwareVersion.major == 3 &&
50        (info.hardwareVersion.minor > 16 ||
51         (info.hardwareVersion.minor == 16 &&
52          info.firmwareVersion.major >= 2)))) {
53     return Status::Success();
54   }
55 
56   return ErrorRsaPrivateKeyImportNotSupported();
57 }
58 #else
59 Status NssSupportsRsaPrivateKeyImport() {
60   return Status::Success();
61 }
62 #endif
63 
CreateRsaHashedPublicKeyAlgorithm(blink::WebCryptoAlgorithmId rsa_algorithm,blink::WebCryptoAlgorithmId hash_algorithm,SECKEYPublicKey * key,blink::WebCryptoKeyAlgorithm * key_algorithm)64 bool CreateRsaHashedPublicKeyAlgorithm(
65     blink::WebCryptoAlgorithmId rsa_algorithm,
66     blink::WebCryptoAlgorithmId hash_algorithm,
67     SECKEYPublicKey* key,
68     blink::WebCryptoKeyAlgorithm* key_algorithm) {
69   // TODO(eroman): What about other key types rsaPss, rsaOaep.
70   if (!key || key->keyType != rsaKey)
71     return false;
72 
73   unsigned int modulus_length_bits = SECKEY_PublicKeyStrength(key) * 8;
74   CryptoData public_exponent(key->u.rsa.publicExponent.data,
75                              key->u.rsa.publicExponent.len);
76 
77   *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed(
78       rsa_algorithm,
79       modulus_length_bits,
80       public_exponent.bytes(),
81       public_exponent.byte_length(),
82       hash_algorithm);
83   return true;
84 }
85 
CreateRsaHashedPrivateKeyAlgorithm(blink::WebCryptoAlgorithmId rsa_algorithm,blink::WebCryptoAlgorithmId hash_algorithm,SECKEYPrivateKey * key,blink::WebCryptoKeyAlgorithm * key_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));
92   if (!public_key)
93     return false;
94   return CreateRsaHashedPublicKeyAlgorithm(
95       rsa_algorithm, hash_algorithm, public_key.get(), key_algorithm);
96 }
97 
98 // From PKCS#1 [http://tools.ietf.org/html/rfc3447]:
99 //
100 //    RSAPrivateKey ::= SEQUENCE {
101 //      version           Version,
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
111 //    }
112 //
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 {
116   SECItem version;
117   SECItem modulus;
118   SECItem public_exponent;
119   SECItem private_exponent;
120   SECItem prime1;
121   SECItem prime2;
122   SECItem exponent1;
123   SECItem exponent2;
124   SECItem coefficient;
125 };
126 
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.
130 #if defined(USE_NSS)
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)},
142     {0}};
143 #endif  // defined(USE_NSS)
144 
145 // On success |value| will be filled with data which must be freed by
146 // SECITEM_FreeItem(value, PR_FALSE);
ReadUint(SECKEYPrivateKey * key,CK_ATTRIBUTE_TYPE attribute,SECItem * value)147 bool ReadUint(SECKEYPrivateKey* key,
148               CK_ATTRIBUTE_TYPE attribute,
149               SECItem* value) {
150   SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, attribute, value);
151 
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;
156 
157   return rv == SECSuccess;
158 }
159 
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.
163 //
164 // The passed in RSAPrivateKey must be zero-initialized.
InitRSAPrivateKey(SECKEYPrivateKey * key,RSAPrivateKey * out)165 bool InitRSAPrivateKey(SECKEYPrivateKey* key, RSAPrivateKey* out) {
166   if (key->keyType != rsaKey)
167     return false;
168 
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);
174 
175   // Always use version=0 since not using multi-prime.
176   if (!SEC_ASN1EncodeInteger(NULL, &out->version, 0))
177     return false;
178 
179   if (!ReadUint(key, CKA_MODULUS, &out->modulus))
180     return false;
181   if (!ReadUint(key, CKA_PUBLIC_EXPONENT, &out->public_exponent))
182     return false;
183   if (!ReadUint(key, CKA_PRIVATE_EXPONENT, &out->private_exponent))
184     return false;
185   if (!ReadUint(key, CKA_PRIME_1, &out->prime1))
186     return false;
187   if (!ReadUint(key, CKA_PRIME_2, &out->prime2))
188     return false;
189   if (!ReadUint(key, CKA_EXPONENT_1, &out->exponent1))
190     return false;
191   if (!ReadUint(key, CKA_EXPONENT_2, &out->exponent2))
192     return false;
193   if (!ReadUint(key, CKA_COEFFICIENT, &out->coefficient))
194     return false;
195 
196   return true;
197 }
198 
199 struct FreeRsaPrivateKey {
operator ()content::webcrypto::__anoncc75c6650111::FreeRsaPrivateKey200   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);
210   }
211 };
212 
213 typedef scoped_ptr<CERTSubjectPublicKeyInfo,
214                    crypto::NSSDestroyer<CERTSubjectPublicKeyInfo,
215                                         SECKEY_DestroySubjectPublicKeyInfo> >
216     ScopedCERTSubjectPublicKeyInfo;
217 
218 struct DestroyGenericObject {
operator ()content::webcrypto::__anoncc75c6650111::DestroyGenericObject219   void operator()(PK11GenericObject* o) const {
220     if (o)
221       PK11_DestroyGenericObject(o);
222   }
223 };
224 
225 typedef scoped_ptr<PK11GenericObject, DestroyGenericObject>
226     ScopedPK11GenericObject;
227 
228 // Helper to add an attribute to a template.
AddAttribute(CK_ATTRIBUTE_TYPE type,void * value,unsigned long length,std::vector<CK_ATTRIBUTE> * templ)229 void AddAttribute(CK_ATTRIBUTE_TYPE type,
230                   void* value,
231                   unsigned long length,
232                   std::vector<CK_ATTRIBUTE>* templ) {
233   CK_ATTRIBUTE attribute = {type, value, length};
234   templ->push_back(attribute);
235 }
236 
AddAttribute(CK_ATTRIBUTE_TYPE type,const CryptoData & data,std::vector<CK_ATTRIBUTE> * templ)237 void AddAttribute(CK_ATTRIBUTE_TYPE type,
238                   const CryptoData& data,
239                   std::vector<CK_ATTRIBUTE>* templ) {
240   CK_ATTRIBUTE attribute = {type, const_cast<unsigned char*>(data.bytes()),
241                             data.byte_length()};
242   templ->push_back(attribute);
243 }
244 
AddAttribute(CK_ATTRIBUTE_TYPE type,const std::string & data,std::vector<CK_ATTRIBUTE> * templ)245 void AddAttribute(CK_ATTRIBUTE_TYPE type,
246                   const std::string& data,
247                   std::vector<CK_ATTRIBUTE>* templ) {
248   AddAttribute(type, CryptoData(data), templ);
249 }
250 
ExportKeyPkcs8Nss(SECKEYPrivateKey * key,std::vector<uint8_t> * buffer)251 Status ExportKeyPkcs8Nss(SECKEYPrivateKey* key, std::vector<uint8_t>* buffer) {
252   if (key->keyType != rsaKey)
253     return Status::ErrorUnsupported();
254 
255 // TODO(rsleevi): Implement OAEP support according to the spec.
256 
257 #if defined(USE_NSS)
258   // PK11_ExportDERPrivateKeyInfo isn't available. Use our fallback code.
259   const SECOidTag algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION;
260   const int kPrivateKeyInfoVersion = 0;
261 
262   SECKEYPrivateKeyInfo private_key_info = {};
263   RSAPrivateKey rsa_private_key = {};
264   scoped_ptr<RSAPrivateKey, FreeRsaPrivateKey> free_private_key(
265       &rsa_private_key);
266 
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();
271 
272   crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
273   if (!arena.get())
274     return Status::OperationError();
275 
276   if (!SEC_ASN1EncodeItem(arena.get(),
277                           &private_key_info.privateKey,
278                           &rsa_private_key,
279                           RSAPrivateKeyTemplate))
280     return Status::OperationError();
281 
282   if (SECSuccess !=
283       SECOID_SetAlgorithmID(
284           arena.get(), &private_key_info.algorithm, algorithm, NULL))
285     return Status::OperationError();
286 
287   if (!SEC_ASN1EncodeInteger(
288           arena.get(), &private_key_info.version, kPrivateKeyInfoVersion))
289     return Status::OperationError();
290 
291   crypto::ScopedSECItem encoded_key(
292       SEC_ASN1EncodeItem(NULL,
293                          NULL,
294                          &private_key_info,
295                          SEC_ASN1_GET(SECKEY_PrivateKeyInfoTemplate)));
296 #else   // defined(USE_NSS)
297   crypto::ScopedSECItem encoded_key(PK11_ExportDERPrivateKeyInfo(key, NULL));
298 #endif  // defined(USE_NSS)
299 
300   if (!encoded_key.get())
301     return Status::OperationError();
302 
303   buffer->assign(encoded_key->data, encoded_key->data + encoded_key->len);
304   return Status::Success();
305 }
306 
ImportRsaPrivateKey(const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,const JwkRsaInfo & params,blink::WebCryptoKey * key)307 Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm,
308                            bool extractable,
309                            blink::WebCryptoKeyUsageMask usage_mask,
310                            const JwkRsaInfo& params,
311                            blink::WebCryptoKey* key) {
312   Status status = NssSupportsRsaPrivateKeyImport();
313   if (status.IsError())
314     return status;
315 
316   CK_OBJECT_CLASS obj_class = CKO_PRIVATE_KEY;
317   CK_KEY_TYPE key_type = CKK_RSA;
318   CK_BBOOL ck_false = CK_FALSE;
319 
320   std::vector<CK_ATTRIBUTE> key_template;
321 
322   AddAttribute(CKA_CLASS, &obj_class, sizeof(obj_class), &key_template);
323   AddAttribute(CKA_KEY_TYPE, &key_type, sizeof(key_type), &key_template);
324   AddAttribute(CKA_TOKEN, &ck_false, sizeof(ck_false), &key_template);
325   AddAttribute(CKA_SENSITIVE, &ck_false, sizeof(ck_false), &key_template);
326   AddAttribute(CKA_PRIVATE, &ck_false, sizeof(ck_false), &key_template);
327 
328   // Required properties by JWA.
329   AddAttribute(CKA_MODULUS, params.n, &key_template);
330   AddAttribute(CKA_PUBLIC_EXPONENT, params.e, &key_template);
331   AddAttribute(CKA_PRIVATE_EXPONENT, params.d, &key_template);
332 
333   // Manufacture a CKA_ID so the created key can be retrieved later as a
334   // SECKEYPrivateKey using FindKeyByKeyID(). Unfortunately there isn't a more
335   // direct way to do this in NSS.
336   //
337   // For consistency with other NSS key creation methods, set the CKA_ID to
338   // PK11_MakeIDFromPubKey(). There are some problems with
339   // this approach:
340   //
341   //  (1) Prior to NSS 3.16.2, there is no parameter validation when creating
342   //      private keys. It is therefore possible to construct a key using the
343   //      known public modulus, and where all the other parameters are bogus.
344   //      FindKeyByKeyID() returns the first key matching the ID. So this would
345   //      effectively allow an attacker to retrieve a private key of their
346   //      choice.
347   //
348   //  (2) The ID space is shared by different key types. So theoretically
349   //      possible to retrieve a key of the wrong type which has a matching
350   //      CKA_ID. In practice I am told this is not likely except for small key
351   //      sizes, since would require constructing keys with the same public
352   //      data.
353   //
354   //  (3) FindKeyByKeyID() doesn't necessarily return the object that was just
355   //      created by CreateGenericObject. If the pre-existing key was
356   //      provisioned with flags incompatible with WebCrypto (for instance
357   //      marked sensitive) then this will break things.
358   SECItem modulus_item = MakeSECItemForBuffer(CryptoData(params.n));
359   crypto::ScopedSECItem object_id(PK11_MakeIDFromPubKey(&modulus_item));
360   AddAttribute(
361       CKA_ID, CryptoData(object_id->data, object_id->len), &key_template);
362 
363   // Optional properties by JWA, however guaranteed to be present by Chromium's
364   // implementation.
365   AddAttribute(CKA_PRIME_1, params.p, &key_template);
366   AddAttribute(CKA_PRIME_2, params.q, &key_template);
367   AddAttribute(CKA_EXPONENT_1, params.dp, &key_template);
368   AddAttribute(CKA_EXPONENT_2, params.dq, &key_template);
369   AddAttribute(CKA_COEFFICIENT, params.qi, &key_template);
370 
371   crypto::ScopedPK11Slot slot(PK11_GetInternalSlot());
372 
373   ScopedPK11GenericObject key_object(PK11_CreateGenericObject(
374       slot.get(), &key_template[0], key_template.size(), PR_FALSE));
375 
376   if (!key_object)
377     return Status::OperationError();
378 
379   crypto::ScopedSECKEYPrivateKey private_key_tmp(
380       PK11_FindKeyByKeyID(slot.get(), object_id.get(), NULL));
381 
382   // PK11_FindKeyByKeyID() may return a handle to an existing key, rather than
383   // the object created by PK11_CreateGenericObject().
384   crypto::ScopedSECKEYPrivateKey private_key(
385       SECKEY_CopyPrivateKey(private_key_tmp.get()));
386 
387   if (!private_key)
388     return Status::OperationError();
389 
390   blink::WebCryptoKeyAlgorithm key_algorithm;
391   if (!CreateRsaHashedPrivateKeyAlgorithm(
392           algorithm.id(),
393           algorithm.rsaHashedImportParams()->hash().id(),
394           private_key.get(),
395           &key_algorithm)) {
396     return Status::ErrorUnexpected();
397   }
398 
399   std::vector<uint8_t> pkcs8_data;
400   status = ExportKeyPkcs8Nss(private_key.get(), &pkcs8_data);
401   if (status.IsError())
402     return status;
403 
404   scoped_ptr<PrivateKeyNss> key_handle(
405       new PrivateKeyNss(private_key.Pass(), CryptoData(pkcs8_data)));
406 
407   *key = blink::WebCryptoKey::create(key_handle.release(),
408                                      blink::WebCryptoKeyTypePrivate,
409                                      extractable,
410                                      key_algorithm,
411                                      usage_mask);
412   return Status::Success();
413 }
414 
ExportKeySpkiNss(SECKEYPublicKey * key,std::vector<uint8_t> * buffer)415 Status ExportKeySpkiNss(SECKEYPublicKey* key, std::vector<uint8_t>* buffer) {
416   const crypto::ScopedSECItem spki_der(
417       SECKEY_EncodeDERSubjectPublicKeyInfo(key));
418   if (!spki_der)
419     return Status::OperationError();
420 
421   buffer->assign(spki_der->data, spki_der->data + spki_der->len);
422   return Status::Success();
423 }
424 
ImportRsaPublicKey(const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,const CryptoData & modulus_data,const CryptoData & exponent_data,blink::WebCryptoKey * key)425 Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm,
426                           bool extractable,
427                           blink::WebCryptoKeyUsageMask usage_mask,
428                           const CryptoData& modulus_data,
429                           const CryptoData& exponent_data,
430                           blink::WebCryptoKey* key) {
431   if (!modulus_data.byte_length())
432     return Status::ErrorImportRsaEmptyModulus();
433 
434   if (!exponent_data.byte_length())
435     return Status::ErrorImportRsaEmptyExponent();
436 
437   DCHECK(modulus_data.bytes());
438   DCHECK(exponent_data.bytes());
439 
440   // NSS does not provide a way to create an RSA public key directly from the
441   // modulus and exponent values, but it can import an DER-encoded ASN.1 blob
442   // with these values and create the public key from that. The code below
443   // follows the recommendation described in
444   // https://developer.mozilla.org/en-US/docs/NSS/NSS_Tech_Notes/nss_tech_note7
445 
446   // Pack the input values into a struct compatible with NSS ASN.1 encoding, and
447   // set up an ASN.1 encoder template for it.
448   struct RsaPublicKeyData {
449     SECItem modulus;
450     SECItem exponent;
451   };
452   const RsaPublicKeyData pubkey_in = {
453       {siUnsignedInteger, const_cast<unsigned char*>(modulus_data.bytes()),
454        modulus_data.byte_length()},
455       {siUnsignedInteger, const_cast<unsigned char*>(exponent_data.bytes()),
456        exponent_data.byte_length()}};
457   const SEC_ASN1Template rsa_public_key_template[] = {
458       {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RsaPublicKeyData)},
459       {
460        SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, modulus),
461       },
462       {
463        SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, exponent),
464       },
465       {
466        0,
467       }};
468 
469   // DER-encode the public key.
470   crypto::ScopedSECItem pubkey_der(
471       SEC_ASN1EncodeItem(NULL, NULL, &pubkey_in, rsa_public_key_template));
472   if (!pubkey_der)
473     return Status::OperationError();
474 
475   // Import the DER-encoded public key to create an RSA SECKEYPublicKey.
476   crypto::ScopedSECKEYPublicKey pubkey(
477       SECKEY_ImportDERPublicKey(pubkey_der.get(), CKK_RSA));
478   if (!pubkey)
479     return Status::OperationError();
480 
481   blink::WebCryptoKeyAlgorithm key_algorithm;
482   if (!CreateRsaHashedPublicKeyAlgorithm(
483           algorithm.id(),
484           algorithm.rsaHashedImportParams()->hash().id(),
485           pubkey.get(),
486           &key_algorithm)) {
487     return Status::ErrorUnexpected();
488   }
489 
490   std::vector<uint8_t> spki_data;
491   Status status = ExportKeySpkiNss(pubkey.get(), &spki_data);
492   if (status.IsError())
493     return status;
494 
495   scoped_ptr<PublicKeyNss> key_handle(
496       new PublicKeyNss(pubkey.Pass(), CryptoData(spki_data)));
497 
498   *key = blink::WebCryptoKey::create(key_handle.release(),
499                                      blink::WebCryptoKeyTypePublic,
500                                      extractable,
501                                      key_algorithm,
502                                      usage_mask);
503   return Status::Success();
504 }
505 
506 }  // namespace
507 
VerifyKeyUsagesBeforeGenerateKeyPair(blink::WebCryptoKeyUsageMask combined_usage_mask,blink::WebCryptoKeyUsageMask * public_usage_mask,blink::WebCryptoKeyUsageMask * private_usage_mask) const508 Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeGenerateKeyPair(
509     blink::WebCryptoKeyUsageMask combined_usage_mask,
510     blink::WebCryptoKeyUsageMask* public_usage_mask,
511     blink::WebCryptoKeyUsageMask* private_usage_mask) const {
512   Status status = CheckKeyCreationUsages(
513       all_public_key_usages_ | all_private_key_usages_, combined_usage_mask);
514   if (status.IsError())
515     return status;
516 
517   *public_usage_mask = combined_usage_mask & all_public_key_usages_;
518   *private_usage_mask = combined_usage_mask & all_private_key_usages_;
519 
520   return Status::Success();
521 }
522 
GenerateKeyPair(const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask public_usage_mask,blink::WebCryptoKeyUsageMask private_usage_mask,blink::WebCryptoKey * public_key,blink::WebCryptoKey * private_key) const523 Status RsaHashedAlgorithm::GenerateKeyPair(
524     const blink::WebCryptoAlgorithm& algorithm,
525     bool extractable,
526     blink::WebCryptoKeyUsageMask public_usage_mask,
527     blink::WebCryptoKeyUsageMask private_usage_mask,
528     blink::WebCryptoKey* public_key,
529     blink::WebCryptoKey* private_key) const {
530   unsigned int public_exponent = 0;
531   unsigned int modulus_length_bits = 0;
532   Status status = GetRsaKeyGenParameters(algorithm.rsaHashedKeyGenParams(),
533                                          &public_exponent,
534                                          &modulus_length_bits);
535   if (status.IsError())
536     return status;
537 
538   crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot());
539   if (!slot)
540     return Status::OperationError();
541 
542   PK11RSAGenParams rsa_gen_params;
543   rsa_gen_params.keySizeInBits = modulus_length_bits;
544   rsa_gen_params.pe = public_exponent;
545 
546   const CK_FLAGS operation_flags_mask =
547       CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY | CKF_WRAP | CKF_UNWRAP;
548 
549   // The private key must be marked as insensitive and extractable, otherwise it
550   // cannot later be exported in unencrypted form or structured-cloned.
551   const PK11AttrFlags attribute_flags =
552       PK11_ATTR_INSENSITIVE | PK11_ATTR_EXTRACTABLE;
553 
554   // Note: NSS does not generate an sec_public_key if the call below fails,
555   // so there is no danger of a leaked sec_public_key.
556   SECKEYPublicKey* sec_public_key;
557   crypto::ScopedSECKEYPrivateKey scoped_sec_private_key(
558       PK11_GenerateKeyPairWithOpFlags(slot.get(),
559                                       CKM_RSA_PKCS_KEY_PAIR_GEN,
560                                       &rsa_gen_params,
561                                       &sec_public_key,
562                                       attribute_flags,
563                                       generate_flags_,
564                                       operation_flags_mask,
565                                       NULL));
566   if (!scoped_sec_private_key)
567     return Status::OperationError();
568 
569   blink::WebCryptoKeyAlgorithm key_algorithm;
570   if (!CreateRsaHashedPublicKeyAlgorithm(
571           algorithm.id(),
572           algorithm.rsaHashedKeyGenParams()->hash().id(),
573           sec_public_key,
574           &key_algorithm)) {
575     return Status::ErrorUnexpected();
576   }
577 
578   std::vector<uint8_t> spki_data;
579   status = ExportKeySpkiNss(sec_public_key, &spki_data);
580   if (status.IsError())
581     return status;
582 
583   scoped_ptr<PublicKeyNss> public_key_handle(new PublicKeyNss(
584       crypto::ScopedSECKEYPublicKey(sec_public_key), CryptoData(spki_data)));
585 
586   std::vector<uint8_t> pkcs8_data;
587   status = ExportKeyPkcs8Nss(scoped_sec_private_key.get(), &pkcs8_data);
588   if (status.IsError())
589     return status;
590 
591   scoped_ptr<PrivateKeyNss> private_key_handle(
592       new PrivateKeyNss(scoped_sec_private_key.Pass(), CryptoData(pkcs8_data)));
593 
594   *public_key = blink::WebCryptoKey::create(public_key_handle.release(),
595                                             blink::WebCryptoKeyTypePublic,
596                                             true,
597                                             key_algorithm,
598                                             public_usage_mask);
599   *private_key = blink::WebCryptoKey::create(private_key_handle.release(),
600                                              blink::WebCryptoKeyTypePrivate,
601                                              extractable,
602                                              key_algorithm,
603                                              private_usage_mask);
604 
605   return Status::Success();
606 }
607 
VerifyKeyUsagesBeforeImportKey(blink::WebCryptoKeyFormat format,blink::WebCryptoKeyUsageMask usage_mask) const608 Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(
609     blink::WebCryptoKeyFormat format,
610     blink::WebCryptoKeyUsageMask usage_mask) const {
611   switch (format) {
612     case blink::WebCryptoKeyFormatSpki:
613       return CheckKeyCreationUsages(all_public_key_usages_, usage_mask);
614     case blink::WebCryptoKeyFormatPkcs8:
615       return CheckKeyCreationUsages(all_private_key_usages_, usage_mask);
616     case blink::WebCryptoKeyFormatJwk:
617       return CheckKeyCreationUsages(
618           all_public_key_usages_ | all_private_key_usages_, usage_mask);
619     default:
620       return Status::ErrorUnsupportedImportKeyFormat();
621   }
622 }
623 
ImportKeyPkcs8(const CryptoData & key_data,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,blink::WebCryptoKey * key) const624 Status RsaHashedAlgorithm::ImportKeyPkcs8(
625     const CryptoData& key_data,
626     const blink::WebCryptoAlgorithm& algorithm,
627     bool extractable,
628     blink::WebCryptoKeyUsageMask usage_mask,
629     blink::WebCryptoKey* key) const {
630   Status status = NssSupportsRsaPrivateKeyImport();
631   if (status.IsError())
632     return status;
633 
634   if (!key_data.byte_length())
635     return Status::ErrorImportEmptyKeyData();
636 
637   // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 PKCS#8
638   // private key info object.
639   SECItem pki_der = MakeSECItemForBuffer(key_data);
640 
641   SECKEYPrivateKey* seckey_private_key = NULL;
642   crypto::ScopedPK11Slot slot(PK11_GetInternalSlot());
643   if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot.get(),
644                                                &pki_der,
645                                                NULL,    // nickname
646                                                NULL,    // publicValue
647                                                false,   // isPerm
648                                                false,   // isPrivate
649                                                KU_ALL,  // usage
650                                                &seckey_private_key,
651                                                NULL) != SECSuccess) {
652     return Status::DataError();
653   }
654   DCHECK(seckey_private_key);
655   crypto::ScopedSECKEYPrivateKey private_key(seckey_private_key);
656 
657   const KeyType sec_key_type = SECKEY_GetPrivateKeyType(private_key.get());
658   if (sec_key_type != rsaKey)
659     return Status::DataError();
660 
661   blink::WebCryptoKeyAlgorithm key_algorithm;
662   if (!CreateRsaHashedPrivateKeyAlgorithm(
663           algorithm.id(),
664           algorithm.rsaHashedImportParams()->hash().id(),
665           private_key.get(),
666           &key_algorithm)) {
667     return Status::ErrorUnexpected();
668   }
669 
670   // TODO(eroman): This is probably going to be the same as the input.
671   std::vector<uint8_t> pkcs8_data;
672   status = ExportKeyPkcs8Nss(private_key.get(), &pkcs8_data);
673   if (status.IsError())
674     return status;
675 
676   scoped_ptr<PrivateKeyNss> key_handle(
677       new PrivateKeyNss(private_key.Pass(), CryptoData(pkcs8_data)));
678 
679   *key = blink::WebCryptoKey::create(key_handle.release(),
680                                      blink::WebCryptoKeyTypePrivate,
681                                      extractable,
682                                      key_algorithm,
683                                      usage_mask);
684 
685   return Status::Success();
686 }
687 
ImportKeySpki(const CryptoData & key_data,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,blink::WebCryptoKey * key) const688 Status RsaHashedAlgorithm::ImportKeySpki(
689     const CryptoData& key_data,
690     const blink::WebCryptoAlgorithm& algorithm,
691     bool extractable,
692     blink::WebCryptoKeyUsageMask usage_mask,
693     blink::WebCryptoKey* key) const {
694   if (!key_data.byte_length())
695     return Status::ErrorImportEmptyKeyData();
696 
697   // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject
698   // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo.
699   SECItem spki_item = MakeSECItemForBuffer(key_data);
700   const ScopedCERTSubjectPublicKeyInfo spki(
701       SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
702   if (!spki)
703     return Status::DataError();
704 
705   crypto::ScopedSECKEYPublicKey sec_public_key(
706       SECKEY_ExtractPublicKey(spki.get()));
707   if (!sec_public_key)
708     return Status::DataError();
709 
710   const KeyType sec_key_type = SECKEY_GetPublicKeyType(sec_public_key.get());
711   if (sec_key_type != rsaKey)
712     return Status::DataError();
713 
714   blink::WebCryptoKeyAlgorithm key_algorithm;
715   if (!CreateRsaHashedPublicKeyAlgorithm(
716           algorithm.id(),
717           algorithm.rsaHashedImportParams()->hash().id(),
718           sec_public_key.get(),
719           &key_algorithm)) {
720     return Status::ErrorUnexpected();
721   }
722 
723   // TODO(eroman): This is probably going to be the same as the input.
724   std::vector<uint8_t> spki_data;
725   Status status = ExportKeySpkiNss(sec_public_key.get(), &spki_data);
726   if (status.IsError())
727     return status;
728 
729   scoped_ptr<PublicKeyNss> key_handle(
730       new PublicKeyNss(sec_public_key.Pass(), CryptoData(spki_data)));
731 
732   *key = blink::WebCryptoKey::create(key_handle.release(),
733                                      blink::WebCryptoKeyTypePublic,
734                                      extractable,
735                                      key_algorithm,
736                                      usage_mask);
737 
738   return Status::Success();
739 }
740 
ExportKeyPkcs8(const blink::WebCryptoKey & key,std::vector<uint8_t> * buffer) const741 Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key,
742                                           std::vector<uint8_t>* buffer) const {
743   if (key.type() != blink::WebCryptoKeyTypePrivate)
744     return Status::ErrorUnexpectedKeyType();
745   *buffer = PrivateKeyNss::Cast(key)->pkcs8_data();
746   return Status::Success();
747 }
748 
ExportKeySpki(const blink::WebCryptoKey & key,std::vector<uint8_t> * buffer) const749 Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key,
750                                          std::vector<uint8_t>* buffer) const {
751   if (key.type() != blink::WebCryptoKeyTypePublic)
752     return Status::ErrorUnexpectedKeyType();
753   *buffer = PublicKeyNss::Cast(key)->spki_data();
754   return Status::Success();
755 }
756 
ImportKeyJwk(const CryptoData & key_data,const blink::WebCryptoAlgorithm & algorithm,bool extractable,blink::WebCryptoKeyUsageMask usage_mask,blink::WebCryptoKey * key) const757 Status RsaHashedAlgorithm::ImportKeyJwk(
758     const CryptoData& key_data,
759     const blink::WebCryptoAlgorithm& algorithm,
760     bool extractable,
761     blink::WebCryptoKeyUsageMask usage_mask,
762     blink::WebCryptoKey* key) const {
763   const char* jwk_algorithm =
764       GetJwkAlgorithm(algorithm.rsaHashedImportParams()->hash().id());
765 
766   if (!jwk_algorithm)
767     return Status::ErrorUnexpected();
768 
769   JwkRsaInfo jwk;
770   Status status =
771       ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usage_mask, &jwk);
772   if (status.IsError())
773     return status;
774 
775   // Once the key type is known, verify the usages.
776   status = CheckKeyCreationUsages(
777       jwk.is_private_key ? all_private_key_usages_ : all_public_key_usages_,
778       usage_mask);
779   if (status.IsError())
780     return Status::ErrorCreateKeyBadUsages();
781 
782   return jwk.is_private_key
783              ? ImportRsaPrivateKey(algorithm, extractable, usage_mask, jwk, key)
784              : ImportRsaPublicKey(algorithm,
785                                   extractable,
786                                   usage_mask,
787                                   CryptoData(jwk.n),
788                                   CryptoData(jwk.e),
789                                   key);
790 }
791 
ExportKeyJwk(const blink::WebCryptoKey & key,std::vector<uint8_t> * buffer) const792 Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key,
793                                         std::vector<uint8_t>* buffer) const {
794   const char* jwk_algorithm =
795       GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id());
796 
797   if (!jwk_algorithm)
798     return Status::ErrorUnexpected();
799 
800   switch (key.type()) {
801     case blink::WebCryptoKeyTypePublic: {
802       SECKEYPublicKey* nss_key = PublicKeyNss::Cast(key)->key();
803       if (nss_key->keyType != rsaKey)
804         return Status::ErrorUnsupported();
805 
806       WriteRsaPublicKeyJwk(SECItemToCryptoData(nss_key->u.rsa.modulus),
807                            SECItemToCryptoData(nss_key->u.rsa.publicExponent),
808                            jwk_algorithm,
809                            key.extractable(),
810                            key.usages(),
811                            buffer);
812 
813       return Status::Success();
814     }
815 
816     case blink::WebCryptoKeyTypePrivate: {
817       SECKEYPrivateKey* nss_key = PrivateKeyNss::Cast(key)->key();
818       RSAPrivateKey key_props = {};
819       scoped_ptr<RSAPrivateKey, FreeRsaPrivateKey> free_private_key(&key_props);
820 
821       if (!InitRSAPrivateKey(nss_key, &key_props))
822         return Status::OperationError();
823 
824       WriteRsaPrivateKeyJwk(SECItemToCryptoData(key_props.modulus),
825                             SECItemToCryptoData(key_props.public_exponent),
826                             SECItemToCryptoData(key_props.private_exponent),
827                             SECItemToCryptoData(key_props.prime1),
828                             SECItemToCryptoData(key_props.prime2),
829                             SECItemToCryptoData(key_props.exponent1),
830                             SECItemToCryptoData(key_props.exponent2),
831                             SECItemToCryptoData(key_props.coefficient),
832                             jwk_algorithm,
833                             key.extractable(),
834                             key.usages(),
835                             buffer);
836 
837       return Status::Success();
838     }
839     default:
840       return Status::ErrorUnexpected();
841   }
842 }
843 
844 }  // namespace webcrypto
845 
846 }  // namespace content
847