Index: net/android/keystore_openssl.cc |
diff --git a/net/android/keystore_openssl.cc b/net/android/keystore_openssl.cc |
deleted file mode 100644 |
index 7437e1a6f0b3e883ada0185bd9b2a6271354f005..0000000000000000000000000000000000000000 |
--- a/net/android/keystore_openssl.cc |
+++ /dev/null |
@@ -1,556 +0,0 @@ |
-// Copyright (c) 2013 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. |
- |
-#include "net/android/keystore_openssl.h" |
- |
-#include <jni.h> |
-#include <openssl/bn.h> |
-#include <openssl/ec.h> |
-#include <openssl/engine.h> |
-#include <openssl/err.h> |
-#include <openssl/evp.h> |
-#include <openssl/rsa.h> |
-#include <stdint.h> |
- |
-#include <memory> |
- |
-#include "base/android/build_info.h" |
-#include "base/android/scoped_java_ref.h" |
-#include "base/lazy_instance.h" |
-#include "base/logging.h" |
-#include "crypto/openssl_util.h" |
-#include "net/android/keystore.h" |
-#include "net/android/legacy_openssl.h" |
-#include "net/ssl/scoped_openssl_types.h" |
-#include "net/ssl/ssl_client_cert_type.h" |
- |
-// IMPORTANT NOTE: The following code will currently only work when used |
-// to implement client certificate support with OpenSSL. That's because |
-// only the signing operations used in this use case are implemented here. |
-// |
-// Generally speaking, OpenSSL provides many different ways to sign |
-// digests. This code doesn't support all these cases, only the ones that |
-// are required to sign the digest during the OpenSSL handshake for TLS. |
-// |
-// The OpenSSL EVP_PKEY type is a generic wrapper around key pairs. |
-// Internally, it can hold a pointer to a RSA or ECDSA structure, which model |
-// keypair implementations of each respective crypto algorithm. |
-// |
-// The RSA type has a 'method' field pointer to a vtable-like structure |
-// called a RSA_METHOD. This contains several function pointers that |
-// correspond to operations on RSA keys (e.g. decode/encode with public |
-// key, decode/encode with private key, signing, validation), as well as |
-// a few flags. |
-// |
-// For example, the RSA_sign() function will call "method->rsa_sign()" if |
-// method->rsa_sign is not NULL, otherwise, it will perform a regular |
-// signing operation using the other fields in the RSA structure (which |
-// are used to hold the typical modulus / exponent / parameters for the |
-// key pair). |
-// |
-// This source file thus defines a custom RSA_METHOD structure whose |
-// fields point to static methods used to implement the corresponding |
-// RSA operation using platform Android APIs. |
-// |
-// However, the platform APIs require a jobject JNI reference to work. It must |
-// be stored in the RSA instance, or made accessible when the custom RSA |
-// methods are called. This is done by storing it in a |KeyExData| structure |
-// that's referenced by the key using |EX_DATA|. |
- |
-using base::android::ScopedJavaGlobalRef; |
-using base::android::ScopedJavaLocalRef; |
- |
-namespace net { |
-namespace android { |
- |
-namespace { |
- |
-extern const RSA_METHOD android_rsa_method; |
-extern const ECDSA_METHOD android_ecdsa_method; |
- |
-// KeyExData contains the data that is contained in the EX_DATA of the RSA and |
-// EC_KEY objects that are created to wrap Android system keys. |
-struct KeyExData { |
- // private_key contains a reference to a Java, private-key object. |
- ScopedJavaGlobalRef<jobject> private_key; |
- // legacy_rsa, if not NULL, points to an RSA* in the system's OpenSSL (which |
- // might not be ABI compatible with Chromium). |
- AndroidRSA* legacy_rsa; |
- // cached_size contains the "size" of the key. This is the size of the |
- // modulus (in bytes) for RSA, or the group order size for ECDSA. This |
- // avoids calling into Java to calculate the size. |
- size_t cached_size; |
-}; |
- |
-// ExDataDup is called when one of the RSA or EC_KEY objects is duplicated. We |
-// don't support this and it should never happen. |
-int ExDataDup(CRYPTO_EX_DATA* to, |
- const CRYPTO_EX_DATA* from, |
- void** from_d, |
- int index, |
- long argl, |
- void* argp) { |
- CHECK_EQ((void*)NULL, *from_d); |
- return 0; |
-} |
- |
-// ExDataFree is called when one of the RSA or EC_KEY objects is freed. |
-void ExDataFree(void* parent, |
- void* ptr, |
- CRYPTO_EX_DATA* ad, |
- int index, |
- long argl, |
- void* argp) { |
- // Ensure the global JNI reference created with this wrapper is |
- // properly destroyed with it. |
- KeyExData *ex_data = reinterpret_cast<KeyExData*>(ptr); |
- delete ex_data; |
-} |
- |
-// BoringSSLEngine is a BoringSSL ENGINE that implements RSA and ECDSA by |
-// forwarding the requested operations to the Java libraries. |
-class BoringSSLEngine { |
- public: |
- BoringSSLEngine() |
- : rsa_index_(RSA_get_ex_new_index(0 /* argl */, |
- NULL /* argp */, |
- NULL /* new_func */, |
- ExDataDup, |
- ExDataFree)), |
- ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */, |
- NULL /* argp */, |
- NULL /* new_func */, |
- ExDataDup, |
- ExDataFree)), |
- engine_(ENGINE_new()) { |
- ENGINE_set_RSA_method( |
- engine_, &android_rsa_method, sizeof(android_rsa_method)); |
- ENGINE_set_ECDSA_method( |
- engine_, &android_ecdsa_method, sizeof(android_ecdsa_method)); |
- } |
- |
- int rsa_ex_index() const { return rsa_index_; } |
- int ec_key_ex_index() const { return ec_key_index_; } |
- |
- const ENGINE* engine() const { return engine_; } |
- |
- private: |
- const int rsa_index_; |
- const int ec_key_index_; |
- ENGINE* const engine_; |
-}; |
- |
-base::LazyInstance<BoringSSLEngine>::Leaky global_boringssl_engine = |
- LAZY_INSTANCE_INITIALIZER; |
- |
- |
-// VectorBignumSize returns the number of bytes needed to represent the bignum |
-// given in |v|, i.e. the length of |v| less any leading zero bytes. |
-size_t VectorBignumSize(const std::vector<uint8_t>& v) { |
- size_t size = v.size(); |
- // Ignore any leading zero bytes. |
- for (size_t i = 0; i < v.size() && v[i] == 0; i++) { |
- size--; |
- } |
- return size; |
-} |
- |
-KeyExData* RsaGetExData(const RSA* rsa) { |
- return reinterpret_cast<KeyExData*>( |
- RSA_get_ex_data(rsa, global_boringssl_engine.Get().rsa_ex_index())); |
-} |
- |
-size_t RsaMethodSize(const RSA *rsa) { |
- const KeyExData *ex_data = RsaGetExData(rsa); |
- return ex_data->cached_size; |
-} |
- |
-int RsaMethodEncrypt(RSA* rsa, |
- size_t* out_len, |
- uint8_t* out, |
- size_t max_out, |
- const uint8_t* in, |
- size_t in_len, |
- int padding) { |
- NOTIMPLEMENTED(); |
- OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE); |
- return 0; |
-} |
- |
-int RsaMethodSignRaw(RSA* rsa, |
- size_t* out_len, |
- uint8_t* out, |
- size_t max_out, |
- const uint8_t* in, |
- size_t in_len, |
- int padding) { |
- DCHECK_EQ(RSA_PKCS1_PADDING, padding); |
- if (padding != RSA_PKCS1_PADDING) { |
- // TODO(davidben): If we need to, we can implement RSA_NO_PADDING |
- // by using javax.crypto.Cipher and picking either the |
- // "RSA/ECB/NoPadding" or "RSA/ECB/PKCS1Padding" transformation as |
- // appropriate. I believe support for both of these was added in |
- // the same Android version as the "NONEwithRSA" |
- // java.security.Signature algorithm, so the same version checks |
- // for GetRsaLegacyKey should work. |
- OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_PADDING_TYPE); |
- return 0; |
- } |
- |
- // Retrieve private key JNI reference. |
- const KeyExData *ex_data = RsaGetExData(rsa); |
- if (!ex_data || !ex_data->private_key.obj()) { |
- LOG(WARNING) << "Null JNI reference passed to RsaMethodSignRaw!"; |
- OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); |
- return 0; |
- } |
- |
- // Pre-4.2 legacy codepath. |
- if (ex_data->legacy_rsa) { |
- int ret = ex_data->legacy_rsa->meth->rsa_priv_enc( |
- in_len, in, out, ex_data->legacy_rsa, ANDROID_RSA_PKCS1_PADDING); |
- if (ret < 0) { |
- LOG(WARNING) << "Could not sign message in RsaMethodSignRaw!"; |
- // 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.) |
- OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); |
- return 0; |
- } |
- *out_len = ret; |
- return 1; |
- } |
- |
- base::StringPiece from_piece(reinterpret_cast<const char*>(in), in_len); |
- std::vector<uint8_t> result; |
- // For RSA keys, this function behaves as RSA_private_encrypt with |
- // PKCS#1 padding. |
- if (!RawSignDigestWithPrivateKey(ex_data->private_key.obj(), from_piece, |
- &result)) { |
- LOG(WARNING) << "Could not sign message in RsaMethodSignRaw!"; |
- OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); |
- return 0; |
- } |
- |
- size_t expected_size = static_cast<size_t>(RSA_size(rsa)); |
- if (result.size() > expected_size) { |
- LOG(ERROR) << "RSA Signature size mismatch, actual: " |
- << result.size() << ", expected <= " << expected_size; |
- OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); |
- return 0; |
- } |
- |
- if (max_out < expected_size) { |
- OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE); |
- return 0; |
- } |
- |
- // Copy result to OpenSSL-provided buffer. RawSignDigestWithPrivateKey |
- // should pad with leading 0s, but if it doesn't, pad the result. |
- size_t zero_pad = expected_size - result.size(); |
- memset(out, 0, zero_pad); |
- memcpy(out + zero_pad, &result[0], result.size()); |
- *out_len = expected_size; |
- |
- return 1; |
-} |
- |
-int RsaMethodDecrypt(RSA* rsa, |
- size_t* out_len, |
- uint8_t* out, |
- size_t max_out, |
- const uint8_t* in, |
- size_t in_len, |
- int padding) { |
- NOTIMPLEMENTED(); |
- OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE); |
- return 0; |
-} |
- |
-int RsaMethodVerifyRaw(RSA* rsa, |
- size_t* out_len, |
- uint8_t* out, |
- size_t max_out, |
- const uint8_t* in, |
- size_t in_len, |
- int padding) { |
- NOTIMPLEMENTED(); |
- OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE); |
- return 0; |
-} |
- |
-const RSA_METHOD android_rsa_method = { |
- { |
- 0 /* references */, |
- 1 /* is_static */ |
- } /* common */, |
- nullptr /* app_data */, |
- |
- nullptr /* init */, |
- nullptr /* finish */, |
- RsaMethodSize, |
- nullptr /* sign */, |
- nullptr /* verify */, |
- RsaMethodEncrypt, |
- RsaMethodSignRaw, |
- RsaMethodDecrypt, |
- RsaMethodVerifyRaw, |
- nullptr /* private_transform */, |
- nullptr /* mod_exp */, |
- nullptr /* bn_mod_exp */, |
- RSA_FLAG_OPAQUE, |
- nullptr /* keygen */, |
- nullptr /* multi_prime_keygen */, |
- nullptr /* supports_digest */, |
-}; |
- |
-// 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. |
-// Returns a new EVP_PKEY on success, NULL 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. |
-crypto::ScopedEVP_PKEY CreateRsaPkeyWrapper( |
- jobject private_key, |
- AndroidRSA* legacy_rsa, |
- const crypto::OpenSSLErrStackTracer& tracer) { |
- crypto::ScopedRSA rsa( |
- RSA_new_method(global_boringssl_engine.Get().engine())); |
- |
- std::vector<uint8_t> modulus; |
- if (!GetRSAKeyModulus(private_key, &modulus)) { |
- LOG(ERROR) << "Failed to get private key modulus"; |
- return nullptr; |
- } |
- |
- std::unique_ptr<KeyExData> ex_data(new KeyExData); |
- ex_data->private_key.Reset(nullptr, private_key); |
- if (ex_data->private_key.is_null()) { |
- LOG(ERROR) << "Could not create global JNI reference"; |
- return nullptr; |
- } |
- ex_data->legacy_rsa = legacy_rsa; |
- ex_data->cached_size = VectorBignumSize(modulus); |
- |
- RSA_set_ex_data(rsa.get(), global_boringssl_engine.Get().rsa_ex_index(), |
- ex_data.release()); |
- |
- crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); |
- if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) |
- return nullptr; |
- return pkey; |
-} |
- |
-// On Android < 4.2, the libkeystore.so ENGINE uses CRYPTO_EX_DATA and is not |
-// added to the global engine list. If all references to it are dropped, OpenSSL |
-// will dlclose the module, leaving a dangling function pointer in the RSA |
-// CRYPTO_EX_DATA class. To work around this, leak an extra reference to the |
-// ENGINE we extract in GetRsaLegacyKey. |
-// |
-// In 4.2, this change avoids the problem: |
-// https://android.googlesource.com/platform/libcore/+/106a8928fb4249f2f3d4dba1dddbe73ca5cb3d61 |
-// |
-// https://crbug.com/381465 |
-class KeystoreEngineWorkaround { |
- public: |
- KeystoreEngineWorkaround() {} |
- |
- void LeakEngine(jobject private_key) { |
- if (!engine_.is_null()) |
- return; |
- ScopedJavaLocalRef<jobject> engine = |
- GetOpenSSLEngineForPrivateKey(private_key); |
- if (engine.is_null()) { |
- NOTREACHED(); |
- return; |
- } |
- engine_.Reset(engine); |
- } |
- |
- private: |
- ScopedJavaGlobalRef<jobject> engine_; |
-}; |
- |
-void LeakEngine(jobject private_key) { |
- static base::LazyInstance<KeystoreEngineWorkaround>::Leaky s_instance = |
- LAZY_INSTANCE_INITIALIZER; |
- s_instance.Get().LeakEngine(private_key); |
-} |
- |
-// Creates an EVP_PKEY wrapper corresponding to the RSA key |
-// |private_key|. Returns nullptr on failure. |
-crypto::ScopedEVP_PKEY GetRsaPkeyWrapper(jobject private_key) { |
- const int kAndroid42ApiLevel = 17; |
- crypto::OpenSSLErrStackTracer tracer(FROM_HERE); |
- |
- if (base::android::BuildInfo::GetInstance()->sdk_int() >= |
- kAndroid42ApiLevel) { |
- return CreateRsaPkeyWrapper(private_key, nullptr, tracer); |
- } |
- |
- // Route around platform limitation: if Android < 4.2, then |
- // base::android::RawSignDigestWithPrivateKey() cannot work, so try to get the |
- // system OpenSSL's EVP_PKEY backing this PrivateKey object. |
- AndroidEVP_PKEY* sys_pkey = |
- GetOpenSSLSystemHandleForPrivateKey(private_key); |
- if (sys_pkey == nullptr) |
- return nullptr; |
- |
- if (sys_pkey->type != ANDROID_EVP_PKEY_RSA) { |
- LOG(ERROR) << "Private key has wrong type!"; |
- return nullptr; |
- } |
- |
- 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") == 0) { |
- LeakEngine(private_key); |
- } else { |
- NOTREACHED(); |
- } |
- } |
- |
- return CreateRsaPkeyWrapper(private_key, sys_rsa, tracer); |
-} |
- |
-// Custom ECDSA_METHOD that uses the platform APIs. |
-// Note that for now, only signing through ECDSA_sign() is really supported. |
-// all other method pointers are either stubs returning errors, or no-ops. |
- |
-jobject EcKeyGetKey(const EC_KEY* ec_key) { |
- KeyExData* ex_data = reinterpret_cast<KeyExData*>(EC_KEY_get_ex_data( |
- ec_key, global_boringssl_engine.Get().ec_key_ex_index())); |
- return ex_data->private_key.obj(); |
-} |
- |
-size_t EcdsaMethodGroupOrderSize(const EC_KEY* ec_key) { |
- KeyExData* ex_data = reinterpret_cast<KeyExData*>(EC_KEY_get_ex_data( |
- ec_key, global_boringssl_engine.Get().ec_key_ex_index())); |
- return ex_data->cached_size; |
-} |
- |
-int EcdsaMethodSign(const uint8_t* digest, |
- size_t digest_len, |
- uint8_t* sig, |
- unsigned int* sig_len, |
- EC_KEY* ec_key) { |
- // Retrieve private key JNI reference. |
- jobject private_key = EcKeyGetKey(ec_key); |
- if (!private_key) { |
- LOG(WARNING) << "Null JNI reference passed to EcdsaMethodSign!"; |
- return 0; |
- } |
- // Sign message with it through JNI. |
- std::vector<uint8_t> signature; |
- base::StringPiece digest_sp(reinterpret_cast<const char*>(digest), |
- digest_len); |
- if (!RawSignDigestWithPrivateKey(private_key, digest_sp, &signature)) { |
- LOG(WARNING) << "Could not sign message in EcdsaMethodSign!"; |
- return 0; |
- } |
- |
- // Note: With ECDSA, the actual signature may be smaller than |
- // ECDSA_size(). |
- size_t max_expected_size = ECDSA_size(ec_key); |
- if (signature.size() > max_expected_size) { |
- LOG(ERROR) << "ECDSA Signature size mismatch, actual: " |
- << signature.size() << ", expected <= " |
- << max_expected_size; |
- return 0; |
- } |
- |
- memcpy(sig, &signature[0], signature.size()); |
- *sig_len = signature.size(); |
- return 1; |
-} |
- |
-int EcdsaMethodVerify(const uint8_t* digest, |
- size_t digest_len, |
- const uint8_t* sig, |
- size_t sig_len, |
- EC_KEY* ec_key) { |
- NOTIMPLEMENTED(); |
- OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NOT_IMPLEMENTED); |
- return 0; |
-} |
- |
-// Setup an EVP_PKEY to wrap an existing platform PrivateKey object. |
-// |private_key| is the JNI reference (local or global) to the object. |
-// Returns a new EVP_PKEY on success, NULL otherwise. |
-// On success, this creates a global JNI reference to the object that |
-// is owned by and destroyed with the EVP_PKEY. I.e. the caller shall |
-// always free |private_key| after the call. |
-crypto::ScopedEVP_PKEY GetEcdsaPkeyWrapper(jobject private_key) { |
- crypto::OpenSSLErrStackTracer tracer(FROM_HERE); |
- crypto::ScopedEC_KEY ec_key( |
- EC_KEY_new_method(global_boringssl_engine.Get().engine())); |
- |
- std::vector<uint8_t> order; |
- if (!GetECKeyOrder(private_key, &order)) { |
- LOG(ERROR) << "Can't extract order parameter from EC private key"; |
- return nullptr; |
- } |
- |
- std::unique_ptr<KeyExData> ex_data(new KeyExData); |
- ex_data->private_key.Reset(nullptr, private_key); |
- if (ex_data->private_key.is_null()) { |
- LOG(ERROR) << "Can't create global JNI reference"; |
- return nullptr; |
- } |
- ex_data->legacy_rsa = nullptr; |
- ex_data->cached_size = VectorBignumSize(order); |
- |
- EC_KEY_set_ex_data(ec_key.get(), |
- global_boringssl_engine.Get().ec_key_ex_index(), |
- ex_data.release()); |
- |
- crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new()); |
- if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get())) |
- return nullptr; |
- return pkey; |
-} |
- |
-const ECDSA_METHOD android_ecdsa_method = { |
- { |
- 0 /* references */, |
- 1 /* is_static */ |
- } /* common */, |
- NULL /* app_data */, |
- |
- NULL /* init */, |
- NULL /* finish */, |
- EcdsaMethodGroupOrderSize, |
- EcdsaMethodSign, |
- EcdsaMethodVerify, |
- ECDSA_FLAG_OPAQUE, |
-}; |
- |
-} // namespace |
- |
-crypto::ScopedEVP_PKEY GetOpenSSLPrivateKeyWrapper(jobject private_key) { |
- // Create sub key type, depending on private key's algorithm type. |
- PrivateKeyType key_type = GetPrivateKeyType(private_key); |
- switch (key_type) { |
- case PRIVATE_KEY_TYPE_RSA: |
- return GetRsaPkeyWrapper(private_key); |
- case PRIVATE_KEY_TYPE_ECDSA: |
- return GetEcdsaPkeyWrapper(private_key); |
- default: |
- LOG(WARNING) |
- << "GetOpenSSLPrivateKeyWrapper() called with invalid key type"; |
- return nullptr; |
- } |
-} |
- |
-} // namespace android |
-} // namespace net |