Index: net/ssl/ssl_platform_key_android.cc |
diff --git a/net/ssl/ssl_platform_key_android.cc b/net/ssl/ssl_platform_key_android.cc |
index 3da3858ff50bdd126fc3ed7670c57c1b90e41641..9df483950e4b7134e029671bce103b11bfa16831 100644 |
--- a/net/ssl/ssl_platform_key_android.cc |
+++ b/net/ssl/ssl_platform_key_android.cc |
@@ -4,26 +4,108 @@ |
#include "net/ssl/ssl_platform_key_android.h" |
-#include <openssl/digest.h> |
-#include <openssl/evp.h> |
-#include <utility> |
+#include <vector> |
+#include "base/android/build_info.h" |
+#include "base/android/scoped_java_ref.h" |
+#include "base/lazy_instance.h" |
#include "base/logging.h" |
#include "base/macros.h" |
#include "crypto/scoped_openssl_types.h" |
+#include "net/android/keystore.h" |
+#include "net/android/legacy_openssl.h" |
#include "net/base/net_errors.h" |
+#include "net/ssl/ssl_platform_key.h" |
#include "net/ssl/ssl_platform_key_task_runner.h" |
#include "net/ssl/ssl_private_key.h" |
#include "net/ssl/threaded_ssl_private_key.h" |
+using base::android::ScopedJavaLocalRef; |
+using base::android::ScopedJavaGlobalRef; |
+ |
namespace net { |
namespace { |
+// 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 = |
+ android::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); |
+} |
+ |
+android::AndroidRSA* GetLegacyRSAKey(jobject private_key) { |
+ const int kAndroid42ApiLevel = 17; |
+ |
+ if (base::android::BuildInfo::GetInstance()->sdk_int() >= |
+ kAndroid42ApiLevel) { |
+ return nullptr; |
+ } |
+ |
+ // 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. |
+ android::AndroidEVP_PKEY* sys_pkey = |
+ android::GetOpenSSLSystemHandleForPrivateKey(private_key); |
+ if (sys_pkey == nullptr) |
+ return nullptr; |
+ |
+ if (sys_pkey->type != android::ANDROID_EVP_PKEY_RSA) { |
+ LOG(ERROR) << "Private key has wrong type!"; |
+ return nullptr; |
+ } |
+ |
+ android::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 sys_rsa; |
+} |
+ |
class SSLPlatformKeyAndroid : public ThreadedSSLPrivateKey::Delegate { |
public: |
- SSLPlatformKeyAndroid(crypto::ScopedEVP_PKEY key, SSLPrivateKey::Type type) |
- : key_(std::move(key)), type_(type) {} |
+ SSLPlatformKeyAndroid(SSLPrivateKey::Type type, |
+ jobject key, |
+ size_t max_length) |
+ : type_(type), max_length_(max_length) { |
+ key_.Reset(nullptr, key); |
+ } |
~SSLPlatformKeyAndroid() override {} |
@@ -37,92 +119,88 @@ class SSLPlatformKeyAndroid : public ThreadedSSLPrivateKey::Delegate { |
kHashes + arraysize(kHashes)); |
} |
- size_t GetMaxSignatureLengthInBytes() override { |
- return EVP_PKEY_size(key_.get()); |
- } |
+ size_t GetMaxSignatureLengthInBytes() override { return max_length_; } |
Error SignDigest(SSLPrivateKey::Hash hash, |
const base::StringPiece& input, |
std::vector<uint8_t>* signature) override { |
- crypto::ScopedEVP_PKEY_CTX ctx = |
- crypto::ScopedEVP_PKEY_CTX(EVP_PKEY_CTX_new(key_.get(), NULL)); |
- if (!ctx) |
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; |
- if (!EVP_PKEY_sign_init(ctx.get())) |
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; |
- |
- if (type_ == SSLPrivateKey::Type::RSA) { |
- const EVP_MD* digest = nullptr; |
- switch (hash) { |
- case SSLPrivateKey::Hash::MD5_SHA1: |
- digest = EVP_md5_sha1(); |
- break; |
- case SSLPrivateKey::Hash::SHA1: |
- digest = EVP_sha1(); |
- break; |
- case SSLPrivateKey::Hash::SHA256: |
- digest = EVP_sha256(); |
- break; |
- case SSLPrivateKey::Hash::SHA384: |
- digest = EVP_sha384(); |
- break; |
- case SSLPrivateKey::Hash::SHA512: |
- digest = EVP_sha512(); |
- break; |
- default: |
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; |
- } |
- DCHECK(digest); |
- if (!EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_PADDING)) |
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; |
- if (!EVP_PKEY_CTX_set_signature_md(ctx.get(), digest)) |
+ const int kAndroid42ApiLevel = 17; |
+ if (base::android::BuildInfo::GetInstance()->sdk_int() >= |
+ kAndroid42ApiLevel) { |
+ if (!android::RawSignDigestWithPrivateKey(key_.obj(), input, signature)) { |
+ LOG(ERROR) << "Could not sign message"; |
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; |
+ } |
+ return OK; |
} |
- const uint8_t* input_ptr = reinterpret_cast<const uint8_t*>(input.data()); |
- size_t input_len = input.size(); |
- size_t sig_len = 0; |
- if (!EVP_PKEY_sign(ctx.get(), NULL, &sig_len, input_ptr, input_len)) |
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; |
- signature->resize(sig_len); |
- if (!EVP_PKEY_sign(ctx.get(), signature->data(), &sig_len, input_ptr, |
- input_len)) { |
+ // Pre-4.2 Legacy Codepath |
+ android::AndroidRSA* legacy_rsa = GetLegacyRSAKey(key_.obj()); |
+ signature->resize(max_length_); |
+ int ret = legacy_rsa->meth->rsa_priv_enc( |
+ input.size(), reinterpret_cast<const uint8_t*>(input.data()), |
+ signature->data(), legacy_rsa, android::ANDROID_RSA_PKCS1_PADDING); |
+ if (ret < 0) { |
+ // System OpenSSL will use a separate error queue, so it is still |
+ // necessary to push a new error. |
+ LOG(ERROR) << "Could not sign message with legacy RSA key"; |
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED; |
} |
- |
- signature->resize(sig_len); |
- |
+ signature->resize(ret); |
return OK; |
} |
private: |
- crypto::ScopedEVP_PKEY key_; |
SSLPrivateKey::Type type_; |
+ ScopedJavaGlobalRef<jobject> key_; |
+ size_t max_length_; |
DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyAndroid); |
}; |
+// 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; |
+} |
+ |
} // namespace |
-scoped_refptr<SSLPrivateKey> WrapOpenSSLPrivateKey(crypto::ScopedEVP_PKEY key) { |
+scoped_refptr<SSLPrivateKey> WrapJavaPrivateKey(jobject key) { |
if (!key) |
return nullptr; |
SSLPrivateKey::Type type; |
- switch (EVP_PKEY_id(key.get())) { |
- case EVP_PKEY_RSA: |
+ std::vector<uint8_t> key_params; |
+ switch (android::GetPrivateKeyType(key)) { |
+ case android::PRIVATE_KEY_TYPE_RSA: |
type = SSLPrivateKey::Type::RSA; |
+ if (!android::GetRSAKeyModulus(key, &key_params)) { |
+ LOG(ERROR) << "Failed to get private key modulus"; |
+ return nullptr; |
+ } |
break; |
- case EVP_PKEY_EC: |
+ case android::PRIVATE_KEY_TYPE_ECDSA: |
type = SSLPrivateKey::Type::ECDSA; |
+ if (!android::GetECKeyOrder(key, &key_params)) { |
+ LOG(ERROR) << "Failed to get private key order"; |
+ return nullptr; |
+ } |
break; |
default: |
- LOG(ERROR) << "Unknown key type: " << EVP_PKEY_id(key.get()); |
+ LOG(ERROR) << "Unknown key type: " << android::GetPrivateKeyType(key); |
return nullptr; |
} |
- return make_scoped_refptr(new ThreadedSSLPrivateKey( |
- make_scoped_ptr(new SSLPlatformKeyAndroid(std::move(key), type)), |
- GetSSLPlatformKeyTaskRunner())); |
+ |
+ return make_scoped_refptr( |
+ new ThreadedSSLPrivateKey(make_scoped_ptr(new SSLPlatformKeyAndroid( |
+ type, key, VectorBignumSize(key_params))), |
+ GetSSLPlatformKeyTaskRunner())); |
} |
} // namespace net |