mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-01 01:36:09 +03:00
232 lines
7.4 KiB
C++
232 lines
7.4 KiB
C++
// Copyright 2015 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/ssl/ssl_platform_key_android.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 "net/android/keystore.h"
|
|
#include "net/android/legacy_openssl.h"
|
|
#include "net/base/net_errors.h"
|
|
#include "net/ssl/ssl_platform_key_util.h"
|
|
#include "net/ssl/threaded_ssl_private_key.h"
|
|
#include "third_party/boringssl/src/include/openssl/ecdsa.h"
|
|
#include "third_party/boringssl/src/include/openssl/evp.h"
|
|
#include "third_party/boringssl/src/include/openssl/mem.h"
|
|
#include "third_party/boringssl/src/include/openssl/nid.h"
|
|
#include "third_party/boringssl/src/include/openssl/rsa.h"
|
|
#include "third_party/boringssl/src/include/openssl/ssl.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);
|
|
}
|
|
|
|
const char* GetJavaAlgorithm(uint16_t algorithm) {
|
|
switch (algorithm) {
|
|
case SSL_SIGN_RSA_PKCS1_SHA1:
|
|
return "SHA1withRSA";
|
|
case SSL_SIGN_RSA_PKCS1_SHA256:
|
|
return "SHA256withRSA";
|
|
case SSL_SIGN_RSA_PKCS1_SHA384:
|
|
return "SHA384withRSA";
|
|
case SSL_SIGN_RSA_PKCS1_SHA512:
|
|
return "SHA512withRSA";
|
|
case SSL_SIGN_ECDSA_SHA1:
|
|
return "SHA1withECDSA";
|
|
case SSL_SIGN_ECDSA_SECP256R1_SHA256:
|
|
return "SHA256withECDSA";
|
|
case SSL_SIGN_ECDSA_SECP384R1_SHA384:
|
|
return "SHA384withECDSA";
|
|
case SSL_SIGN_ECDSA_SECP521R1_SHA512:
|
|
return "SHA512withECDSA";
|
|
case SSL_SIGN_RSA_PSS_SHA256:
|
|
return "SHA256withRSA/PSS";
|
|
case SSL_SIGN_RSA_PSS_SHA384:
|
|
return "SHA384withRSA/PSS";
|
|
case SSL_SIGN_RSA_PSS_SHA512:
|
|
return "SHA512withRSA/PSS";
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
class SSLPlatformKeyAndroid : public ThreadedSSLPrivateKey::Delegate {
|
|
public:
|
|
SSLPlatformKeyAndroid(int 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 {}
|
|
|
|
std::vector<uint16_t> GetAlgorithmPreferences() override {
|
|
bool supports_pss = base::android::BuildInfo::GetInstance()->sdk_int() >=
|
|
base::android::SDK_VERSION_NOUGAT;
|
|
return SSLPrivateKey::DefaultAlgorithmPreferences(type_, supports_pss);
|
|
}
|
|
|
|
Error Sign(uint16_t algorithm,
|
|
base::span<const uint8_t> input,
|
|
std::vector<uint8_t>* signature) override {
|
|
if (algorithm == SSL_SIGN_RSA_PKCS1_MD5_SHA1) {
|
|
// SSL_SIGN_RSA_PKCS1_MD5_SHA1 cannot be implemented with the Java
|
|
// signature API directly.
|
|
return SignRSAWithMD5SHA1(input, signature);
|
|
}
|
|
|
|
const char* java_algorithm = GetJavaAlgorithm(algorithm);
|
|
if (!java_algorithm) {
|
|
LOG(ERROR) << "Unknown algorithm " << algorithm;
|
|
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
|
|
}
|
|
if (!android::SignWithPrivateKey(key_, java_algorithm, input, signature)) {
|
|
LOG(ERROR) << "Could not sign message with private key!";
|
|
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
private:
|
|
Error SignRSAWithMD5SHA1(base::span<const uint8_t> input,
|
|
std::vector<uint8_t>* signature) {
|
|
uint8_t digest[EVP_MAX_MD_SIZE];
|
|
unsigned digest_len;
|
|
if (!EVP_Digest(input.data(), input.size(), digest, &digest_len,
|
|
EVP_md5_sha1(), nullptr)) {
|
|
LOG(ERROR) << "Could not take digest.";
|
|
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
|
|
}
|
|
|
|
// Android prior to 4.2 did not implement NONEwithRSA. Workaround this with
|
|
// legacy_rsa_.
|
|
if (legacy_rsa_) {
|
|
signature->resize(max_length_);
|
|
int ret = legacy_rsa_->meth->rsa_priv_enc(
|
|
digest_len, digest, signature->data(), legacy_rsa_,
|
|
android::ANDROID_RSA_PKCS1_PADDING);
|
|
if (ret < 0) {
|
|
LOG(ERROR) << "Could not sign message with legacy RSA key!";
|
|
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
|
|
}
|
|
signature->resize(ret);
|
|
return OK;
|
|
}
|
|
|
|
if (!android::SignWithPrivateKey(key_, "NONEwithRSA",
|
|
base::make_span(digest, digest_len),
|
|
signature)) {
|
|
LOG(ERROR) << "Could not sign message with private key!";
|
|
return ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
int type_;
|
|
ScopedJavaGlobalRef<jobject> key_;
|
|
size_t max_length_;
|
|
android::AndroidRSA* legacy_rsa_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyAndroid);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
scoped_refptr<SSLPrivateKey> WrapJavaPrivateKey(
|
|
const X509Certificate* certificate,
|
|
const JavaRef<jobject>& key) {
|
|
int type;
|
|
size_t max_length;
|
|
if (!GetClientCertInfo(certificate, &type, &max_length))
|
|
return nullptr;
|
|
|
|
android::AndroidRSA* sys_rsa = nullptr;
|
|
if (type == EVP_PKEY_RSA) {
|
|
if (base::android::BuildInfo::GetInstance()->sdk_int() <
|
|
base::android::SDK_VERSION_JELLY_BEAN_MR1) {
|
|
// Route around platform limitations: if Android < 4.2 (Jelly
|
|
// Bean MR1), 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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return base::MakeRefCounted<ThreadedSSLPrivateKey>(
|
|
std::make_unique<SSLPlatformKeyAndroid>(type, key, max_length, sys_rsa),
|
|
GetSSLPlatformKeyTaskRunner());
|
|
}
|
|
|
|
} // namespace net
|