platform_system_security/keystore-engine/keystore2_engine.cpp
David Benjamin 66d00307aa Use BoringSSL accessors to set up EVP_PKEY.
Constructing an RSA object is sadly a bit tedious, but that's the API
OpenSSL came up with. While I'm here, switch an EVP_PKEY_get1_RSA to
EVP_PKEY_get0_RSA; we can avoid an unnecessary refcount bump.

Test: mm
Change-Id: I8b368ea1efa71e3f8b3cd0c734c1cdd0f2c964e5
2023-01-30 16:02:49 -05:00

442 lines
16 KiB
C++

/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "keystore2_engine.h"
#include <aidl/android/system/keystore2/IKeystoreService.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android/binder_manager.h>
#include <private/android_filesystem_config.h>
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/ec_key.h>
#include <openssl/ecdsa.h>
#include <openssl/engine.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#define AT __func__ << ":" << __LINE__ << " "
constexpr const char keystore2_service_name[] = "android.system.keystore2.IKeystoreService/default";
const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:");
/**
* Keystore 2.0 namespace identifiers.
* Keep in sync with system/sepolicy/private/keystore2_key_contexts.
*/
constexpr const int64_t KS2_NAMESPACE_WIFI = 102;
namespace ks2 = ::aidl::android::system::keystore2;
namespace KMV1 = ::aidl::android::hardware::security::keymint;
namespace {
int64_t getNamespaceforCurrentUid() {
auto uid = getuid();
switch (uid) {
case AID_WIFI:
return KS2_NAMESPACE_WIFI;
// 0 is the super user namespace, and nothing has access to this namespace on user builds.
// So this will always fail.
default:
return 0;
}
}
struct Keystore2KeyBackend {
ks2::KeyDescriptor descriptor_;
std::shared_ptr<ks2::IKeystoreSecurityLevel> i_keystore_security_level_;
};
/* key_backend_dup is called when one of the RSA or EC_KEY objects is duplicated. */
extern "C" int key_backend_dup(CRYPTO_EX_DATA* /* to */, const CRYPTO_EX_DATA* /* from */,
void** from_d, int /* index */, long /* argl */, void* /* argp */) {
auto key_backend = reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(*from_d);
if (key_backend != nullptr) {
*from_d = new std::shared_ptr<Keystore2KeyBackend>(*key_backend);
}
return 1;
}
/* key_backend_free is called when one of the RSA, DSA or EC_KEY object is freed. */
extern "C" void key_backend_free(void* /* parent */, void* ptr, CRYPTO_EX_DATA* /* ad */,
int /* index */, long /* argl */, void* /* argp */) {
delete reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(ptr);
}
extern "C" int rsa_private_transform(RSA* rsa, uint8_t* out, const uint8_t* in, size_t len);
extern "C" int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig,
unsigned int* sig_len, EC_KEY* ec_key);
/* KeystoreEngine is a BoringSSL ENGINE that implements RSA and ECDSA by
* forwarding the requested operations to Keystore. */
class Keystore2Engine {
public:
Keystore2Engine()
: rsa_index_(RSA_get_ex_new_index(0 /* argl */, nullptr /* argp */, nullptr /* new_func */,
key_backend_dup, key_backend_free)),
ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */, nullptr /* argp */,
nullptr /* new_func */, key_backend_dup,
key_backend_free)),
engine_(ENGINE_new()) {
memset(&rsa_method_, 0, sizeof(rsa_method_));
rsa_method_.common.is_static = 1;
rsa_method_.private_transform = rsa_private_transform;
rsa_method_.flags = RSA_FLAG_OPAQUE;
ENGINE_set_RSA_method(engine_, &rsa_method_, sizeof(rsa_method_));
memset(&ecdsa_method_, 0, sizeof(ecdsa_method_));
ecdsa_method_.common.is_static = 1;
ecdsa_method_.sign = ecdsa_sign;
ecdsa_method_.flags = ECDSA_FLAG_OPAQUE;
ENGINE_set_ECDSA_method(engine_, &ecdsa_method_, sizeof(ecdsa_method_));
}
int rsa_ex_index() const { return rsa_index_; }
int ec_key_ex_index() const { return ec_key_index_; }
const ENGINE* engine() const { return engine_; }
static const Keystore2Engine& get() {
static Keystore2Engine engine;
return engine;
}
private:
const int rsa_index_;
const int ec_key_index_;
RSA_METHOD rsa_method_;
ECDSA_METHOD ecdsa_method_;
ENGINE* const engine_;
};
#define OWNERSHIP_TRANSFERRED(x) x.release()
/* wrap_rsa returns an |EVP_PKEY| that contains an RSA key where the public
* part is taken from |public_rsa| and the private operations are forwarded to
* KeyStore and operate on the key named |key_id|. */
bssl::UniquePtr<EVP_PKEY> wrap_rsa(std::shared_ptr<Keystore2KeyBackend> key_backend,
const RSA* public_rsa) {
bssl::UniquePtr<RSA> rsa(RSA_new_method(Keystore2Engine::get().engine()));
if (rsa.get() == nullptr) {
return nullptr;
}
auto key_backend_copy = new decltype(key_backend)(key_backend);
if (!RSA_set_ex_data(rsa.get(), Keystore2Engine::get().rsa_ex_index(), key_backend_copy)) {
delete key_backend_copy;
return nullptr;
}
bssl::UniquePtr<BIGNUM> n(BN_dup(RSA_get0_n(public_rsa)));
bssl::UniquePtr<BIGNUM> e(BN_dup(RSA_get0_e(public_rsa)));
if (n == nullptr || e == nullptr || !RSA_set0_key(rsa.get(), n.get(), e.get(), nullptr)) {
return nullptr;
}
OWNERSHIP_TRANSFERRED(n);
OWNERSHIP_TRANSFERRED(e);
bssl::UniquePtr<EVP_PKEY> result(EVP_PKEY_new());
if (result.get() == nullptr || !EVP_PKEY_assign_RSA(result.get(), rsa.get())) {
return nullptr;
}
OWNERSHIP_TRANSFERRED(rsa);
return result;
}
/* wrap_ecdsa returns an |EVP_PKEY| that contains an ECDSA key where the public
* part is taken from |public_rsa| and the private operations are forwarded to
* KeyStore and operate on the key named |key_id|. */
bssl::UniquePtr<EVP_PKEY> wrap_ecdsa(std::shared_ptr<Keystore2KeyBackend> key_backend,
const EC_KEY* public_ecdsa) {
bssl::UniquePtr<EC_KEY> ec(EC_KEY_new_method(Keystore2Engine::get().engine()));
if (ec.get() == nullptr) {
return nullptr;
}
if (!EC_KEY_set_group(ec.get(), EC_KEY_get0_group(public_ecdsa)) ||
!EC_KEY_set_public_key(ec.get(), EC_KEY_get0_public_key(public_ecdsa))) {
return nullptr;
}
auto key_backend_copy = new decltype(key_backend)(key_backend);
if (!EC_KEY_set_ex_data(ec.get(), Keystore2Engine::get().ec_key_ex_index(), key_backend_copy)) {
delete key_backend_copy;
return nullptr;
}
bssl::UniquePtr<EVP_PKEY> result(EVP_PKEY_new());
if (result.get() == nullptr || !EVP_PKEY_assign_EC_KEY(result.get(), ec.get())) {
return nullptr;
}
OWNERSHIP_TRANSFERRED(ec);
return result;
}
std::optional<std::vector<uint8_t>> keystore2_sign(const Keystore2KeyBackend& key_backend,
std::vector<uint8_t> input,
KMV1::Algorithm algorithm) {
auto sec_level = key_backend.i_keystore_security_level_;
ks2::CreateOperationResponse response;
std::vector<KMV1::KeyParameter> op_params(4);
op_params[0] = KMV1::KeyParameter{
.tag = KMV1::Tag::PURPOSE,
.value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::keyPurpose>(
KMV1::KeyPurpose::SIGN)};
op_params[1] = KMV1::KeyParameter{
.tag = KMV1::Tag::ALGORITHM,
.value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::algorithm>(algorithm)};
op_params[2] = KMV1::KeyParameter{
.tag = KMV1::Tag::PADDING,
.value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::paddingMode>(
KMV1::PaddingMode::NONE)};
op_params[3] =
KMV1::KeyParameter{.tag = KMV1::Tag::DIGEST,
.value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::digest>(
KMV1::Digest::NONE)};
auto rc = sec_level->createOperation(key_backend.descriptor_, op_params, false /* forced */,
&response);
if (!rc.isOk()) {
auto exception_code = rc.getExceptionCode();
if (exception_code == EX_SERVICE_SPECIFIC) {
LOG(ERROR) << AT << "Keystore createOperation returned service specific error: "
<< rc.getServiceSpecificError();
} else {
LOG(ERROR) << AT << "Communication with Keystore createOperation failed error: "
<< exception_code;
}
return std::nullopt;
}
auto op = response.iOperation;
std::optional<std::vector<uint8_t>> output = std::nullopt;
rc = op->finish(std::move(input), {}, &output);
if (!rc.isOk()) {
auto exception_code = rc.getExceptionCode();
if (exception_code == EX_SERVICE_SPECIFIC) {
LOG(ERROR) << AT << "Keystore finish returned service specific error: "
<< rc.getServiceSpecificError();
} else {
LOG(ERROR) << AT
<< "Communication with Keystore finish failed error: " << exception_code;
}
return std::nullopt;
}
if (!output) {
LOG(ERROR) << AT << "We did not get a signature from Keystore.";
}
return output;
}
/* rsa_private_transform takes a big-endian integer from |in|, calculates the
* d'th power of it, modulo the RSA modulus, and writes the result as a
* big-endian integer to |out|. Both |in| and |out| are |len| bytes long. It
* returns one on success and zero otherwise. */
extern "C" int rsa_private_transform(RSA* rsa, uint8_t* out, const uint8_t* in, size_t len) {
auto key_backend = reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(
RSA_get_ex_data(rsa, Keystore2Engine::get().rsa_ex_index()));
if (key_backend == nullptr) {
LOG(ERROR) << AT << "Invalid key.";
return 0;
}
auto output =
keystore2_sign(**key_backend, std::vector<uint8_t>(in, in + len), KMV1::Algorithm::RSA);
if (!output) {
return 0;
}
if (output->size() > len) {
/* The result of the RSA operation can never be larger than the size of
* the modulus so we assume that the result has extra zeros on the
* left. This provides attackers with an oracle, but there's nothing
* that we can do about it here. */
LOG(WARNING) << "Reply len " << output->size() << " greater than expected " << len;
memcpy(out, &output->data()[output->size() - len], len);
} else if (output->size() < len) {
/* If the Keystore implementation returns a short value we assume that
* it's because it removed leading zeros from the left side. This is
* bad because it provides attackers with an oracle but we cannot do
* anything about a broken Keystore implementation here. */
LOG(WARNING) << "Reply len " << output->size() << " less than expected " << len;
memset(out, 0, len);
memcpy(out + len - output->size(), output->data(), output->size());
} else {
memcpy(out, output->data(), len);
}
return 1;
}
/* ecdsa_sign signs |digest_len| bytes from |digest| with |ec_key| and writes
* the resulting signature (an ASN.1 encoded blob) to |sig|. It returns one on
* success and zero otherwise. */
extern "C" int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig,
unsigned int* sig_len, EC_KEY* ec_key) {
auto key_backend = reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(
EC_KEY_get_ex_data(ec_key, Keystore2Engine::get().ec_key_ex_index()));
if (key_backend == nullptr) {
LOG(ERROR) << AT << "Invalid key.";
return 0;
}
size_t ecdsa_size = ECDSA_size(ec_key);
auto output = keystore2_sign(**key_backend, std::vector<uint8_t>(digest, digest + digest_len),
KMV1::Algorithm::EC);
if (!output) {
LOG(ERROR) << "There was an error during ecdsa_sign.";
return 0;
}
if (output->size() == 0) {
LOG(ERROR) << "No valid signature returned";
return 0;
} else if (output->size() > ecdsa_size) {
LOG(ERROR) << "Signature is too large";
return 0;
}
memcpy(sig, output->data(), output->size());
*sig_len = output->size();
return 1;
}
bssl::UniquePtr<EVP_PKEY> extractPubKey(const std::vector<uint8_t>& cert_bytes) {
const uint8_t* p = cert_bytes.data();
bssl::UniquePtr<X509> decoded_cert(d2i_X509(nullptr, &p, cert_bytes.size()));
if (!decoded_cert) {
LOG(INFO) << AT << "Could not decode the cert, trying decoding as PEM";
bssl::UniquePtr<BIO> cert_bio(BIO_new_mem_buf(cert_bytes.data(), cert_bytes.size()));
if (!cert_bio) {
LOG(ERROR) << AT << "Failed to create BIO";
return {};
}
decoded_cert =
bssl::UniquePtr<X509>(PEM_read_bio_X509(cert_bio.get(), nullptr, nullptr, nullptr));
}
if (!decoded_cert) {
LOG(ERROR) << AT << "Could not decode the cert.";
return {};
}
bssl::UniquePtr<EVP_PKEY> pub_key(X509_get_pubkey(decoded_cert.get()));
if (!pub_key) {
LOG(ERROR) << AT << "Could not extract public key.";
return {};
}
return pub_key;
}
} // namespace
/* EVP_PKEY_from_keystore returns an |EVP_PKEY| that contains either an RSA or
* ECDSA key where the public part of the key reflects the value of the key
* named |key_id| in Keystore and the private operations are forwarded onto
* KeyStore. */
extern "C" EVP_PKEY* EVP_PKEY_from_keystore2(const char* key_id) {
::ndk::SpAIBinder keystoreBinder(AServiceManager_checkService(keystore2_service_name));
auto keystore2 = ks2::IKeystoreService::fromBinder(keystoreBinder);
if (!keystore2) {
LOG(ERROR) << AT << "Unable to connect to Keystore 2.0.";
return nullptr;
}
std::string alias = key_id;
if (android::base::StartsWith(alias, "USRPKEY_")) {
LOG(WARNING) << AT << "Keystore backend used with legacy alias prefix - ignoring.";
alias = alias.substr(8);
}
ks2::KeyDescriptor descriptor = {
.domain = ks2::Domain::SELINUX,
.nspace = getNamespaceforCurrentUid(),
.alias = alias,
.blob = std::nullopt,
};
// If the key_id starts with the grant id prefix, we parse the following string as numeric
// grant id. We can then use the grant domain without alias to load the designated key.
if (android::base::StartsWith(alias, keystore2_grant_id_prefix)) {
std::stringstream s(alias.substr(keystore2_grant_id_prefix.size()));
s >> std::hex >> reinterpret_cast<uint64_t&>(descriptor.nspace);
descriptor.domain = ks2::Domain::GRANT;
descriptor.alias = std::nullopt;
}
ks2::KeyEntryResponse response;
auto rc = keystore2->getKeyEntry(descriptor, &response);
if (!rc.isOk()) {
auto exception_code = rc.getExceptionCode();
if (exception_code == EX_SERVICE_SPECIFIC) {
LOG(ERROR) << AT << "Keystore getKeyEntry returned service specific error: "
<< rc.getServiceSpecificError();
} else {
LOG(ERROR) << AT << "Communication with Keystore getKeyEntry failed error: "
<< exception_code;
}
return nullptr;
}
if (!response.metadata.certificate) {
LOG(ERROR) << AT << "No public key found.";
return nullptr;
}
auto pkey = extractPubKey(*response.metadata.certificate);
if (!pkey) {
LOG(ERROR) << AT << "Failed to extract public key.";
return nullptr;
}
auto key_backend = std::make_shared<Keystore2KeyBackend>(
Keystore2KeyBackend{response.metadata.key, response.iSecurityLevel});
bssl::UniquePtr<EVP_PKEY> result;
switch (EVP_PKEY_id(pkey.get())) {
case EVP_PKEY_RSA: {
RSA* public_rsa = EVP_PKEY_get0_RSA(pkey.get());
result = wrap_rsa(key_backend, public_rsa);
break;
}
case EVP_PKEY_EC: {
EC_KEY* public_ecdsa = EVP_PKEY_get0_EC_KEY(pkey.get());
result = wrap_ecdsa(key_backend, public_ecdsa);
break;
}
default:
LOG(ERROR) << AT << "Unsupported key type " << EVP_PKEY_id(pkey.get());
return nullptr;
}
return result.release();
}