| 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..cbb3aa7fda0a8d2124e25ee0c230379383b8ac70 100644
|
| --- a/net/ssl/ssl_platform_key_android.cc
|
| +++ b/net/ssl/ssl_platform_key_android.cc
|
| @@ -2,31 +2,83 @@
|
| // 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::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(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(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,
|
| + jobject key,
|
| + size_t max_length,
|
| + android::AndroidRSA* legacy_rsa)
|
| + : type_(type), max_length_(max_length), legacy_rsa_(legacy_rsa) {
|
| + key_.Reset(nullptr, key);
|
| + }
|
|
|
| ~SSLPlatformKeyAndroid() override {}
|
|
|
| @@ -40,100 +92,174 @@ 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_.obj(), 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(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(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(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
|
|
|