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

Unified Diff: net/ssl/ssl_platform_key_android.cc

Issue 2291213002: Remove ENGINE indirection from Android SSLPrivateKey. (Closed)
Patch Set: re-delete undeleted files Created 4 years, 3 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/ssl/ssl_platform_key_android.h ('k') | net/ssl/ssl_platform_key_android_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 800107a5dc4747c153bf0dc130df2de5fe4959df..b4cc1a792c096cf246198999cc7b5ac330614db6 100644
--- a/net/ssl/ssl_platform_key_android.cc
+++ b/net/ssl/ssl_platform_key_android.cc
@@ -2,31 +2,84 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "net/ssl/ssl_platform_key.h"
+#include "net/ssl/ssl_platform_key_android.h"
-#include <openssl/digest.h>
-#include <openssl/evp.h>
+#include <openssl/ecdsa.h>
+#include <openssl/rsa.h>
+#include <strings.h>
+#include <memory>
#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 "base/memory/ptr_util.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/openssl_client_key_store.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::JavaRef;
+using base::android::ScopedJavaGlobalRef;
+using base::android::ScopedJavaLocalRef;
+
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(const JavaRef<jobject>& key) {
+ if (!engine_.is_null())
+ return;
+ ScopedJavaLocalRef<jobject> engine =
+ android::GetOpenSSLEngineForPrivateKey(key);
+ if (engine.is_null()) {
+ NOTREACHED();
+ return;
+ }
+ engine_.Reset(engine);
+ }
+
+ private:
+ ScopedJavaGlobalRef<jobject> engine_;
+};
+
+void LeakEngine(const JavaRef<jobject>& private_key) {
+ static base::LazyInstance<KeystoreEngineWorkaround>::Leaky s_instance =
+ LAZY_INSTANCE_INITIALIZER;
+ s_instance.Get().LeakEngine(private_key);
+}
+
class SSLPlatformKeyAndroid : public ThreadedSSLPrivateKey::Delegate {
public:
- SSLPlatformKeyAndroid(crypto::ScopedEVP_PKEY key, SSLPrivateKey::Type type)
- : key_(std::move(key)), type_(type) {}
+ SSLPlatformKeyAndroid(SSLPrivateKey::Type type,
+ const JavaRef<jobject>& key,
+ size_t max_length,
+ android::AndroidRSA* legacy_rsa)
+ : type_(type), max_length_(max_length), legacy_rsa_(legacy_rsa) {
+ key_.Reset(key);
+ }
~SSLPlatformKeyAndroid() override {}
@@ -40,100 +93,176 @@ 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,
+ const base::StringPiece& input_in,
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;
+ base::StringPiece input = input_in;
+ // Prepend the DigestInfo for RSA.
+ crypto::ScopedOpenSSLBytes digest_info_storage;
if (type_ == SSLPrivateKey::Type::RSA) {
- const EVP_MD* digest = nullptr;
+ int hash_nid = NID_undef;
switch (hash) {
case SSLPrivateKey::Hash::MD5_SHA1:
- digest = EVP_md5_sha1();
+ hash_nid = NID_md5_sha1;
break;
case SSLPrivateKey::Hash::SHA1:
- digest = EVP_sha1();
+ hash_nid = NID_sha1;
break;
case SSLPrivateKey::Hash::SHA256:
- digest = EVP_sha256();
+ hash_nid = NID_sha256;
break;
case SSLPrivateKey::Hash::SHA384:
- digest = EVP_sha384();
+ hash_nid = NID_sha384;
break;
case SSLPrivateKey::Hash::SHA512:
- digest = EVP_sha512();
+ hash_nid = NID_sha512;
break;
- default:
- return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
}
- DCHECK(digest);
- if (!EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_PADDING))
+ DCHECK_NE(NID_undef, hash_nid);
+
+ uint8_t* digest_info;
+ size_t digest_info_len;
+ int is_alloced;
+ if (!RSA_add_pkcs1_prefix(
+ &digest_info, &digest_info_len, &is_alloced, hash_nid,
+ reinterpret_cast<const uint8_t*>(input.data()), input.size())) {
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
- if (!EVP_PKEY_CTX_set_signature_md(ctx.get(), digest))
+ }
+
+ if (is_alloced)
+ digest_info_storage.reset(digest_info);
+ input = base::StringPiece(reinterpret_cast<const char*>(digest_info),
+ digest_info_len);
+ }
+
+ // Pre-4.2 legacy codepath.
+ if (legacy_rsa_) {
+ 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) {
+ LOG(WARNING) << "Could not sign message with legacy RSA key!";
+ // 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.)
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
+ }
+ signature->resize(ret);
+ 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)) {
+ if (!android::RawSignDigestWithPrivateKey(key_, input, signature)) {
+ LOG(WARNING) << "Could not sign message with private key!";
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
}
-
- signature->resize(sig_len);
-
return OK;
}
private:
- crypto::ScopedEVP_PKEY key_;
SSLPrivateKey::Type type_;
+ ScopedJavaGlobalRef<jobject> key_;
+ size_t max_length_;
+ android::AndroidRSA* legacy_rsa_;
DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyAndroid);
};
-scoped_refptr<SSLPrivateKey> WrapOpenSSLPrivateKey(crypto::ScopedEVP_PKEY key) {
- if (!key)
+// 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;
+}
+
+std::unique_ptr<SSLPlatformKeyAndroid> CreateRsaKey(
+ const JavaRef<jobject>& key) {
+ android::AndroidRSA* sys_rsa = nullptr;
+ const int kAndroid42ApiLevel = 17;
+ if (base::android::BuildInfo::GetInstance()->sdk_int() < kAndroid42ApiLevel) {
+ // Route around platform limitations: 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(key);
+ if (!sys_pkey)
+ return nullptr;
+
+ if (sys_pkey->type != android::ANDROID_EVP_PKEY_RSA) {
+ LOG(ERROR) << "Private key has wrong type!";
+ return nullptr;
+ }
+
+ 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(key);
+ } else {
+ NOTREACHED();
+ }
+ }
+ }
+
+ std::vector<uint8_t> modulus;
+ if (!android::GetRSAKeyModulus(key, &modulus)) {
+ LOG(ERROR) << "Failed to get private key modulus";
return nullptr;
+ }
- SSLPrivateKey::Type type;
- switch (EVP_PKEY_id(key.get())) {
- case EVP_PKEY_RSA:
- type = SSLPrivateKey::Type::RSA;
+ return base::MakeUnique<SSLPlatformKeyAndroid>(
+ SSLPrivateKey::Type::RSA, key, VectorBignumSize(modulus), sys_rsa);
+}
+
+std::unique_ptr<SSLPlatformKeyAndroid> CreateEcdsaKey(
+ const JavaRef<jobject>& key) {
+ std::vector<uint8_t> order;
+ if (!android::GetECKeyOrder(key, &order)) {
+ LOG(ERROR) << "Can't extract order parameter from EC private key";
+ return nullptr;
+ }
+
+ return base::MakeUnique<SSLPlatformKeyAndroid>(
+ SSLPrivateKey::Type::ECDSA, key,
+ ECDSA_SIG_max_len(VectorBignumSize(order)), nullptr);
+}
+
+} // namespace
+
+scoped_refptr<SSLPrivateKey> WrapJavaPrivateKey(const JavaRef<jobject>& key) {
+ std::unique_ptr<SSLPlatformKeyAndroid> delegate;
+ switch (android::GetPrivateKeyType(key)) {
+ case android::PRIVATE_KEY_TYPE_RSA:
+ delegate = CreateRsaKey(key);
break;
- case EVP_PKEY_EC:
- type = SSLPrivateKey::Type::ECDSA;
+ case android::PRIVATE_KEY_TYPE_ECDSA:
+ delegate = CreateEcdsaKey(key);
break;
default:
- LOG(ERROR) << "Unknown key type: " << EVP_PKEY_id(key.get());
+ LOG(WARNING) << "GetPrivateKeyType() returned invalid type";
return nullptr;
}
+
return make_scoped_refptr(new ThreadedSSLPrivateKey(
- base::WrapUnique(new SSLPlatformKeyAndroid(std::move(key), type)),
- GetSSLPlatformKeyTaskRunner()));
+ std::move(delegate), GetSSLPlatformKeyTaskRunner()));
}
-} // namespace
-
scoped_refptr<SSLPrivateKey> FetchClientCertPrivateKey(
X509Certificate* certificate) {
- crypto::ScopedEVP_PKEY key =
- OpenSSLClientKeyStore::GetInstance()->FetchClientCertPrivateKey(
- certificate);
- return WrapOpenSSLPrivateKey(std::move(key));
+ return OpenSSLClientKeyStore::GetInstance()->FetchClientCertPrivateKey(
+ certificate);
}
} // namespace net
« no previous file with comments | « net/ssl/ssl_platform_key_android.h ('k') | net/ssl/ssl_platform_key_android_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698