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