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 478c7aaff73fbb6c873cdcab6465c6de80a146ec..5192cf4f7989c353bd866030ef72029b2bbae10f 100644 |
--- a/content/child/webcrypto/platform_crypto_nss.cc |
+++ b/content/child/webcrypto/platform_crypto_nss.cc |
@@ -7,6 +7,7 @@ |
#include <cryptohi.h> |
#include <pk11pub.h> |
#include <sechash.h> |
+#include <secoid.h> |
#include <vector> |
@@ -552,6 +553,116 @@ Status DoUnwrapSymKeyAesKw(const CryptoData& wrapped_key_data, |
return Status::Success(); |
} |
+// From PKCS#1 [http://tools.ietf.org/html/rfc3447]: |
+// |
+// RSAPrivateKey ::= SEQUENCE { |
+// version Version, |
+// modulus INTEGER, -- n |
+// publicExponent INTEGER, -- e |
+// privateExponent INTEGER, -- d |
+// prime1 INTEGER, -- p |
+// prime2 INTEGER, -- q |
+// exponent1 INTEGER, -- d mod (p-1) |
+// exponent2 INTEGER, -- d mod (q-1) |
+// coefficient INTEGER, -- (inverse of q) mod p |
+// otherPrimeInfos OtherPrimeInfos OPTIONAL |
+// } |
+// |
+// Note that otherPrimeInfos is only applicable for version=1. Since NSS |
+// doesn't use multi-prime can safely use version=0. |
+struct RSAPrivateKey { |
+ SECItem version; |
+ SECItem modulus; |
+ SECItem public_exponent; |
+ SECItem private_exponent; |
+ SECItem prime1; |
+ SECItem prime2; |
+ SECItem exponent1; |
+ SECItem exponent2; |
+ SECItem coefficient; |
+}; |
+ |
+const SEC_ASN1Template RSAPrivateKeyTemplate[] = { |
+ {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RSAPrivateKey)}, |
+ {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, version)}, |
+ {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, modulus)}, |
+ {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, public_exponent)}, |
+ {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, private_exponent)}, |
+ {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, prime1)}, |
+ {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, prime2)}, |
+ {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, exponent1)}, |
+ {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, exponent2)}, |
+ {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, coefficient)}, |
+ {0}}; |
+ |
+// On success |value| will be filled with data which must be freed by |
+// SECITEM_FreeItem(value, PR_FALSE); |
+bool ReadUint(SECKEYPrivateKey* key, |
+ CK_ATTRIBUTE_TYPE attribute, |
+ SECItem* value) { |
+ SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, attribute, value); |
+ |
+ // PK11_ReadRawAttribute() returns items of type siBuffer. However in order |
+ // for the ASN.1 encoding to be correct, the items must be of type |
+ // siUnsignedInteger. |
+ value->type = siUnsignedInteger; |
+ |
+ return rv == SECSuccess; |
+} |
+ |
+// Fills |out| with the RSA private key properties. Returns true on success. |
+// Regardless of the return value, the caller must invoke FreeRSAPrivateKey() |
+// to free up any allocated memory. |
+// |
+// The passed in RSAPrivateKey must be zero-initialized. |
+bool InitRSAPrivateKey(SECKEYPrivateKey* key, RSAPrivateKey* out) { |
+ if (key->keyType != rsaKey) |
+ return false; |
+ |
+ // Everything should be zero-ed out. These are just some spot checks. |
+ DCHECK(!out->version.data); |
+ DCHECK(!out->version.len); |
+ DCHECK(!out->modulus.data); |
+ DCHECK(!out->modulus.len); |
+ |
+ // Always use version=0 since not using multi-prime. |
+ if (!SEC_ASN1EncodeInteger(NULL, &out->version, 0)) |
+ return false; |
+ |
+ if (!ReadUint(key, CKA_MODULUS, &out->modulus)) |
+ return false; |
+ if (!ReadUint(key, CKA_PUBLIC_EXPONENT, &out->public_exponent)) |
+ return false; |
+ if (!ReadUint(key, CKA_PRIVATE_EXPONENT, &out->private_exponent)) |
+ return false; |
+ if (!ReadUint(key, CKA_PRIME_1, &out->prime1)) |
+ return false; |
+ if (!ReadUint(key, CKA_PRIME_2, &out->prime2)) |
+ return false; |
+ if (!ReadUint(key, CKA_EXPONENT_1, &out->exponent1)) |
+ return false; |
+ if (!ReadUint(key, CKA_EXPONENT_2, &out->exponent2)) |
+ return false; |
+ if (!ReadUint(key, CKA_COEFFICIENT, &out->coefficient)) |
+ return false; |
+ |
+ return true; |
+} |
+ |
+struct FreeRsaPrivateKey { |
+ void operator()(RSAPrivateKey* out) { |
+ SECITEM_FreeItem(&out->version, PR_FALSE); |
+ SECITEM_FreeItem(&out->modulus, PR_FALSE); |
+ SECITEM_FreeItem(&out->public_exponent, PR_FALSE); |
+ SECITEM_FreeItem(&out->private_exponent, PR_FALSE); |
+ SECITEM_FreeItem(&out->prime1, PR_FALSE); |
+ SECITEM_FreeItem(&out->prime2, PR_FALSE); |
+ SECITEM_FreeItem(&out->exponent1, PR_FALSE); |
+ SECITEM_FreeItem(&out->exponent2, PR_FALSE); |
+ SECITEM_FreeItem(&out->coefficient, PR_FALSE); |
+ } |
+}; |
+ |
} // namespace |
Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, |
@@ -695,6 +806,57 @@ Status ExportKeySpki(PublicKey* key, blink::WebArrayBuffer* buffer) { |
return Status::Success(); |
} |
+Status ExportKeyPkcs8(PrivateKey* key, |
+ const blink::WebCryptoKeyAlgorithm& key_algorithm, |
+ blink::WebArrayBuffer* buffer) { |
+ // TODO(eroman): Support other RSA key types as they are added to Blink. |
+ if (key_algorithm.id() != blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 && |
+ key_algorithm.id() != blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5) |
+ return Status::ErrorUnsupported(); |
+ |
+ const SECOidTag algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION; |
+ const int kPrivateKeyInfoVersion = 0; |
+ |
+ SECKEYPrivateKeyInfo private_key_info = {}; |
+ RSAPrivateKey rsa_private_key = {}; |
+ scoped_ptr<RSAPrivateKey, FreeRsaPrivateKey> free_private_key( |
+ &rsa_private_key); |
+ |
+ if (!InitRSAPrivateKey(key->key(), &rsa_private_key)) |
+ return Status::Error(); |
+ |
+ crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); |
+ if (!arena.get()) |
+ return Status::Error(); |
+ |
+ if (!SEC_ASN1EncodeItem(arena.get(), |
+ &private_key_info.privateKey, |
+ &rsa_private_key, |
+ RSAPrivateKeyTemplate)) |
+ return Status::Error(); |
+ |
+ if (SECSuccess != |
+ SECOID_SetAlgorithmID( |
+ arena.get(), &private_key_info.algorithm, algorithm, NULL)) |
+ return Status::Error(); |
+ |
+ if (!SEC_ASN1EncodeInteger( |
+ arena.get(), &private_key_info.version, kPrivateKeyInfoVersion)) |
+ return Status::Error(); |
+ |
+ crypto::ScopedSECItem encoded_key( |
+ SEC_ASN1EncodeItem(NULL, |
+ NULL, |
+ &private_key_info, |
+ SEC_ASN1_GET(SECKEY_PrivateKeyInfoTemplate))); |
+ |
+ if (!encoded_key.get()) |
+ return Status::Error(); |
+ |
+ *buffer = CreateArrayBuffer(encoded_key->data, encoded_key->len); |
+ return Status::Success(); |
+} |
+ |
Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm, |
const CryptoData& key_data, |
bool extractable, |