mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Add attestation certificate generation and identity credential tags.
Bug: 149908474 Test: atest android.security.identity.cts.AttestationTest Test: atest VtsHalIdentityCredentialTargetTest Test: atest android.hardware.identity-support-lib-test Merged-In: I18c5d05d806d4157c9dce42a398cc89421e26907 Change-Id: Ifaffef3606a6398613e33982ff5db81ade1af0b2
This commit is contained in:
committed by
David Zeuthen
parent
c5a652431f
commit
ee37ee9252
@@ -16,6 +16,9 @@
|
||||
|
||||
#define LOG_TAG "WritableIdentityCredential"
|
||||
|
||||
#include "WritableIdentityCredential.h"
|
||||
#include "IdentityCredentialStore.h"
|
||||
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
@@ -23,6 +26,8 @@
|
||||
#include <cppbor/cppbor.h>
|
||||
#include <cppbor/cppbor_parse.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "IdentityCredentialStore.h"
|
||||
#include "Util.h"
|
||||
#include "WritableIdentityCredential.h"
|
||||
@@ -33,26 +38,6 @@ using ::std::optional;
|
||||
using namespace ::android::hardware::identity;
|
||||
|
||||
bool WritableIdentityCredential::initialize() {
|
||||
optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
|
||||
if (!keyPair) {
|
||||
LOG(ERROR) << "Error creating credentialKey";
|
||||
return false;
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
|
||||
if (!pubKey) {
|
||||
LOG(ERROR) << "Error getting public part of credentialKey";
|
||||
return false;
|
||||
}
|
||||
credentialPubKey_ = pubKey.value();
|
||||
|
||||
optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
|
||||
if (!privKey) {
|
||||
LOG(ERROR) << "Error getting private part of credentialKey";
|
||||
return false;
|
||||
}
|
||||
credentialPrivKey_ = privKey.value();
|
||||
|
||||
optional<vector<uint8_t>> random = support::getRandom(16);
|
||||
if (!random) {
|
||||
LOG(ERROR) << "Error creating storageKey";
|
||||
@@ -63,88 +48,54 @@ bool WritableIdentityCredential::initialize() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: use |attestationApplicationId| and |attestationChallenge| and also
|
||||
// ensure the returned certificate chain satisfy the requirements listed in
|
||||
// the docs for IWritableIdentityCredential::getAttestationCertificate()
|
||||
//
|
||||
// This function generates the attestation certificate using the passed in
|
||||
// |attestationApplicationId| and |attestationChallenge|. It will generate an
|
||||
// attestation certificate with current time and expires one year from now. The
|
||||
// certificate shall contain all values as specified in hal.
|
||||
ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
|
||||
const vector<int8_t>& /*attestationApplicationId*/,
|
||||
const vector<int8_t>& /*attestationChallenge*/, vector<Certificate>* outCertificateChain) {
|
||||
// For now, we dynamically generate an attestion key on each and every
|
||||
// request and use that to sign CredentialKey. In a real implementation this
|
||||
// would look very differently.
|
||||
optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
|
||||
if (!attestationKeyPair) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error creating attestationKey"));
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> attestationPubKey =
|
||||
support::ecKeyPairGetPublicKey(attestationKeyPair.value());
|
||||
if (!attestationPubKey) {
|
||||
const vector<int8_t>& attestationApplicationId, //
|
||||
const vector<int8_t>& attestationChallenge, //
|
||||
vector<Certificate>* outCertificateChain) {
|
||||
if (!credentialPrivKey_.empty() || !credentialPubKey_.empty() || !certificateChain_.empty()) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error getting public part of attestationKey"));
|
||||
"Error attestation certificate previously generated"));
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> attestationPrivKey =
|
||||
support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
|
||||
if (!attestationPrivKey) {
|
||||
vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
|
||||
vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
|
||||
|
||||
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
|
||||
support::createEcKeyPairAndAttestation(challenge, appId);
|
||||
if (!keyAttestationPair) {
|
||||
LOG(ERROR) << "Error creating credentialKey and attestation";
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error getting private part of attestationKey"));
|
||||
"Error creating credentialKey and attestation"));
|
||||
}
|
||||
|
||||
string serialDecimal;
|
||||
string issuer;
|
||||
string subject;
|
||||
time_t validityNotBefore = time(nullptr);
|
||||
time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
|
||||
vector<uint8_t> keyPair = keyAttestationPair.value().first;
|
||||
certificateChain_ = keyAttestationPair.value().second;
|
||||
|
||||
// First create a certificate for |credentialPubKey| which is signed by
|
||||
// |attestationPrivKey|.
|
||||
//
|
||||
serialDecimal = "0"; // TODO: set serial to |attestationChallenge|
|
||||
issuer = "Android Open Source Project";
|
||||
subject = "Android IdentityCredential CredentialKey";
|
||||
optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
|
||||
credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
|
||||
validityNotBefore, validityNotAfter);
|
||||
if (!credentialPubKeyCertificate) {
|
||||
optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair);
|
||||
if (!pubKey) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error creating certificate for credentialPubKey"));
|
||||
"Error getting public part of credentialKey"));
|
||||
}
|
||||
credentialPubKey_ = pubKey.value();
|
||||
|
||||
// This is followed by a certificate for |attestationPubKey| self-signed by
|
||||
// |attestationPrivKey|.
|
||||
serialDecimal = "0"; // TODO: set serial
|
||||
issuer = "Android Open Source Project";
|
||||
subject = "Android IdentityCredential AttestationKey";
|
||||
optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
|
||||
attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
|
||||
validityNotBefore, validityNotAfter);
|
||||
if (!attestationKeyCertificate) {
|
||||
optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair);
|
||||
if (!privKey) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error creating certificate for attestationPubKey"));
|
||||
"Error getting private part of credentialKey"));
|
||||
}
|
||||
credentialPrivKey_ = privKey.value();
|
||||
|
||||
// Concatenate the certificates to form the chain.
|
||||
vector<uint8_t> certificateChain;
|
||||
certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
|
||||
credentialPubKeyCertificate.value().end());
|
||||
certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
|
||||
attestationKeyCertificate.value().end());
|
||||
|
||||
optional<vector<vector<uint8_t>>> splitCertChain =
|
||||
support::certificateChainSplit(certificateChain);
|
||||
if (!splitCertChain) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error splitting certificate chain"));
|
||||
}
|
||||
// convert from vector<vector<uint8_t>>> to vector<Certificate>*
|
||||
*outCertificateChain = vector<Certificate>();
|
||||
for (const vector<uint8_t>& cert : splitCertChain.value()) {
|
||||
for (const vector<uint8_t>& cert : certificateChain_) {
|
||||
Certificate c = Certificate();
|
||||
c.encodedCertificate = byteStringToSigned(cert);
|
||||
outCertificateChain->push_back(std::move(c));
|
||||
|
||||
@@ -64,10 +64,13 @@ class WritableIdentityCredential : public BnWritableIdentityCredential {
|
||||
string docType_;
|
||||
bool testCredential_;
|
||||
|
||||
// These are set in initialize().
|
||||
// This is set in initialize().
|
||||
vector<uint8_t> storageKey_;
|
||||
|
||||
// These are set in getAttestationCertificate().
|
||||
vector<uint8_t> credentialPrivKey_;
|
||||
vector<uint8_t> credentialPubKey_;
|
||||
vector<vector<uint8_t>> certificateChain_;
|
||||
|
||||
// These fields are initialized during startPersonalization()
|
||||
size_t numAccessControlProfileRemaining_;
|
||||
|
||||
@@ -23,10 +23,14 @@ cc_library {
|
||||
"include",
|
||||
],
|
||||
shared_libs: [
|
||||
"android.hardware.keymaster@4.0",
|
||||
"libcrypto",
|
||||
"libbase",
|
||||
"libhidlbase",
|
||||
"libhardware",
|
||||
"libkeymaster_portable",
|
||||
"libsoft_attestation_cert",
|
||||
"libpuresoftkeymasterdevice",
|
||||
],
|
||||
static_libs: [
|
||||
"libcppbor",
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
@@ -106,6 +107,17 @@ optional<vector<uint8_t>> encryptAes128Gcm(const vector<uint8_t>& key, const vec
|
||||
// ---------------------------------------------------------------------------
|
||||
// EC crypto functionality / abstraction (only supports P-256).
|
||||
// ---------------------------------------------------------------------------
|
||||
// Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
|
||||
// PKCS#8 encoded key-pair. Also generates an attestation
|
||||
// certificate using the |challenge| and |applicationId|, and returns the generated
|
||||
// certificate in X.509 certificate chain format.
|
||||
//
|
||||
// The attestation time fields used will be the current time, and expires in one year.
|
||||
//
|
||||
// The first parameter of the return value is the keyPair generated, second return in
|
||||
// the pair is the attestation certificate generated.
|
||||
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
|
||||
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId);
|
||||
|
||||
// Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
|
||||
// PKCS#8 encoded key-pair.
|
||||
|
||||
@@ -47,6 +47,13 @@
|
||||
#include <cppbor.h>
|
||||
#include <cppbor_parse.h>
|
||||
|
||||
#include <android/hardware/keymaster/4.0/types.h>
|
||||
#include <keymaster/authorization_set.h>
|
||||
#include <keymaster/contexts/pure_soft_keymaster_context.h>
|
||||
#include <keymaster/contexts/soft_attestation_cert.h>
|
||||
#include <keymaster/keymaster_tags.h>
|
||||
#include <keymaster/km_openssl/attestation_utils.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace identity {
|
||||
@@ -816,6 +823,138 @@ optional<vector<uint8_t>> hmacSha256(const vector<uint8_t>& key, const vector<ui
|
||||
return hmac;
|
||||
}
|
||||
|
||||
// Generates the attestation certificate with the parameters passed in. Note
|
||||
// that the passed in |activeTimeMilliSeconds| |expireTimeMilliSeconds| are in
|
||||
// milli seconds since epoch. We are setting them to milliseconds due to
|
||||
// requirement in AuthorizationSet KM_DATE fields. The certificate created is
|
||||
// actually in seconds.
|
||||
optional<vector<vector<uint8_t>>> createAttestation(const EVP_PKEY* key,
|
||||
const vector<uint8_t>& applicationId,
|
||||
const vector<uint8_t>& challenge,
|
||||
uint64_t activeTimeMilliSeconds,
|
||||
uint64_t expireTimeMilliSeconds) {
|
||||
::keymaster::AuthorizationSet auth_set(
|
||||
::keymaster::AuthorizationSetBuilder()
|
||||
.Authorization(::keymaster::TAG_ATTESTATION_CHALLENGE, challenge.data(),
|
||||
challenge.size())
|
||||
.Authorization(::keymaster::TAG_ACTIVE_DATETIME, activeTimeMilliSeconds)
|
||||
// Even though identity attestation hal said the application
|
||||
// id should be in software enforced authentication set,
|
||||
// keymaster portable lib expect the input in this
|
||||
// parameter because the software enforced in input to keymaster
|
||||
// refers to the key software enforced properties. And this
|
||||
// parameter refers to properties of the attestation which
|
||||
// includes app id.
|
||||
.Authorization(::keymaster::TAG_ATTESTATION_APPLICATION_ID,
|
||||
applicationId.data(), applicationId.size())
|
||||
.Authorization(::keymaster::TAG_USAGE_EXPIRE_DATETIME, expireTimeMilliSeconds));
|
||||
|
||||
// Unique id and device id is not applicable for identity credential attestation,
|
||||
// so we don't need to set those or application id.
|
||||
::keymaster::AuthorizationSet swEnforced(::keymaster::AuthorizationSetBuilder().Authorization(
|
||||
::keymaster::TAG_CREATION_DATETIME, activeTimeMilliSeconds));
|
||||
|
||||
::keymaster::AuthorizationSet hwEnforced(
|
||||
::keymaster::AuthorizationSetBuilder()
|
||||
.Authorization(::keymaster::TAG_PURPOSE, KM_PURPOSE_SIGN)
|
||||
.Authorization(::keymaster::TAG_KEY_SIZE, 256)
|
||||
.Authorization(::keymaster::TAG_ALGORITHM, KM_ALGORITHM_EC)
|
||||
.Authorization(::keymaster::TAG_NO_AUTH_REQUIRED)
|
||||
.Authorization(::keymaster::TAG_DIGEST, KM_DIGEST_SHA_2_256)
|
||||
.Authorization(::keymaster::TAG_EC_CURVE, KM_EC_CURVE_P_256)
|
||||
.Authorization(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY));
|
||||
|
||||
const keymaster_cert_chain_t* attestation_chain =
|
||||
::keymaster::getAttestationChain(KM_ALGORITHM_EC, nullptr);
|
||||
|
||||
if (attestation_chain == nullptr) {
|
||||
LOG(ERROR) << "Error getting attestation chain";
|
||||
return {};
|
||||
}
|
||||
|
||||
const keymaster_key_blob_t* attestation_signing_key =
|
||||
::keymaster::getAttestationKey(KM_ALGORITHM_EC, nullptr);
|
||||
if (attestation_signing_key == nullptr) {
|
||||
LOG(ERROR) << "Error getting attestation key";
|
||||
return {};
|
||||
}
|
||||
|
||||
keymaster_error_t error;
|
||||
::keymaster::CertChainPtr cert_chain_out;
|
||||
::keymaster::PureSoftKeymasterContext context;
|
||||
|
||||
// set identity version to 10 per hal requirements specified in IWriteableCredential.hal
|
||||
// For now, the identity version in the attestation is set in the keymaster
|
||||
// version field in the portable keymaster lib, which is a bit misleading.
|
||||
uint identity_version = 10;
|
||||
error = generate_attestation_from_EVP(key, swEnforced, hwEnforced, auth_set, context,
|
||||
identity_version, *attestation_chain,
|
||||
*attestation_signing_key, &cert_chain_out);
|
||||
|
||||
if (KM_ERROR_OK != error || !cert_chain_out) {
|
||||
LOG(ERROR) << "Error generate attestation from EVP key" << error;
|
||||
return {};
|
||||
}
|
||||
|
||||
// translate certificate format from keymaster_cert_chain_t to vector<uint8_t>.
|
||||
vector<vector<uint8_t>> attestationCertificate;
|
||||
for (int i = 0; i < cert_chain_out->entry_count; i++) {
|
||||
attestationCertificate.insert(
|
||||
attestationCertificate.end(),
|
||||
vector<uint8_t>(
|
||||
cert_chain_out->entries[i].data,
|
||||
cert_chain_out->entries[i].data + cert_chain_out->entries[i].data_length));
|
||||
}
|
||||
|
||||
return attestationCertificate;
|
||||
}
|
||||
|
||||
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
|
||||
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
|
||||
auto ec_key = ::keymaster::EC_KEY_Ptr(EC_KEY_new());
|
||||
auto pkey = ::keymaster::EVP_PKEY_Ptr(EVP_PKEY_new());
|
||||
auto group = ::keymaster::EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
|
||||
|
||||
if (ec_key.get() == nullptr || pkey.get() == nullptr) {
|
||||
LOG(ERROR) << "Memory allocation failed";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 ||
|
||||
EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) {
|
||||
LOG(ERROR) << "Error generating key";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()) != 1) {
|
||||
LOG(ERROR) << "Error getting private key";
|
||||
return {};
|
||||
}
|
||||
|
||||
uint64_t now = time(nullptr);
|
||||
uint64_t secondsInOneYear = 365 * 24 * 60 * 60;
|
||||
uint64_t expireTimeMs = (now + secondsInOneYear) * 1000;
|
||||
|
||||
optional<vector<vector<uint8_t>>> attestationCert =
|
||||
createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs);
|
||||
if (!attestationCert) {
|
||||
LOG(ERROR) << "Error create attestation from key and challenge";
|
||||
return {};
|
||||
}
|
||||
|
||||
int size = i2d_PrivateKey(pkey.get(), nullptr);
|
||||
if (size == 0) {
|
||||
LOG(ERROR) << "Error generating public key encoding";
|
||||
return {};
|
||||
}
|
||||
|
||||
vector<uint8_t> keyPair(size);
|
||||
unsigned char* p = keyPair.data();
|
||||
i2d_PrivateKey(pkey.get(), &p);
|
||||
|
||||
return make_pair(keyPair, attestationCert.value());
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> createEcKeyPair() {
|
||||
auto ec_key = EC_KEY_Ptr(EC_KEY_new());
|
||||
auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
|
||||
|
||||
@@ -101,6 +101,7 @@ using V4_0::TAG_VENDOR_PATCHLEVEL;
|
||||
DECLARE_KM_4_1_TYPED_TAG(EARLY_BOOT_ONLY);
|
||||
DECLARE_KM_4_1_TYPED_TAG(DEVICE_UNIQUE_ATTESTATION);
|
||||
DECLARE_KM_4_1_TYPED_TAG(STORAGE_KEY);
|
||||
DECLARE_KM_4_1_TYPED_TAG(IDENTITY_CREDENTIAL_KEY);
|
||||
|
||||
} // namespace android::hardware::keymaster::V4_1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user