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

Unified Diff: net/ssl/ssl_platform_key_android.cc

Issue 1640833002: Android SSLPrivateKey Work (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix rebase. Created 4 years, 11 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') | no next file » | 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 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
« no previous file with comments | « net/ssl/ssl_platform_key_android.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698