Index: content/child/webcrypto/platform_crypto_openssl.cc |
diff --git a/content/child/webcrypto/platform_crypto_openssl.cc b/content/child/webcrypto/platform_crypto_openssl.cc |
index 1235e51f2cc9d0bb57529f9133409f4293a78e98..e1a5f029e79c946665a82c26adb9bcd56dd46643 100644 |
--- a/content/child/webcrypto/platform_crypto_openssl.cc |
+++ b/content/child/webcrypto/platform_crypto_openssl.cc |
@@ -8,6 +8,7 @@ |
#include <openssl/aes.h> |
#include <openssl/evp.h> |
#include <openssl/hmac.h> |
+#include <openssl/pkcs12.h> |
#include <openssl/rand.h> |
#include <openssl/sha.h> |
@@ -49,6 +50,70 @@ class SymKey : public Key { |
DISALLOW_COPY_AND_ASSIGN(SymKey); |
}; |
+class PublicKey : public Key { |
+ public: |
+ // Takes ownership of |key|. |
+ // TODO(eroman): use Pass() semantics. |
+ static Status Create(EVP_PKEY* key, |
+ const blink::WebCryptoKeyAlgorithm& algorithm, |
+ scoped_ptr<PublicKey>* out) { |
+ out->reset(new PublicKey(key)); |
+ return ExportKeySpki(out->get(), &(*out)->serialized_key_); |
+ } |
+ |
+ EVP_PKEY* key() { return key_.get(); } |
+ |
+ virtual SymKey* AsSymKey() OVERRIDE { return NULL; } |
+ virtual PublicKey* AsPublicKey() OVERRIDE { return this; } |
+ virtual PrivateKey* AsPrivateKey() OVERRIDE { return NULL; } |
+ |
+ virtual bool ThreadSafeSerializeForClone( |
+ blink::WebVector<uint8>* key_data) OVERRIDE { |
+ key_data->assign(Uint8VectorStart(serialized_key_), serialized_key_.size()); |
+ return true; |
+ } |
+ |
+ private: |
+ explicit PublicKey(EVP_PKEY* key) : key_(key) {} |
+ |
+ crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> key_; |
+ std::vector<uint8> serialized_key_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PublicKey); |
+}; |
+ |
+class PrivateKey : public Key { |
+ public: |
+ // Takes ownership of |key|. |
+ // TODO(eroman): use Pass() semantics. |
+ static Status Create(EVP_PKEY* key, |
+ const blink::WebCryptoKeyAlgorithm& algorithm, |
+ scoped_ptr<PrivateKey>* out) { |
+ out->reset(new PrivateKey(key)); |
+ return ExportKeyPkcs8(out->get(), algorithm, &(*out)->serialized_key_); |
+ } |
+ |
+ EVP_PKEY* key() { return key_.get(); } |
+ |
+ virtual SymKey* AsSymKey() OVERRIDE { return NULL; } |
+ virtual PublicKey* AsPublicKey() OVERRIDE { return NULL; } |
+ virtual PrivateKey* AsPrivateKey() OVERRIDE { return this; } |
+ |
+ virtual bool ThreadSafeSerializeForClone( |
+ blink::WebVector<uint8>* key_data) OVERRIDE { |
+ key_data->assign(Uint8VectorStart(serialized_key_), serialized_key_.size()); |
+ return true; |
+ } |
+ |
+ private: |
+ explicit PrivateKey(EVP_PKEY* key) : key_(key) {} |
+ |
+ crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> key_; |
+ std::vector<uint8> serialized_key_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PrivateKey); |
+}; |
+ |
namespace { |
const EVP_CIPHER* GetAESCipherByKeyLength(unsigned int key_length_bytes) { |
@@ -153,6 +218,55 @@ Status AesCbcEncryptDecrypt(EncryptOrDecrypt mode, |
return Status::Success(); |
} |
+// Creates a blink::WebCryptoAlgorithm having the modulus length and public |
+// exponent of |key|. |
+Status CreateRsaHashedKeyAlgorithm( |
+ blink::WebCryptoAlgorithmId rsa_algorithm, |
+ blink::WebCryptoAlgorithmId hash_algorithm, |
+ EVP_PKEY* key, |
+ blink::WebCryptoKeyAlgorithm* key_algorithm) { |
+ DCHECK(IsAlgorithmRsa(rsa_algorithm)); |
+ DCHECK(EVP_PKEY_id(key) == EVP_PKEY_RSA); |
Ryan Sleevi
2014/06/27 01:43:25
DCHECK_EQ
eroman
2014/06/27 02:12:47
Done.
|
+ |
+ crypto::ScopedOpenSSL<RSA, RSA_free> rsa(EVP_PKEY_get1_RSA(key)); |
+ if (!rsa.get()) |
+ return Status::ErrorUnexpected(); |
+ |
+ unsigned int modulus_length_bits = RSA_size(rsa.get()) * 8; |
+ |
+ // Convert the public exponent to big-endian representation. |
+ std::vector<uint8> e(BN_num_bytes(rsa.get()->e)); |
+ if (e.size() == 0) |
+ return Status::ErrorUnexpected(); |
+ if (static_cast<int>(e.size()) != BN_bn2bin(rsa.get()->e, &e[0])) |
Ryan Sleevi
2014/06/27 01:43:25
I think these may differ dependent on leading bits
eroman
2014/06/27 02:12:47
Looks like at least in the current implementation,
|
+ return Status::ErrorUnexpected(); |
+ |
+ *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( |
+ rsa_algorithm, modulus_length_bits, &e[0], e.size(), hash_algorithm); |
+ |
+ return Status::Success(); |
+} |
+ |
+// Verifies that |key| is consistent with the input algorithm id, and creates a |
+// blink::WebCryptoKeyAlgorithm describing the key. |
+// Returns Status::Success() on success and sets |*key_algorithm|. |
+Status ValidateKeyTypeAndCreateKeyAlgorithm( |
+ const blink::WebCryptoAlgorithm& algorithm, |
+ EVP_PKEY* key, |
+ blink::WebCryptoKeyAlgorithm* key_algorithm) { |
+ if (IsAlgorithmRsa(algorithm.id())) { |
+ if (EVP_PKEY_id(key) != EVP_PKEY_RSA) |
+ return Status::DataError(); // Data did not define an RSA key. |
+ return CreateRsaHashedKeyAlgorithm(algorithm.id(), |
+ GetInnerHashAlgorithm(algorithm).id(), |
+ key, |
+ key_algorithm); |
+ return Status::Success(); |
+ } |
+ |
+ return Status::ErrorUnsupported(); |
+} |
+ |
} // namespace |
class DigestorOpenSSL : public blink::WebCryptoDigestor { |
@@ -449,8 +563,36 @@ Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm, |
bool extractable, |
blink::WebCryptoKeyUsageMask usage_mask, |
blink::WebCryptoKey* key) { |
- // TODO(eroman): http://crbug.com/267888 |
- return Status::ErrorUnsupported(); |
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
+ |
+ crypto::ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new_mem_buf( |
+ const_cast<uint8*>(key_data.bytes()), key_data.byte_length())); |
+ if (!bio.get()) |
+ return Status::ErrorUnexpected(); |
+ |
+ crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> public_key( |
+ d2i_PUBKEY_bio(bio.get(), NULL)); |
+ if (!public_key.get()) |
+ return Status::DataError(); |
+ |
+ blink::WebCryptoKeyAlgorithm key_algorithm; |
+ Status status = ValidateKeyTypeAndCreateKeyAlgorithm( |
+ algorithm, public_key.get(), &key_algorithm); |
+ if (status.IsError()) |
+ return status; |
+ |
+ scoped_ptr<PublicKey> key_handle; |
+ status = PublicKey::Create(public_key.release(), key_algorithm, &key_handle); |
+ if (status.IsError()) |
+ return status; |
+ |
+ *key = blink::WebCryptoKey::create(key_handle.release(), |
+ blink::WebCryptoKeyTypePublic, |
+ extractable, |
+ key_algorithm, |
+ usage_mask); |
+ |
+ return Status::Success(); |
} |
Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm, |
@@ -458,20 +600,75 @@ Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm, |
bool extractable, |
blink::WebCryptoKeyUsageMask usage_mask, |
blink::WebCryptoKey* key) { |
- // TODO(eroman): http://crbug.com/267888 |
- return Status::ErrorUnsupported(); |
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
+ |
+ crypto::ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new_mem_buf( |
+ const_cast<uint8*>(key_data.bytes()), key_data.byte_length())); |
+ if (!bio.get()) |
+ return Status::ErrorUnexpected(); |
+ |
+ crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> p8inf( |
Ryan Sleevi
2014/06/27 01:43:24
// TODO reminder - need to validate the EVP data a
eroman
2014/06/27 02:12:47
Done. I have added comments linking to the corresp
|
+ d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL)); |
+ if (!p8inf.get()) |
+ return Status::DataError(); |
+ |
+ crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> private_key( |
+ EVP_PKCS82PKEY(p8inf.get())); |
+ if (!private_key.get()) |
+ return Status::DataError(); |
+ |
+ blink::WebCryptoKeyAlgorithm key_algorithm; |
+ Status status = ValidateKeyTypeAndCreateKeyAlgorithm( |
+ algorithm, private_key.get(), &key_algorithm); |
+ if (status.IsError()) |
+ return status; |
+ |
+ scoped_ptr<PrivateKey> key_handle; |
+ status = |
+ PrivateKey::Create(private_key.release(), key_algorithm, &key_handle); |
+ if (status.IsError()) |
+ return status; |
+ |
+ *key = blink::WebCryptoKey::create(key_handle.release(), |
+ blink::WebCryptoKeyTypePrivate, |
+ extractable, |
+ key_algorithm, |
+ usage_mask); |
+ |
+ return Status::Success(); |
} |
Status ExportKeySpki(PublicKey* key, std::vector<uint8>* buffer) { |
- // TODO(eroman): http://crbug.com/267888 |
- return Status::ErrorUnsupported(); |
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
+ crypto::ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new(BIO_s_mem())); |
+ if (!i2d_PUBKEY_bio(bio.get(), key->key())) |
+ return Status::ErrorUnexpected(); |
+ |
+ uint8* data = NULL; |
+ long len = BIO_get_mem_data(bio.get(), &data); |
+ if (!data || len < 0) |
+ return Status::ErrorUnexpected(); |
+ |
+ buffer->assign(data, data + len); |
+ return Status::Success(); |
} |
Status ExportKeyPkcs8(PrivateKey* key, |
const blink::WebCryptoKeyAlgorithm& key_algorithm, |
std::vector<uint8>* buffer) { |
- // TODO(eroman): http://crbug.com/267888 |
- return Status::ErrorUnsupported(); |
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
+ crypto::ScopedOpenSSL<BIO, BIO_free_all> bio(BIO_new(BIO_s_mem())); |
+ |
+ if (!i2d_PKCS8PrivateKeyInfo_bio(bio.get(), key->key())) |
+ return Status::ErrorUnexpected(); |
+ |
+ uint8* data = NULL; |
+ long len = BIO_get_mem_data(bio.get(), &data); |
+ if (!data || len < 0) |
+ return Status::ErrorUnexpected(); |
+ |
+ buffer->assign(data, data + len); |
+ return Status::Success(); |
} |
Status ExportRsaPublicKey(PublicKey* key, |