Index: content/child/webcrypto/platform_crypto_nss.cc |
diff --git a/content/child/webcrypto/platform_crypto_nss.cc b/content/child/webcrypto/platform_crypto_nss.cc |
index 3d0e9923a69f4185b2bdb2e4919fa2ff61e25204..dab8582753cf2329dedec349bba9e40e92c160f3 100644 |
--- a/content/child/webcrypto/platform_crypto_nss.cc |
+++ b/content/child/webcrypto/platform_crypto_nss.cc |
@@ -1717,6 +1717,39 @@ Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, |
AddOptionalAttribute(CKA_PUBLIC_EXPONENT, public_exponent, &key_template); |
AddOptionalAttribute(CKA_PRIVATE_EXPONENT, private_exponent, &key_template); |
+ // Manufacture a CKA_ID so the created key can be retrieved later as a |
+ // SECKEYPrivateKey using FindKeyByKeyID(). Unfortunately there isn't a more |
+ // direct way to do this in NSS. |
+ // |
+ // For consistency with other NSS key creation methods, set the CKA_ID to |
+ // PK11_MakeIDFromPubKey(). There are some problems with |
+ // this approach: |
+ // |
+ // (1) Prior to NSS 3.16.2, there is no parameter validation when creating |
+ // private keys. It is therefore possible to construct a key using the |
+ // known public modulus, and where all the other parameters are bogus. |
+ // FindKeyByKeyID() returns the first key matching the ID. So this would |
+ // effectively allow an attacker to retrieve a private key of their |
+ // choice. |
+ // TODO(eroman): Once NSS rolls and this is fixed, disallow RSA key |
+ // import on older versions of NSS. |
+ // http://crbug.com/378315 |
+ // |
+ // (2) The ID space is shared by different key types. So theoretically |
+ // possible to retrieve a key of the wrong type which has a matching |
+ // CKA_ID. In practice I am told this is not likely except for small key |
+ // sizes, since would require constructing keys with the same public |
+ // data. |
+ // |
+ // (3) FindKeyByKeyID() doesn't necessarily return the object that was just |
+ // created by CreateGenericObject. If the pre-existing key was |
+ // provisioned with flags incompatible with WebCrypto (for instance |
+ // marked sensitive) then this will break things. |
+ SECItem modulus_item = MakeSECItemForBuffer(CryptoData(modulus)); |
+ crypto::ScopedSECItem object_id(PK11_MakeIDFromPubKey(&modulus_item)); |
+ AddOptionalAttribute( |
+ CKA_ID, CryptoData(object_id->data, object_id->len), &key_template); |
+ |
// Optional properties (all of these will have been specified or none). |
AddOptionalAttribute(CKA_PRIME_1, prime1, &key_template); |
AddOptionalAttribute(CKA_PRIME_2, prime2, &key_template); |
@@ -1732,17 +1765,13 @@ Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, |
if (!key_object) |
return Status::OperationError(); |
- // The ID isn't guaranteed to be set by PKCS#11. However it is by softtoken so |
- // this should work. |
- SECItem object_id = {}; |
- if (PK11_ReadRawAttribute( |
- PK11_TypeGeneric, key_object.get(), CKA_ID, &object_id) != SECSuccess) |
- return Status::OperationError(); |
+ crypto::ScopedSECKEYPrivateKey private_key_tmp( |
+ PK11_FindKeyByKeyID(slot.get(), object_id.get(), NULL)); |
+ // PK11_FindKeyByKeyID() may return a handle to an existing key, rather than |
+ // the object created by PK11_CreateGenericObject(). |
crypto::ScopedSECKEYPrivateKey private_key( |
- PK11_FindKeyByKeyID(slot.get(), &object_id, NULL)); |
- |
- SECITEM_FreeItem(&object_id, PR_FALSE); |
+ SECKEY_CopyPrivateKey(private_key_tmp.get())); |
if (!private_key) |
return Status::OperationError(); |