You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
333 lines
12 KiB
333 lines
12 KiB
/*
|
|
**
|
|
** Copyright 2017, 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 <openssl/evp.h>
|
|
#include <openssl/x509v3.h>
|
|
|
|
#include <hardware/keymaster_defs.h>
|
|
|
|
#include <keymaster/authorization_set.h>
|
|
#include <keymaster/km_openssl/asymmetric_key.h>
|
|
#include <keymaster/km_openssl/attestation_record.h>
|
|
#include <keymaster/km_openssl/attestation_utils.h>
|
|
#include <keymaster/km_openssl/certificate_utils.h>
|
|
#include <keymaster/km_openssl/openssl_err.h>
|
|
#include <keymaster/km_openssl/openssl_utils.h>
|
|
|
|
namespace keymaster {
|
|
|
|
namespace {
|
|
|
|
CertificateChain make_cert_chain(X509* certificate, CertificateChain chain,
|
|
keymaster_error_t* error) {
|
|
keymaster_blob_t blob{};
|
|
*error = encode_certificate(certificate, &blob);
|
|
if (*error != KM_ERROR_OK) return {};
|
|
|
|
if (!chain.push_front(blob)) {
|
|
*error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
|
|
return {};
|
|
}
|
|
return chain;
|
|
}
|
|
|
|
keymaster_error_t build_attestation_extension(const AuthorizationSet& attest_params,
|
|
const AuthorizationSet& tee_enforced,
|
|
const AuthorizationSet& sw_enforced,
|
|
const AttestationContext& context,
|
|
X509_EXTENSION_Ptr* extension) {
|
|
ASN1_OBJECT_Ptr oid(
|
|
OBJ_txt2obj(kAsn1TokenOid, 1 /* accept numerical dotted string form only */));
|
|
if (!oid.get()) return TranslateLastOpenSslError();
|
|
|
|
UniquePtr<uint8_t[]> attest_bytes;
|
|
size_t attest_bytes_len;
|
|
keymaster_error_t error = build_attestation_record(attest_params, sw_enforced, tee_enforced,
|
|
context, &attest_bytes, &attest_bytes_len);
|
|
if (error != KM_ERROR_OK) return error;
|
|
|
|
ASN1_OCTET_STRING_Ptr attest_str(ASN1_OCTET_STRING_new());
|
|
if (!attest_str.get() ||
|
|
!ASN1_OCTET_STRING_set(attest_str.get(), attest_bytes.get(), attest_bytes_len)) {
|
|
return TranslateLastOpenSslError();
|
|
}
|
|
|
|
extension->reset(
|
|
X509_EXTENSION_create_by_OBJ(nullptr, oid.get(), 0 /* not critical */, attest_str.get()));
|
|
if (!extension->get()) {
|
|
return TranslateLastOpenSslError();
|
|
}
|
|
|
|
return KM_ERROR_OK;
|
|
}
|
|
|
|
keymaster_error_t build_eat_extension(const AuthorizationSet& attest_params,
|
|
const AuthorizationSet& tee_enforced,
|
|
const AuthorizationSet& sw_enforced,
|
|
const AttestationContext& context, //
|
|
X509_EXTENSION_Ptr* extension) {
|
|
ASN1_OBJECT_Ptr oid(
|
|
OBJ_txt2obj(kEatTokenOid, 1 /* accept numerical dotted string form only */));
|
|
if (!oid.get()) {
|
|
return TranslateLastOpenSslError();
|
|
}
|
|
|
|
std::vector<uint8_t> eat_bytes;
|
|
keymaster_error_t error =
|
|
build_eat_record(attest_params, sw_enforced, tee_enforced, context, &eat_bytes);
|
|
if (error != KM_ERROR_OK) return error;
|
|
|
|
ASN1_OCTET_STRING_Ptr eat_str(ASN1_OCTET_STRING_new());
|
|
if (!eat_str.get() ||
|
|
!ASN1_OCTET_STRING_set(eat_str.get(), eat_bytes.data(), eat_bytes.size())) {
|
|
return TranslateLastOpenSslError();
|
|
}
|
|
|
|
extension->reset(
|
|
X509_EXTENSION_create_by_OBJ(nullptr, oid.get(), 0 /* not critical */, eat_str.get()));
|
|
if (!extension->get()) {
|
|
return TranslateLastOpenSslError();
|
|
}
|
|
|
|
return KM_ERROR_OK;
|
|
}
|
|
|
|
keymaster_error_t add_attestation_extension(const AuthorizationSet& attest_params,
|
|
const AuthorizationSet& tee_enforced,
|
|
const AuthorizationSet& sw_enforced,
|
|
const AttestationContext& context, //
|
|
X509* certificate) {
|
|
X509_EXTENSION_Ptr attest_extension;
|
|
if (context.GetKmVersion() <= KmVersion::KEYMINT_1) {
|
|
if (auto error = build_attestation_extension(attest_params, tee_enforced, sw_enforced,
|
|
context, &attest_extension)) {
|
|
return error;
|
|
}
|
|
} else {
|
|
if (auto error = build_eat_extension(attest_params, tee_enforced, sw_enforced, context,
|
|
&attest_extension)) {
|
|
return error;
|
|
}
|
|
}
|
|
|
|
if (!X509_add_ext(certificate, attest_extension.get() /* Don't release; copied */,
|
|
-1 /* insert at end */)) {
|
|
return TranslateLastOpenSslError();
|
|
}
|
|
|
|
return KM_ERROR_OK;
|
|
}
|
|
|
|
keymaster_error_t make_attestation_cert(const EVP_PKEY* evp_pkey, const X509_NAME* issuer,
|
|
const CertificateCallerParams& cert_params,
|
|
const AuthorizationSet& attest_params,
|
|
const AuthorizationSet& tee_enforced,
|
|
const AuthorizationSet& sw_enforced,
|
|
const AttestationContext& context, X509_Ptr* cert_out) {
|
|
|
|
// First make the basic certificate with usage extension.
|
|
X509_Ptr certificate;
|
|
if (auto error = make_cert(evp_pkey, issuer, cert_params, &certificate)) {
|
|
return error;
|
|
}
|
|
|
|
// Add attestation extension.
|
|
if (auto error = add_attestation_extension(attest_params, tee_enforced, sw_enforced, context,
|
|
certificate.get())) {
|
|
return error;
|
|
}
|
|
|
|
*cert_out = move(certificate);
|
|
return KM_ERROR_OK;
|
|
}
|
|
|
|
X509_NAME_Ptr get_issuer_subject(const AttestKeyInfo& attest_key, keymaster_error_t* error) {
|
|
// Use subject from attest_key.
|
|
const uint8_t* p = attest_key.issuer_subject->data;
|
|
if (!p || !attest_key.issuer_subject->data_length) {
|
|
*error = KM_ERROR_MISSING_ISSUER_SUBJECT;
|
|
return {};
|
|
}
|
|
X509_NAME_Ptr retval(d2i_X509_NAME(nullptr /* Allocate X509_NAME */, &p,
|
|
attest_key.issuer_subject->data_length));
|
|
if (!retval) *error = KM_ERROR_INVALID_ISSUER_SUBJECT;
|
|
return retval;
|
|
}
|
|
|
|
X509_NAME_Ptr get_issuer_subject(const keymaster_blob_t& signing_cert_der,
|
|
keymaster_error_t* error) {
|
|
const uint8_t* p = signing_cert_der.data;
|
|
if (!p) {
|
|
*error = KM_ERROR_UNEXPECTED_NULL_POINTER;
|
|
return {};
|
|
}
|
|
X509_Ptr signing_cert(d2i_X509(nullptr /* Allocate X509 */, &p, signing_cert_der.data_length));
|
|
if (!signing_cert) {
|
|
*error = TranslateLastOpenSslError();
|
|
return {};
|
|
}
|
|
|
|
X509_NAME* issuer_subject = X509_get_subject_name(signing_cert.get());
|
|
if (!issuer_subject) {
|
|
*error = TranslateLastOpenSslError();
|
|
return {};
|
|
}
|
|
|
|
X509_NAME_Ptr retval(X509_NAME_dup(issuer_subject));
|
|
if (!retval) *error = TranslateLastOpenSslError();
|
|
|
|
return retval;
|
|
}
|
|
|
|
// Return subject from attest_key, if non-null, otherwise extract from cert_chain.
|
|
X509_NAME_Ptr get_issuer_subject(const AttestKeyInfo& attest_key,
|
|
const CertificateChain& cert_chain, keymaster_error_t* error) {
|
|
if (attest_key) {
|
|
return get_issuer_subject(attest_key, error);
|
|
}
|
|
|
|
// Need to extract issuer from cert chain. First cert in the chain is the signing key cert.
|
|
if (cert_chain.entry_count >= 1) return get_issuer_subject(cert_chain.entries[0], error);
|
|
|
|
*error = KM_ERROR_UNKNOWN_ERROR;
|
|
return {};
|
|
}
|
|
|
|
keymaster_error_t check_attest_key_auths(const Key& key) {
|
|
auto auths = key.authorizations();
|
|
|
|
if (!auths.Contains(TAG_ALGORITHM, KM_ALGORITHM_RSA) &&
|
|
!auths.Contains(TAG_ALGORITHM, KM_ALGORITHM_EC)) {
|
|
return KM_ERROR_INCOMPATIBLE_ALGORITHM;
|
|
}
|
|
if (!auths.Contains(TAG_PURPOSE, KM_PURPOSE_ATTEST_KEY)) {
|
|
return KM_ERROR_INCOMPATIBLE_PURPOSE;
|
|
}
|
|
return KM_ERROR_OK;
|
|
}
|
|
|
|
EVP_PKEY_Ptr get_attestation_key(keymaster_algorithm_t algorithm, const AttestationContext& context,
|
|
keymaster_error_t* error) {
|
|
KeymasterKeyBlob signing_key_blob = context.GetAttestationKey(algorithm, error);
|
|
if (*error != KM_ERROR_OK) return {};
|
|
|
|
const uint8_t* p = signing_key_blob.key_material;
|
|
EVP_PKEY_Ptr retval(
|
|
d2i_AutoPrivateKey(nullptr /* Allocate key */, &p, signing_key_blob.key_material_size));
|
|
if (!retval) *error = TranslateLastOpenSslError();
|
|
return retval;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
AttestKeyInfo::AttestKeyInfo(const UniquePtr<Key>& key, const KeymasterBlob* issuer_subject_,
|
|
keymaster_error_t* error)
|
|
: issuer_subject(issuer_subject_) {
|
|
if (!error) return;
|
|
|
|
if (!key) {
|
|
// No key... so this is just an empty AttestKeyInfo.
|
|
issuer_subject = nullptr;
|
|
return;
|
|
}
|
|
|
|
if (!issuer_subject) {
|
|
*error = KM_ERROR_UNEXPECTED_NULL_POINTER;
|
|
return;
|
|
}
|
|
|
|
*error = check_attest_key_auths(*key);
|
|
if (*error != KM_ERROR_OK) return;
|
|
|
|
signing_key.reset(EVP_PKEY_new());
|
|
if (!signing_key) {
|
|
*error = TranslateLastOpenSslError();
|
|
return;
|
|
}
|
|
|
|
if (!static_cast<const AsymmetricKey&>(*key).InternalToEvp(signing_key.get())) {
|
|
*error = KM_ERROR_UNKNOWN_ERROR;
|
|
}
|
|
}
|
|
|
|
CertificateChain generate_attestation(const AsymmetricKey& key,
|
|
const AuthorizationSet& attest_params,
|
|
AttestKeyInfo attest_key,
|
|
const AttestationContext& context, //
|
|
keymaster_error_t* error) {
|
|
EVP_PKEY_Ptr pkey(EVP_PKEY_new());
|
|
if (!key.InternalToEvp(pkey.get())) {
|
|
*error = TranslateLastOpenSslError();
|
|
return {};
|
|
}
|
|
|
|
return generate_attestation(pkey.get(), key.sw_enforced(), key.hw_enforced(), attest_params,
|
|
move(attest_key), context, error);
|
|
}
|
|
|
|
CertificateChain generate_attestation(const EVP_PKEY* evp_key, //
|
|
const AuthorizationSet& sw_enforced, //
|
|
const AuthorizationSet& tee_enforced,
|
|
const AuthorizationSet& attest_params,
|
|
AttestKeyInfo attest_key,
|
|
const AttestationContext& context, //
|
|
keymaster_error_t* error) {
|
|
if (!error) return {};
|
|
|
|
CertificateCallerParams cert_params{};
|
|
*error = get_certificate_params(attest_params, &cert_params, context.GetKmVersion());
|
|
if (*error != KM_ERROR_OK) return {};
|
|
|
|
AuthProxy proxy(tee_enforced, sw_enforced);
|
|
cert_params.is_signing_key = proxy.Contains(TAG_PURPOSE, KM_PURPOSE_SIGN);
|
|
cert_params.is_encryption_key = proxy.Contains(TAG_PURPOSE, KM_PURPOSE_DECRYPT);
|
|
cert_params.is_agreement_key = proxy.Contains(TAG_PURPOSE, KM_PURPOSE_AGREE_KEY);
|
|
|
|
keymaster_algorithm_t algorithm;
|
|
if (!proxy.GetTagValue(TAG_ALGORITHM, &algorithm)) {
|
|
*error = KM_ERROR_UNSUPPORTED_PURPOSE;
|
|
return {};
|
|
}
|
|
|
|
CertificateChain cert_chain =
|
|
attest_key ? CertificateChain() : context.GetAttestationChain(algorithm, error);
|
|
if (*error != KM_ERROR_OK) return {};
|
|
|
|
X509_NAME_Ptr issuer_subject = get_issuer_subject(attest_key, cert_chain, error);
|
|
if (*error != KM_ERROR_OK) return {};
|
|
|
|
X509_Ptr certificate;
|
|
*error = make_attestation_cert(evp_key, issuer_subject.get(), cert_params, attest_params,
|
|
tee_enforced, sw_enforced, context, &certificate);
|
|
if (*error != KM_ERROR_OK) return {};
|
|
|
|
EVP_PKEY_Ptr signing_key;
|
|
const EVP_PKEY* signing_key_ptr = attest_key.signing_key.get();
|
|
if (!signing_key_ptr) {
|
|
signing_key = get_attestation_key(algorithm, context, error);
|
|
if (*error != KM_ERROR_OK) return {};
|
|
signing_key_ptr = signing_key.get();
|
|
}
|
|
|
|
*error = sign_cert(certificate.get(), signing_key_ptr);
|
|
if (*error != KM_ERROR_OK) return {};
|
|
|
|
return make_cert_chain(certificate.get(), move(cert_chain), error);
|
|
}
|
|
|
|
} // namespace keymaster
|