Chromium Code Reviews| 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, |