Commit eeff853e authored by davidben@chromium.org's avatar davidben@chromium.org

Insulate the legacy Android client auth code from OpenSSL ABI changes.

The current implementation assumes the Android system OpenSSL and our copy have
compatible ABIs. This will be a problem when switching to BoringSSL which has
already changed from ABIs. Moreoever, it's already slightly off now as any
globals (error queue and locks) have different instances between the two.

Rework the code to never mix the two ABIs. We replicate the subset of OpenSSL
ABI we care about and use it to find the rsa_priv_enc implementation. This is
then mapped to Chromium OpenSSL via the custom RSA_METHOD. In addition, because
we cannot safely increase reference counts, retain references to Java wrappers
where appropriate in lieu of the C structures.

Also fix AndroidKeyStore unit tests on 4.1; they broke after an overzealous
NOTREACHED() in r278305.

BUG=389414

Review URL: https://codereview.chromium.org/365503007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282713 0039d316-1c4b-4281-b951-d872f2087c98
parent 38127887
...@@ -137,6 +137,19 @@ public interface AndroidKeyStore { ...@@ -137,6 +137,19 @@ public interface AndroidKeyStore {
@CalledByNative @CalledByNative
long getOpenSSLHandleForPrivateKey(AndroidPrivateKey key); long getOpenSSLHandleForPrivateKey(AndroidPrivateKey key);
/**
* Return the OpenSSLEngine object corresponding to a given PrivateKey
* object.
*
* This shall only be used for Android 4.1 to work around a platform bug.
* See https://crbug.com/381465.
*
* @param key The PrivateKey handle.
* @return The OpenSSLEngine object (or null if not available)
*/
@CalledByNative
Object getOpenSSLEngineForPrivateKey(AndroidPrivateKey key);
/** /**
* Called when the native OpenSSL engine no longer needs access to the underlying key. * Called when the native OpenSSL engine no longer needs access to the underlying key.
*/ */
......
...@@ -142,17 +142,16 @@ public class DefaultAndroidKeyStore implements AndroidKeyStore { ...@@ -142,17 +142,16 @@ public class DefaultAndroidKeyStore implements AndroidKeyStore {
return PrivateKeyType.INVALID; return PrivateKeyType.INVALID;
} }
@Override private Object getOpenSSLKeyForPrivateKey(AndroidPrivateKey key) {
public long getOpenSSLHandleForPrivateKey(AndroidPrivateKey key) {
PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey(); PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
// Sanity checks // Sanity checks
if (javaKey == null) { if (javaKey == null) {
Log.e(TAG, "key == null"); Log.e(TAG, "key == null");
return 0; return null;
} }
if (!(javaKey instanceof RSAPrivateKey)) { if (!(javaKey instanceof RSAPrivateKey)) {
Log.e(TAG, "does not implement RSAPrivateKey"); Log.e(TAG, "does not implement RSAPrivateKey");
return 0; return null;
} }
// First, check that this is a proper instance of OpenSSLRSAPrivateKey // First, check that this is a proper instance of OpenSSLRSAPrivateKey
// or one of its sub-classes. // or one of its sub-classes.
...@@ -165,7 +164,7 @@ public class DefaultAndroidKeyStore implements AndroidKeyStore { ...@@ -165,7 +164,7 @@ public class DefaultAndroidKeyStore implements AndroidKeyStore {
// implementation of the java.security APIs, compared to vanilla // implementation of the java.security APIs, compared to vanilla
// Android. Highly unlikely, but still possible. // Android. Highly unlikely, but still possible.
Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e); Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e);
return 0; return null;
} }
if (!superClass.isInstance(javaKey)) { if (!superClass.isInstance(javaKey)) {
// This may happen if the PrivateKey was not created by the "AndroidOpenSSL" // This may happen if the PrivateKey was not created by the "AndroidOpenSSL"
...@@ -173,14 +172,14 @@ public class DefaultAndroidKeyStore implements AndroidKeyStore { ...@@ -173,14 +172,14 @@ public class DefaultAndroidKeyStore implements AndroidKeyStore {
// to implement a different default provider. Also highly unlikely. // to implement a different default provider. Also highly unlikely.
Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:" + Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:" +
javaKey.getClass().getCanonicalName()); javaKey.getClass().getCanonicalName());
return 0; return null;
} }
try { try {
// Use reflection to invoke the 'getOpenSSLKey()' method on // Use reflection to invoke the 'getOpenSSLKey()' method on the
// the private key. This returns another Java object that wraps // private key. This returns another Java object that wraps a native
// a native EVP_PKEY. Note that the method is final, so calling // EVP_PKEY and OpenSSLEngine. Note that the method is final in Android
// the superclass implementation is ok. // 4.1, so calling the superclass implementation is ok.
Method getKey = superClass.getDeclaredMethod("getOpenSSLKey"); Method getKey = superClass.getDeclaredMethod("getOpenSSLKey");
getKey.setAccessible(true); getKey.setAccessible(true);
Object opensslKey = null; Object opensslKey = null;
...@@ -192,9 +191,22 @@ public class DefaultAndroidKeyStore implements AndroidKeyStore { ...@@ -192,9 +191,22 @@ public class DefaultAndroidKeyStore implements AndroidKeyStore {
if (opensslKey == null) { if (opensslKey == null) {
// Bail when detecting OEM "enhancement". // Bail when detecting OEM "enhancement".
Log.e(TAG, "getOpenSSLKey() returned null"); Log.e(TAG, "getOpenSSLKey() returned null");
return 0; return null;
} }
return opensslKey;
} catch (Exception e) {
Log.e(TAG, "Exception while trying to retrieve system EVP_PKEY handle: " + e);
return null;
}
}
@Override
public long getOpenSSLHandleForPrivateKey(AndroidPrivateKey key) {
Object opensslKey = getOpenSSLKeyForPrivateKey(key);
if (opensslKey == null)
return 0;
try {
// Use reflection to invoke the 'getPkeyContext' method on the // Use reflection to invoke the 'getPkeyContext' method on the
// result of the getOpenSSLKey(). This is an 32-bit integer // result of the getOpenSSLKey(). This is an 32-bit integer
// which is the address of an EVP_PKEY object. Note that this // which is the address of an EVP_PKEY object. Note that this
...@@ -229,6 +241,61 @@ public class DefaultAndroidKeyStore implements AndroidKeyStore { ...@@ -229,6 +241,61 @@ public class DefaultAndroidKeyStore implements AndroidKeyStore {
} }
} }
@Override
public Object getOpenSSLEngineForPrivateKey(AndroidPrivateKey key) {
// Find the system OpenSSLEngine class.
Class<?> engineClass;
try {
engineClass = Class.forName(
"org.apache.harmony.xnet.provider.jsse.OpenSSLEngine");
} catch (Exception e) {
// This may happen if the target device has a completely different
// implementation of the java.security APIs, compared to vanilla
// Android. Highly unlikely, but still possible.
Log.e(TAG, "Cannot find system OpenSSLEngine class: " + e);
return null;
}
Object opensslKey = getOpenSSLKeyForPrivateKey(key);
if (opensslKey == null)
return null;
try {
// Use reflection to invoke the 'getEngine' method on the
// result of the getOpenSSLKey().
Method getEngine;
try {
getEngine = opensslKey.getClass().getDeclaredMethod("getEngine");
} catch (Exception e) {
// Bail here too, something really not working as expected.
Log.e(TAG, "No getEngine() method on OpenSSLKey member:" + e);
return null;
}
getEngine.setAccessible(true);
Object engine = null;
try {
engine = getEngine.invoke(opensslKey);
} finally {
getEngine.setAccessible(false);
}
if (engine == null) {
// The PrivateKey is probably rotten for some reason.
Log.e(TAG, "getEngine() returned null");
}
// Sanity-check the returned engine.
if (!engineClass.isInstance(engine)) {
Log.e(TAG, "Engine is not an OpenSSLEngine instance, its class name is:" +
engine.getClass().getCanonicalName());
return null;
}
return engine;
} catch (Exception e) {
Log.e(TAG, "Exception while trying to retrieve OpenSSLEngine object: " + e);
return null;
}
}
@Override @Override
public void releaseKey(AndroidPrivateKey key) { public void releaseKey(AndroidPrivateKey key) {
// no-op for in-process. GC will handle key collection // no-op for in-process. GC will handle key collection
......
...@@ -116,6 +116,13 @@ public class RemoteAndroidKeyStore implements AndroidKeyStore { ...@@ -116,6 +116,13 @@ public class RemoteAndroidKeyStore implements AndroidKeyStore {
return 0; return 0;
} }
@Override
public Object getOpenSSLEngineForPrivateKey(AndroidPrivateKey privateKey) {
// This should not be called as it's only for older versions of Android.
assert false;
return null;
}
public AndroidPrivateKey createKey(String alias) { public AndroidPrivateKey createKey(String alias) {
try { try {
int handle = mRemoteManager.getPrivateKeyHandle(alias); int handle = mRemoteManager.getPrivateKeyHandle(alias);
......
...@@ -124,7 +124,7 @@ PrivateKeyType GetPrivateKeyType(jobject private_key_ref) { ...@@ -124,7 +124,7 @@ PrivateKeyType GetPrivateKeyType(jobject private_key_ref) {
return static_cast<PrivateKeyType>(type); return static_cast<PrivateKeyType>(type);
} }
EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key_ref) { AndroidEVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key_ref) {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
// Note: the pointer is passed as a jint here because that's how it // Note: the pointer is passed as a jint here because that's how it
// is stored in the Java object. Java doesn't have a primitive type // is stored in the Java object. Java doesn't have a primitive type
...@@ -138,7 +138,18 @@ EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key_ref) { ...@@ -138,7 +138,18 @@ EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key_ref) {
env, env,
GetKeyStore(private_key_ref).obj(), GetKeyStore(private_key_ref).obj(),
private_key_ref); private_key_ref);
return reinterpret_cast<EVP_PKEY*>(pkey); return reinterpret_cast<AndroidEVP_PKEY*>(pkey);
}
ScopedJavaLocalRef<jobject> GetOpenSSLEngineForPrivateKey(
jobject private_key_ref) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> engine =
Java_AndroidKeyStore_getOpenSSLEngineForPrivateKey(
env,
GetKeyStore(private_key_ref).obj(),
private_key_ref);
return engine;
} }
void ReleaseKey(jobject private_key_ref) { void ReleaseKey(jobject private_key_ref) {
......
...@@ -10,19 +10,19 @@ ...@@ -10,19 +10,19 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/android/scoped_java_ref.h"
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "net/base/net_export.h" #include "net/base/net_export.h"
#include "net/ssl/ssl_client_cert_type.h" #include "net/ssl/ssl_client_cert_type.h"
// Avoid including <openssl/evp.h> here.
typedef struct evp_pkey_st EVP_PKEY;
// Misc functions to access the Android platform KeyStore. // Misc functions to access the Android platform KeyStore.
namespace net { namespace net {
namespace android { namespace android {
struct AndroidEVP_PKEY;
// Define a list of constants describing private key types. The // Define a list of constants describing private key types. The
// values are shared with Java through org.chromium.net.PrivateKeyType. // values are shared with Java through org.chromium.net.PrivateKeyType.
// Example: PRIVATE_KEY_TYPE_RSA. // Example: PRIVATE_KEY_TYPE_RSA.
...@@ -93,20 +93,28 @@ NET_EXPORT bool RawSignDigestWithPrivateKey( ...@@ -93,20 +93,28 @@ NET_EXPORT bool RawSignDigestWithPrivateKey(
// on error. // on error.
NET_EXPORT PrivateKeyType GetPrivateKeyType(jobject private_key); NET_EXPORT PrivateKeyType GetPrivateKeyType(jobject private_key);
// Returns a handle to the system EVP_PKEY object used to back a given // Returns a handle to the system AndroidEVP_PKEY object used to back a given
// private_key object. This must *only* be used for RSA private keys // private_key object. This must *only* be used for RSA private keys on Android
// on Android < 4.2. Technically, this is only guaranteed to work if // < 4.2. Technically, this is only guaranteed to work if the system image
// the system image contains a vanilla implementation of the Java // contains a vanilla implementation of the Java API frameworks based on Harmony
// API frameworks based on Harmony + OpenSSL. // + OpenSSL.
// //
// |private_key| is a JNI reference for the private key. // |private_key| is a JNI reference for the private key.
// Returns an EVP_PKEY* handle, or NULL in case of error. // Returns an AndroidEVP_PKEY* handle, or NULL in case of error.
// //
// Note: Despite its name and return type, this function doesn't know // Note: Despite its name and return type, this function doesn't know
// anything about OpenSSL, it just type-casts a system pointer that // anything about OpenSSL, it just type-casts a system pointer that
// is passed as an int through JNI. As such, it never increments // is passed as an int through JNI. As such, it never increments
// the returned key's reference count. // the returned key's reference count.
EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key); AndroidEVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key);
// Returns a JNI reference to the OpenSSLEngine object which is used to back a
// given private_key object. This must *only* be used for RSA private keys on
// Android < 4.2. Technically, this is only guaranteed to work if the system
// image contains a vanilla implementation of the Java API frameworks based on
// Harmony + OpenSSL.
base::android::ScopedJavaLocalRef<jobject> GetOpenSSLEngineForPrivateKey(
jobject private_key);
NET_EXPORT void ReleaseKey(jobject private_key); NET_EXPORT void ReleaseKey(jobject private_key);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "crypto/openssl_util.h" #include "crypto/openssl_util.h"
#include "crypto/scoped_openssl_types.h" #include "crypto/scoped_openssl_types.h"
#include "net/android/keystore.h" #include "net/android/keystore.h"
#include "net/android/legacy_openssl.h"
#include "net/ssl/ssl_client_cert_type.h" #include "net/ssl/ssl_client_cert_type.h"
// IMPORTANT NOTE: The following code will currently only work when used // IMPORTANT NOTE: The following code will currently only work when used
...@@ -96,6 +97,7 @@ ...@@ -96,6 +97,7 @@
// here, which saves a lot of complexity. // here, which saves a lot of complexity.
using base::android::ScopedJavaGlobalRef; using base::android::ScopedJavaGlobalRef;
using base::android::ScopedJavaLocalRef;
namespace net { namespace net {
namespace android { namespace android {
...@@ -109,6 +111,11 @@ typedef crypto::ScopedOpenSSL<EC_GROUP, EC_GROUP_free>::Type ScopedEC_GROUP; ...@@ -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. // all other method pointers are either stubs returning errors, or no-ops.
// See <openssl/rsa.h> for exact declaration of RSA_METHOD. // See <openssl/rsa.h> for exact declaration of RSA_METHOD.
struct RsaAppData {
jobject private_key;
AndroidRSA* legacy_rsa;
};
int RsaMethodPubEnc(int flen, int RsaMethodPubEnc(int flen,
const unsigned char* from, const unsigned char* from,
unsigned char* to, unsigned char* to,
...@@ -151,18 +158,38 @@ int RsaMethodPrivEnc(int flen, ...@@ -151,18 +158,38 @@ int RsaMethodPrivEnc(int flen,
} }
// Retrieve private key JNI reference. // Retrieve private key JNI reference.
jobject private_key = reinterpret_cast<jobject>(RSA_get_app_data(rsa)); RsaAppData* app_data = static_cast<RsaAppData*>(RSA_get_app_data(rsa));
if (!private_key) { if (!app_data || !app_data->private_key) {
LOG(WARNING) << "Null JNI reference passed to RsaMethodPrivEnc!"; LOG(WARNING) << "Null JNI reference passed to RsaMethodPrivEnc!";
RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR); RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
return -1; 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); base::StringPiece from_piece(reinterpret_cast<const char*>(from), flen);
std::vector<uint8> result; std::vector<uint8> result;
// For RSA keys, this function behaves as RSA_private_encrypt with // For RSA keys, this function behaves as RSA_private_encrypt with
// PKCS#1 padding. // 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!"; LOG(WARNING) << "Could not sign message in RsaMethodPrivEnc!";
RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR); RSAerr(RSA_F_RSA_PRIVATE_ENCRYPT, ERR_R_INTERNAL_ERROR);
return -1; return -1;
...@@ -202,10 +229,11 @@ int RsaMethodInit(RSA* rsa) { ...@@ -202,10 +229,11 @@ int RsaMethodInit(RSA* rsa) {
int RsaMethodFinish(RSA* rsa) { int RsaMethodFinish(RSA* rsa) {
// Ensure the global JNI reference created with this wrapper is // Ensure the global JNI reference created with this wrapper is
// properly destroyed with it. // properly destroyed with it.
jobject key = reinterpret_cast<jobject>(RSA_get_app_data(rsa)); RsaAppData* app_data = static_cast<RsaAppData*>(RSA_get_app_data(rsa));
if (key != NULL) { if (app_data != NULL) {
RSA_set_app_data(rsa, 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 // Actual return value is ignored by OpenSSL. There are no docs
// explaining what this is supposed to be. // explaining what this is supposed to be.
...@@ -272,20 +300,26 @@ bool SwapBigNumPtrFromBytes(const std::vector<uint8>& new_bytes, ...@@ -272,20 +300,26 @@ bool SwapBigNumPtrFromBytes(const std::vector<uint8>& new_bytes,
// Setup an EVP_PKEY to wrap an existing platform RSA PrivateKey object. // Setup an EVP_PKEY to wrap an existing platform RSA PrivateKey object.
// |private_key| is the JNI reference (local or global) to the 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. // |pkey| is the EVP_PKEY to setup as a wrapper.
// Returns true on success, false otherwise. // Returns true on success, false otherwise.
// On success, this creates a new global JNI reference to the object // 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 // that is owned by and destroyed with the EVP_PKEY. I.e. caller can
// free |private_key| after the call. // free |private_key| after the call.
// IMPORTANT: The EVP_PKEY will *only* work on Android >= 4.2. For older bool GetRsaPkeyWrapper(jobject private_key,
// platforms, use GetRsaLegacyKey() instead. AndroidRSA* legacy_rsa,
bool GetRsaPkeyWrapper(jobject private_key, EVP_PKEY* pkey) { EVP_PKEY* pkey) {
crypto::ScopedRSA rsa(RSA_new()); crypto::ScopedRSA rsa(RSA_new());
RSA_set_method(rsa.get(), &android_rsa_method); RSA_set_method(rsa.get(), &android_rsa_method);
// HACK: RSA_size() doesn't work with custom RSA_METHODs. To ensure that // 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 // it will return the right value, set the 'n' field of the RSA object
// to match the private key's modulus. // 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; std::vector<uint8> modulus;
if (!GetRSAKeyModulus(private_key, &modulus)) { if (!GetRSAKeyModulus(private_key, &modulus)) {
LOG(ERROR) << "Failed to get private key modulus"; LOG(ERROR) << "Failed to get private key modulus";
...@@ -302,7 +336,10 @@ bool GetRsaPkeyWrapper(jobject private_key, EVP_PKEY* pkey) { ...@@ -302,7 +336,10 @@ bool GetRsaPkeyWrapper(jobject private_key, EVP_PKEY* pkey) {
LOG(ERROR) << "Could not create global JNI reference"; LOG(ERROR) << "Could not create global JNI reference";
return false; 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()); EVP_PKEY_assign_RSA(pkey, rsa.release());
return true; return true;
} }
...@@ -319,30 +356,28 @@ bool GetRsaPkeyWrapper(jobject private_key, EVP_PKEY* pkey) { ...@@ -319,30 +356,28 @@ bool GetRsaPkeyWrapper(jobject private_key, EVP_PKEY* pkey) {
// https://crbug.com/381465 // https://crbug.com/381465
class KeystoreEngineWorkaround { class KeystoreEngineWorkaround {
public: public:
KeystoreEngineWorkaround() : leaked_engine_(false) {} KeystoreEngineWorkaround() {}
void LeakRsaEngine(EVP_PKEY* pkey) { void LeakEngine(jobject private_key) {
if (leaked_engine_) if (!engine_.is_null())
return; return;
crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(pkey)); ScopedJavaLocalRef<jobject> engine =
if (!rsa.get() || GetOpenSSLEngineForPrivateKey(private_key);
!rsa.get()->engine || if (engine.is_null()) {
strcmp(ENGINE_get_id(rsa.get()->engine), "keystore") ||
!ENGINE_init(rsa.get()->engine)) {
NOTREACHED(); NOTREACHED();
return; return;
} }
leaked_engine_ = true; engine_.Reset(engine);
} }
private: private:
bool leaked_engine_; ScopedJavaGlobalRef<jobject> engine_;
}; };
void LeakRsaEngine(EVP_PKEY* pkey) { void LeakEngine(jobject private_key) {
static base::LazyInstance<KeystoreEngineWorkaround>::Leaky s_instance = static base::LazyInstance<KeystoreEngineWorkaround>::Leaky s_instance =
LAZY_INSTANCE_INITIALIZER; 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 // Setup an EVP_PKEY to wrap an existing platform RSA PrivateKey object
...@@ -351,31 +386,49 @@ void LeakRsaEngine(EVP_PKEY* pkey) { ...@@ -351,31 +386,49 @@ void LeakRsaEngine(EVP_PKEY* pkey) {
// |pkey| is the EVP_PKEY to setup as a wrapper. // |pkey| is the EVP_PKEY to setup as a wrapper.
// Returns true on success, false otherwise. // Returns true on success, false otherwise.
EVP_PKEY* GetRsaLegacyKey(jobject private_key) { EVP_PKEY* GetRsaLegacyKey(jobject private_key) {
EVP_PKEY* sys_pkey = AndroidEVP_PKEY* sys_pkey =
GetOpenSSLSystemHandleForPrivateKey(private_key); GetOpenSSLSystemHandleForPrivateKey(private_key);
if (sys_pkey != NULL) { if (sys_pkey != NULL) {
CRYPTO_add(&sys_pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); if (sys_pkey->type != ANDROID_EVP_PKEY_RSA) {
LeakRsaEngine(sys_pkey); LOG(ERROR) << "Private key has wrong type!";
} 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!";
return NULL; return NULL;
} }
const unsigned char* p =
reinterpret_cast<const unsigned char*>(&encoded[0]); AndroidRSA* sys_rsa = sys_pkey->pkey.rsa;
int len = static_cast<int>(encoded.size()); if (sys_rsa->engine) {
sys_pkey = d2i_AutoPrivateKey(NULL, &p, len); // |private_key| may not have an engine if the PrivateKey did not come
if (sys_pkey == NULL) { // from the key store, such as in unit tests.
LOG(ERROR) << "Can't convert private key data!"; if (!strcmp(sys_rsa->engine->id, "keystore")) {
return NULL; 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. // Custom DSA_METHOD that uses the platform APIs.
...@@ -707,7 +760,7 @@ EVP_PKEY* GetOpenSSLPrivateKeyWrapper(jobject private_key) { ...@@ -707,7 +760,7 @@ EVP_PKEY* GetOpenSSLPrivateKeyWrapper(jobject private_key) {
pkey.reset(legacy_key); pkey.reset(legacy_key);
} else { } else {
// Running on Android 4.2. // Running on Android 4.2.
if (!GetRsaPkeyWrapper(private_key, pkey.get())) if (!GetRsaPkeyWrapper(private_key, NULL, pkey.get()))
return NULL; return NULL;
} }
} }
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_ANDROID_LEGACY_OPENSSL_H
#define NET_ANDROID_LEGACY_OPENSSL_H
// This file contains a replica of the Android system OpenSSL ABI shipped in
// Android 4.1.x (API level 16). The ABI may not necessarily be compatible with
// the copy of OpenSSL shipped in Chromium. This is used to implement
// RSA_private_encrypt in one of the legacy client auth codepaths.
//
// See https://android.googlesource.com/platform/external/openssl/+/android-4.1.2_r2.1
namespace net {
namespace android {
enum {
ANDROID_EVP_PKEY_RSA = 6,
};
enum {
ANDROID_RSA_PKCS1_PADDING = 1,
ANDROID_RSA_SSLV23_PADDING = 2,
ANDROID_RSA_NO_PADDING = 3,
ANDROID_RSA_PKCS1_OAEP_PADDING = 4,
ANDROID_X931_PADDING = 5,
ANDROID_PKCS1_PSS_PADDING = 6,
};
struct AndroidEVP_PKEY_ASN1_METHOD;
struct AndroidRSA_METHOD;
struct AndroidSTACK;
struct AndroidCRYPTO_EX_DATA {
AndroidSTACK* sk;
int dummy;
};
struct AndroidENGINE {
const char* id;
// Remaining fields intentionally omitted.
};
struct AndroidRSA {
int pad;
long version;
const AndroidRSA_METHOD* meth;
AndroidENGINE* engine;
// Remaining fields intentionally omitted.
};
struct AndroidRSA_METHOD {
const char* name;
int (*rsa_pub_enc)(int flen,
const unsigned char* from,
unsigned char* to,
AndroidRSA* rsa,
int padding);
int (*rsa_pub_dec)(int flen,
const unsigned char* from,
unsigned char* to,
AndroidRSA* rsa,
int padding);
int (*rsa_priv_enc)(int flen,
const unsigned char* from,
unsigned char* to,
AndroidRSA* rsa,
int padding);
int (*rsa_priv_dec)(int flen,
const unsigned char* from,
unsigned char* to,
AndroidRSA* rsa,
int padding);
// Remaining fields intentionally omitted.
};
struct AndroidEVP_PKEY {
int type;
int save_type;
// Note: this value must NOT be modified using Chromium's CRYPTO_add
// function. That may not necessarily use the same locking implementation as
// system OpenSSL.
int references;
const AndroidEVP_PKEY_ASN1_METHOD* ameth;
AndroidENGINE* engine;
union {
char* ptr;
AndroidRSA* rsa;
} pkey;
int save_parameters;
AndroidSTACK* attributes;
};
} // namespace android
} // namespace net
#endif // NET_ANDROID_LEGACY_OPENSSL_H
...@@ -161,6 +161,7 @@ ...@@ -161,6 +161,7 @@
'android/keystore.h', 'android/keystore.h',
'android/keystore_openssl.cc', 'android/keystore_openssl.cc',
'android/keystore_openssl.h', 'android/keystore_openssl.h',
'android/legacy_openssl.h',
'android/net_jni_registrar.cc', 'android/net_jni_registrar.cc',
'android/net_jni_registrar.h', 'android/net_jni_registrar.h',
'android/network_change_notifier_android.cc', 'android/network_change_notifier_android.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment