| Index: net/android/keystore_openssl.cc
|
| diff --git a/net/android/keystore_openssl.cc b/net/android/keystore_openssl.cc
|
| index e2e53d7b10e93a774238e212b7ab8aedcdea9d16..dbb3b1c73f2394bbb697e3f2ba79d1fe54cf88cf 100644
|
| --- a/net/android/keystore_openssl.cc
|
| +++ b/net/android/keystore_openssl.cc
|
| @@ -28,6 +28,7 @@
|
| #include "crypto/openssl_util.h"
|
| #include "crypto/scoped_openssl_types.h"
|
| #include "net/android/keystore.h"
|
| +#include "net/android/legacy_openssl.h"
|
| #include "net/ssl/ssl_client_cert_type.h"
|
|
|
| // IMPORTANT NOTE: The following code will currently only work when used
|
| @@ -96,6 +97,7 @@
|
| // here, which saves a lot of complexity.
|
|
|
| using base::android::ScopedJavaGlobalRef;
|
| +using base::android::ScopedJavaLocalRef;
|
|
|
| namespace net {
|
| namespace android {
|
| @@ -109,6 +111,11 @@ typedef crypto::ScopedOpenSSL<EC_GROUP, EC_GROUP_free>::Type ScopedEC_GROUP;
|
| // all other method pointers are either stubs returning errors, or no-ops.
|
| // See <openssl/rsa.h> for exact declaration of RSA_METHOD.
|
|
|
| +struct RsaAppData {
|
| + jobject private_key;
|
| + AndroidRSA* legacy_rsa;
|
| +};
|
| +
|
| int RsaMethodPubEnc(int flen,
|
| const unsigned char* from,
|
| unsigned char* to,
|
| @@ -151,18 +158,38 @@ int RsaMethodPrivEnc(int flen,
|
| }
|
|
|
| // Retrieve private key JNI reference.
|
| - jobject private_key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
|
| - if (!private_key) {
|
| + RsaAppData* app_data = static_cast<RsaAppData*>(RSA_get_app_data(rsa));
|
| + if (!app_data || !app_data->private_key) {
|
| LOG(WARNING) << "Null JNI reference passed to RsaMethodPrivEnc!";
|
| RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
|
| return -1;
|
| }
|
|
|
| + // Pre-4.2 legacy codepath.
|
| + if (app_data->legacy_rsa) {
|
| + int ret = app_data->legacy_rsa->meth->rsa_priv_enc(
|
| + flen, from, to, app_data->legacy_rsa, ANDROID_RSA_PKCS1_PADDING);
|
| + if (ret < 0) {
|
| + LOG(WARNING) << "Could not sign message in RsaMethodPrivEnc!";
|
| + // System OpenSSL will use a separate error queue, so it is still
|
| + // necessary to push a new error.
|
| + //
|
| + // TODO(davidben): It would be good to also clear the system error queue
|
| + // if there were some way to convince Java to do it. (Without going
|
| + // through Java, it's difficult to get a handle on a system OpenSSL
|
| + // function; dlopen loads a second copy.)
|
| + RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
|
| + return -1;
|
| + }
|
| + return ret;
|
| + }
|
| +
|
| base::StringPiece from_piece(reinterpret_cast<const char*>(from), flen);
|
| std::vector<uint8> result;
|
| // For RSA keys, this function behaves as RSA_private_encrypt with
|
| // PKCS#1 padding.
|
| - if (!RawSignDigestWithPrivateKey(private_key, from_piece, &result)) {
|
| + if (!RawSignDigestWithPrivateKey(app_data->private_key,
|
| + from_piece, &result)) {
|
| LOG(WARNING) << "Could not sign message in RsaMethodPrivEnc!";
|
| RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
|
| return -1;
|
| @@ -202,10 +229,11 @@ int RsaMethodInit(RSA* rsa) {
|
| int RsaMethodFinish(RSA* rsa) {
|
| // Ensure the global JNI reference created with this wrapper is
|
| // properly destroyed with it.
|
| - jobject key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
|
| - if (key != NULL) {
|
| + RsaAppData* app_data = static_cast<RsaAppData*>(RSA_get_app_data(rsa));
|
| + if (app_data != NULL) {
|
| RSA_set_app_data(rsa, NULL);
|
| - ReleaseKey(key);
|
| + ReleaseKey(app_data->private_key);
|
| + delete app_data;
|
| }
|
| // Actual return value is ignored by OpenSSL. There are no docs
|
| // explaining what this is supposed to be.
|
| @@ -272,20 +300,26 @@ bool SwapBigNumPtrFromBytes(const std::vector<uint8>& new_bytes,
|
|
|
| // Setup an EVP_PKEY to wrap an existing platform RSA PrivateKey object.
|
| // |private_key| is the JNI reference (local or global) to the object.
|
| +// |legacy_rsa|, if non-NULL, is a pointer to the system OpenSSL RSA object
|
| +// backing |private_key|. This parameter is only used for Android < 4.2 to
|
| +// implement key operations not exposed by the platform.
|
| // |pkey| is the EVP_PKEY to setup as a wrapper.
|
| // Returns true on success, false otherwise.
|
| // On success, this creates a new global JNI reference to the object
|
| // that is owned by and destroyed with the EVP_PKEY. I.e. caller can
|
| // free |private_key| after the call.
|
| -// IMPORTANT: The EVP_PKEY will *only* work on Android >= 4.2. For older
|
| -// platforms, use GetRsaLegacyKey() instead.
|
| -bool GetRsaPkeyWrapper(jobject private_key, EVP_PKEY* pkey) {
|
| +bool GetRsaPkeyWrapper(jobject private_key,
|
| + AndroidRSA* legacy_rsa,
|
| + EVP_PKEY* pkey) {
|
| crypto::ScopedRSA rsa(RSA_new());
|
| RSA_set_method(rsa.get(), &android_rsa_method);
|
|
|
| // HACK: RSA_size() doesn't work with custom RSA_METHODs. To ensure that
|
| // it will return the right value, set the 'n' field of the RSA object
|
| // to match the private key's modulus.
|
| + //
|
| + // TODO(davidben): After switching to BoringSSL, consider making RSA_size call
|
| + // into an RSA_METHOD hook.
|
| std::vector<uint8> modulus;
|
| if (!GetRSAKeyModulus(private_key, &modulus)) {
|
| LOG(ERROR) << "Failed to get private key modulus";
|
| @@ -302,7 +336,10 @@ bool GetRsaPkeyWrapper(jobject private_key, EVP_PKEY* pkey) {
|
| LOG(ERROR) << "Could not create global JNI reference";
|
| return false;
|
| }
|
| - RSA_set_app_data(rsa.get(), global_key.Release());
|
| + RsaAppData* app_data = new RsaAppData();
|
| + app_data->private_key = global_key.Release();
|
| + app_data->legacy_rsa = legacy_rsa;
|
| + RSA_set_app_data(rsa.get(), app_data);
|
| EVP_PKEY_assign_RSA(pkey, rsa.release());
|
| return true;
|
| }
|
| @@ -319,30 +356,28 @@ bool GetRsaPkeyWrapper(jobject private_key, EVP_PKEY* pkey) {
|
| // https://crbug.com/381465
|
| class KeystoreEngineWorkaround {
|
| public:
|
| - KeystoreEngineWorkaround() : leaked_engine_(false) {}
|
| + KeystoreEngineWorkaround() {}
|
|
|
| - void LeakRsaEngine(EVP_PKEY* pkey) {
|
| - if (leaked_engine_)
|
| + void LeakEngine(jobject private_key) {
|
| + if (!engine_.is_null())
|
| return;
|
| - crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(pkey));
|
| - if (!rsa.get() ||
|
| - !rsa.get()->engine ||
|
| - strcmp(ENGINE_get_id(rsa.get()->engine), "keystore") ||
|
| - !ENGINE_init(rsa.get()->engine)) {
|
| + ScopedJavaLocalRef<jobject> engine =
|
| + GetOpenSSLEngineForPrivateKey(private_key);
|
| + if (engine.is_null()) {
|
| NOTREACHED();
|
| return;
|
| }
|
| - leaked_engine_ = true;
|
| + engine_.Reset(engine);
|
| }
|
|
|
| private:
|
| - bool leaked_engine_;
|
| + ScopedJavaGlobalRef<jobject> engine_;
|
| };
|
|
|
| -void LeakRsaEngine(EVP_PKEY* pkey) {
|
| +void LeakEngine(jobject private_key) {
|
| static base::LazyInstance<KeystoreEngineWorkaround>::Leaky s_instance =
|
| LAZY_INSTANCE_INITIALIZER;
|
| - s_instance.Get().LeakRsaEngine(pkey);
|
| + s_instance.Get().LeakEngine(private_key);
|
| }
|
|
|
| // Setup an EVP_PKEY to wrap an existing platform RSA PrivateKey object
|
| @@ -351,31 +386,49 @@ void LeakRsaEngine(EVP_PKEY* pkey) {
|
| // |pkey| is the EVP_PKEY to setup as a wrapper.
|
| // Returns true on success, false otherwise.
|
| EVP_PKEY* GetRsaLegacyKey(jobject private_key) {
|
| - EVP_PKEY* sys_pkey =
|
| + AndroidEVP_PKEY* sys_pkey =
|
| GetOpenSSLSystemHandleForPrivateKey(private_key);
|
| if (sys_pkey != NULL) {
|
| - CRYPTO_add(&sys_pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
|
| - LeakRsaEngine(sys_pkey);
|
| - } else {
|
| - // GetOpenSSLSystemHandleForPrivateKey() will fail on Android
|
| - // 4.0.3 and earlier. However, it is possible to get the key
|
| - // content with PrivateKey.getEncoded() on these platforms.
|
| - // Note that this method may return NULL on 4.0.4 and later.
|
| - std::vector<uint8> encoded;
|
| - if (!GetPrivateKeyEncodedBytes(private_key, &encoded)) {
|
| - LOG(ERROR) << "Can't get private key data!";
|
| + if (sys_pkey->type != ANDROID_EVP_PKEY_RSA) {
|
| + LOG(ERROR) << "Private key has wrong type!";
|
| return NULL;
|
| }
|
| - const unsigned char* p =
|
| - reinterpret_cast<const unsigned char*>(&encoded[0]);
|
| - int len = static_cast<int>(encoded.size());
|
| - sys_pkey = d2i_AutoPrivateKey(NULL, &p, len);
|
| - if (sys_pkey == NULL) {
|
| - LOG(ERROR) << "Can't convert private key data!";
|
| - return NULL;
|
| +
|
| + AndroidRSA* sys_rsa = sys_pkey->pkey.rsa;
|
| + if (sys_rsa->engine) {
|
| + // |private_key| may not have an engine if the PrivateKey did not come
|
| + // from the key store, such as in unit tests.
|
| + if (!strcmp(sys_rsa->engine->id, "keystore")) {
|
| + LeakEngine(private_key);
|
| + } else {
|
| + NOTREACHED();
|
| + }
|
| }
|
| +
|
| + crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
|
| + if (!GetRsaPkeyWrapper(private_key, sys_rsa, pkey.get()))
|
| + return NULL;
|
| + return pkey.release();
|
| + }
|
| +
|
| + // GetOpenSSLSystemHandleForPrivateKey() will fail on Android 4.0.3 and
|
| + // earlier. However, it is possible to get the key content with
|
| + // PrivateKey.getEncoded() on these platforms. Note that this method may
|
| + // return NULL on 4.0.4 and later.
|
| + std::vector<uint8> encoded;
|
| + if (!GetPrivateKeyEncodedBytes(private_key, &encoded)) {
|
| + LOG(ERROR) << "Can't get private key data!";
|
| + return NULL;
|
| + }
|
| + const unsigned char* p =
|
| + reinterpret_cast<const unsigned char*>(&encoded[0]);
|
| + int len = static_cast<int>(encoded.size());
|
| + EVP_PKEY* pkey = d2i_AutoPrivateKey(NULL, &p, len);
|
| + if (pkey == NULL) {
|
| + LOG(ERROR) << "Can't convert private key data!";
|
| + return NULL;
|
| }
|
| - return sys_pkey;
|
| + return pkey;
|
| }
|
|
|
| // Custom DSA_METHOD that uses the platform APIs.
|
| @@ -707,7 +760,7 @@ EVP_PKEY* GetOpenSSLPrivateKeyWrapper(jobject private_key) {
|
| pkey.reset(legacy_key);
|
| } else {
|
| // Running on Android 4.2.
|
| - if (!GetRsaPkeyWrapper(private_key, pkey.get()))
|
| + if (!GetRsaPkeyWrapper(private_key, NULL, pkey.get()))
|
| return NULL;
|
| }
|
| }
|
|
|