Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(20)

Unified Diff: net/android/keystore_openssl.cc

Issue 365503007: Insulate the legacy Android client auth code from OpenSSL ABI changes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Mismerge Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/android/keystore.cc ('k') | net/android/legacy_openssl.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
}
}
« no previous file with comments | « net/android/keystore.cc ('k') | net/android/legacy_openssl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698