mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:50:18 +00:00
Merge changes from topic "add rkp to identity-default"
* changes: Add remote key provisioning to the IC HAL Refactor IC support for RKP Log to logd in the default identity service Fix formatting of identity credential aidl
This commit is contained in:
@@ -15,6 +15,7 @@ aidl_interface {
|
||||
],
|
||||
imports: [
|
||||
"android.hardware.keymaster",
|
||||
"android.hardware.security.keymint",
|
||||
],
|
||||
stability: "vintf",
|
||||
backend: {
|
||||
@@ -25,6 +26,7 @@ aidl_interface {
|
||||
vndk: {
|
||||
enabled: true,
|
||||
},
|
||||
apps_enabled: false,
|
||||
},
|
||||
},
|
||||
versions: [
|
||||
|
||||
@@ -39,4 +39,5 @@ parcelable HardwareInformation {
|
||||
int dataChunkSize;
|
||||
boolean isDirectAccess;
|
||||
@utf8InCpp String[] supportedDocTypes;
|
||||
boolean isRemoteKeyProvisioningSupported = false;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ interface IIdentityCredentialStore {
|
||||
android.hardware.identity.IWritableIdentityCredential createCredential(in @utf8InCpp String docType, in boolean testCredential);
|
||||
android.hardware.identity.IIdentityCredential getCredential(in android.hardware.identity.CipherSuite cipherSuite, in byte[] credentialData);
|
||||
android.hardware.identity.IPresentationSession createPresentationSession(in android.hardware.identity.CipherSuite cipherSuite);
|
||||
android.hardware.security.keymint.IRemotelyProvisionedComponent getRemotelyProvisionedComponent();
|
||||
const int STATUS_OK = 0;
|
||||
const int STATUS_FAILED = 1;
|
||||
const int STATUS_CIPHER_SUITE_NOT_SUPPORTED = 2;
|
||||
|
||||
@@ -41,4 +41,5 @@ interface IWritableIdentityCredential {
|
||||
byte[] addEntryValue(in byte[] content);
|
||||
@SuppressWarnings(value={"out-array"}) void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature);
|
||||
void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
|
||||
void setRemotelyProvisionedAttestationKey(in byte[] attestationKeyBlob, in byte[] attestationCertificate);
|
||||
}
|
||||
|
||||
@@ -51,4 +51,19 @@ parcelable HardwareInformation {
|
||||
*
|
||||
*/
|
||||
@utf8InCpp String[] supportedDocTypes;
|
||||
|
||||
/**
|
||||
* isRemoteKeyProvisioningSupported indicates whether or not the underlying implementation
|
||||
* supports a remotely provisioned key for attestation or not. If this field is false, then
|
||||
* the implementation only uses a factory-installed, fixed attestation key. If this field is
|
||||
* true, then an IRemotelyProvisionedComponent is associated with the IIdentityCredentialStore,
|
||||
* and a remotely provisioned key blob may be provided for credential key attestation.
|
||||
*
|
||||
* Note that remote provisioning is not required, even when it is supported. Implementations
|
||||
* MUST use a factory-installed attestation key as a fallback for when there are no
|
||||
* remotely provisioned keys available. This behavior mirrors keystore key attestation.
|
||||
*
|
||||
* This field was added in API version 4.
|
||||
*/
|
||||
boolean isRemoteKeyProvisioningSupported = false;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.hardware.identity.HardwareInformation;
|
||||
import android.hardware.identity.IIdentityCredential;
|
||||
import android.hardware.identity.IPresentationSession;
|
||||
import android.hardware.identity.IWritableIdentityCredential;
|
||||
import android.hardware.security.keymint.IRemotelyProvisionedComponent;
|
||||
|
||||
/**
|
||||
* IIdentityCredentialStore provides an interface to a secure store for user identity documents.
|
||||
@@ -215,16 +216,16 @@ interface IIdentityCredentialStore {
|
||||
* @return an IWritableIdentityCredential interface that provides operations to
|
||||
* provision a credential.
|
||||
*/
|
||||
IWritableIdentityCredential createCredential(in @utf8InCpp String docType,
|
||||
in boolean testCredential);
|
||||
IWritableIdentityCredential createCredential(
|
||||
in @utf8InCpp String docType, in boolean testCredential);
|
||||
|
||||
/**
|
||||
* getCredential retrieves an IIdentityCredential interface which allows use of a stored
|
||||
* Credential.
|
||||
*
|
||||
* The cipher suite used to communicate with the remote verifier must also be specified. Currently
|
||||
* only a single cipher-suite is supported. Support for other cipher suites may be added in a
|
||||
* future version of this HAL.
|
||||
* The cipher suite used to communicate with the remote verifier must also be specified.
|
||||
* Currently only a single cipher-suite is supported. Support for other cipher suites may be
|
||||
* added in a future version of this HAL.
|
||||
*
|
||||
* This method fails with STATUS_INVALID_DATA if the passed in credentialData cannot be
|
||||
* decoded or decrypted.
|
||||
@@ -263,4 +264,23 @@ interface IIdentityCredentialStore {
|
||||
* @return an IPresentationSession interface.
|
||||
*/
|
||||
IPresentationSession createPresentationSession(in CipherSuite cipherSuite);
|
||||
|
||||
/**
|
||||
* Fetch the IRemotelyProvisionedComponent that is used to generate attestation keys for
|
||||
* remote provisionining. Keys generated by this component are to be certified by a remote
|
||||
* provisionined authority, then used to attest to credential keys via
|
||||
* IWritableIdentityCredential.setRemotelyProvisionedAttestationKey.
|
||||
*
|
||||
* Support for this method is indicated by HardwareInformation. If the
|
||||
* |isRemoteKeyProvisioningSupported| field is false, this method will fail with
|
||||
* EX_UNSUPPORTED_OPERATION.
|
||||
*
|
||||
* This method was added in API version 4.
|
||||
*
|
||||
* @see
|
||||
* android.hardware.identity.IWritableIdentityCredential#setRemotelyProvisionedAttestationKey
|
||||
*
|
||||
* @return an IRemotelyProvisionedComponent that is used to generate attestation keys.
|
||||
*/
|
||||
IRemotelyProvisionedComponent getRemotelyProvisionedComponent();
|
||||
}
|
||||
|
||||
@@ -131,7 +131,8 @@ interface IWritableIdentityCredential {
|
||||
*
|
||||
* @return the X.509 certificate chain for the credentialKey
|
||||
*/
|
||||
Certificate[] getAttestationCertificate(in byte[] attestationApplicationId, in byte[] attestationChallenge);
|
||||
Certificate[] getAttestationCertificate(
|
||||
in byte[] attestationApplicationId, in byte[] attestationChallenge);
|
||||
|
||||
/**
|
||||
* Start the personalization process.
|
||||
@@ -183,11 +184,11 @@ interface IWritableIdentityCredential {
|
||||
* in the secure environment. If this requirement is not met the call fails with
|
||||
* STATUS_INVALID_DATA.
|
||||
*
|
||||
* @return a structure with the passed-in data and MAC created with storageKey for authenticating
|
||||
* the data at a later point in time.
|
||||
* @return a structure with the passed-in data and MAC created with storageKey for
|
||||
* authenticating the data at a later point in time.
|
||||
*/
|
||||
SecureAccessControlProfile addAccessControlProfile(in int id, in Certificate readerCertificate,
|
||||
in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId);
|
||||
in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId);
|
||||
|
||||
/**
|
||||
* Begins the process of adding an entry to the credential. All access control profiles must be
|
||||
@@ -209,7 +210,7 @@ interface IWritableIdentityCredential {
|
||||
* is not met this method fails with STATUS_INVALID_DATA.
|
||||
*/
|
||||
void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace,
|
||||
in @utf8InCpp String name, in int entrySize);
|
||||
in @utf8InCpp String name, in int entrySize);
|
||||
|
||||
/**
|
||||
* Continues the process of adding an entry, providing a value or part of a value.
|
||||
@@ -221,8 +222,8 @@ interface IWritableIdentityCredential {
|
||||
* chunk sizes must equal the value of the beginAddEntry() entrySize argument. If this
|
||||
* requirement is not met the call fails with STATUS_INVALID_DATA.
|
||||
*
|
||||
* @param content is the entry value, encoded as CBOR. In the case the content exceeds gcmChunkSize,
|
||||
* this may be partial content up to gcmChunkSize bytes long.
|
||||
* @param content is the entry value, encoded as CBOR. In the case the content exceeds
|
||||
* gcmChunkSize, this may be partial content up to gcmChunkSize bytes long.
|
||||
*
|
||||
* @return the encrypted and MACed content. For directly-available credentials the contents are
|
||||
* implementation-defined. For other credentials, the result contains
|
||||
@@ -321,8 +322,7 @@ interface IWritableIdentityCredential {
|
||||
* }
|
||||
*/
|
||||
@SuppressWarnings(value={"out-array"})
|
||||
void finishAddingEntries(out byte[] credentialData,
|
||||
out byte[] proofOfProvisioningSignature);
|
||||
void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature);
|
||||
|
||||
/**
|
||||
* Sets the expected size of the ProofOfProvisioning returned by finishAddingEntries(). This
|
||||
@@ -336,4 +336,35 @@ interface IWritableIdentityCredential {
|
||||
*/
|
||||
void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
|
||||
|
||||
/**
|
||||
* Sets the attestation key used to sign the credentialKey certificate. This method is used to
|
||||
* support remotely provisioned attestation keys, removing the credential's dependency on any
|
||||
* factory-provisioned attestation key.
|
||||
*
|
||||
* This method must be called before getAttestationCertificate. After this method is called,
|
||||
* the certificate chain returned by getAttestationCertificate will contain a leaf certificate
|
||||
* signed by attestationKeyBlob and the chain in attestationCertificate will make up the rest
|
||||
* of the returned chain.
|
||||
*
|
||||
* Returns EX_UNSUPPORTED_FUNCTION if remote provisioning is not supported
|
||||
* (see IIdentityCredentialStore.getHardwareInformation()).
|
||||
*
|
||||
* This method was added in API version 4.
|
||||
*
|
||||
* @param attestationKeyBlob is a key blob generated by the IRemotelyProvisionedComponent that
|
||||
* is returned by ICredentialStore.getRemotelyProvisionedComponent. The format is vendor-
|
||||
* specified, and matches the key blob returned by IKeyMintDevice.generateKey.
|
||||
*
|
||||
* @param attestationCertificate contains the X.509 certificate chain that certifies the
|
||||
* attestationKeyBlob. This certificate is expected to have been remotely provisioned
|
||||
* by a trusted authority. This parameter must contain a concatenated chain of DER-encoded
|
||||
* X.509 certificates. The certificates must be ordered such that the attestation key
|
||||
* certificate is first (starting at byte 0). The issuer certificate for the attestation
|
||||
* certificate immediately follows, continuing this chain to the final, root certificate.
|
||||
*
|
||||
* @see getAttestationCertificate
|
||||
* @see android.hardware.identity.ICredentialStore#getRemotelyProvisionedComponent
|
||||
*/
|
||||
void setRemotelyProvisionedAttestationKey(
|
||||
in byte[] attestationKeyBlob, in byte[] attestationCertificate);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ cc_library_static {
|
||||
"android.hardware.identity-support-lib",
|
||||
"android.hardware.identity-V4-ndk",
|
||||
"android.hardware.keymaster-V4-ndk",
|
||||
"android.hardware.security.keymint-V2-ndk",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -81,6 +82,9 @@ cc_binary {
|
||||
init_rc: ["identity-default.rc"],
|
||||
vintf_fragments: ["identity-default.xml"],
|
||||
vendor: true,
|
||||
defaults: [
|
||||
"keymint_use_latest_hal_aidl_ndk_static",
|
||||
],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
|
||||
@@ -267,25 +267,42 @@ bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
|
||||
|
||||
bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const uint8_t* challenge,
|
||||
size_t challengeSize, const uint8_t* applicationId,
|
||||
size_t applicationIdSize, bool testCredential, uint8_t* cert,
|
||||
size_t* certSize) {
|
||||
vector<uint8_t> challengeVec(challengeSize);
|
||||
memcpy(challengeVec.data(), challenge, challengeSize);
|
||||
|
||||
vector<uint8_t> applicationIdVec(applicationIdSize);
|
||||
memcpy(applicationIdVec.data(), applicationId, applicationIdSize);
|
||||
|
||||
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> ret =
|
||||
android::hardware::identity::support::createEcKeyPairAndAttestation(
|
||||
challengeVec, applicationIdVec, testCredential);
|
||||
if (!ret) {
|
||||
eicDebug("Error generating CredentialKey and attestation");
|
||||
return false;
|
||||
size_t applicationIdSize, bool testCredential,
|
||||
const uint8_t* attestationKeyBlob, size_t attestationKeyBlobSize,
|
||||
const uint8_t* attestationKeyCert, size_t attestationKeyCertSize,
|
||||
uint8_t* cert, size_t* certSize) {
|
||||
vector<uint8_t> flatChain;
|
||||
vector<uint8_t> keyPair;
|
||||
vector<uint8_t> challengeVec(challenge, challenge + challengeSize);
|
||||
vector<uint8_t> applicationIdVec(applicationId, applicationId + applicationIdSize);
|
||||
if (attestationKeyBlob && attestationKeyBlobSize > 0 && attestationKeyCert &&
|
||||
attestationKeyCertSize > 0) {
|
||||
vector<uint8_t> attestationKeyBlobVec(attestationKeyBlob,
|
||||
attestationKeyBlob + attestationKeyBlobSize);
|
||||
vector<uint8_t> attestationKeyCertVec(attestationKeyCert,
|
||||
attestationKeyCert + attestationKeyCertSize);
|
||||
optional<std::pair<vector<uint8_t>, vector<uint8_t>>> keyAndCert =
|
||||
android::hardware::identity::support::createEcKeyPairWithAttestationKey(
|
||||
challengeVec, applicationIdVec, attestationKeyBlobVec,
|
||||
attestationKeyCertVec, testCredential);
|
||||
if (!keyAndCert) {
|
||||
eicDebug("Error generating CredentialKey and attestation");
|
||||
return false;
|
||||
}
|
||||
keyPair = std::move(keyAndCert->first);
|
||||
flatChain = std::move(keyAndCert->second);
|
||||
} else {
|
||||
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> ret =
|
||||
android::hardware::identity::support::createEcKeyPairAndAttestation(
|
||||
challengeVec, applicationIdVec, testCredential);
|
||||
if (!ret) {
|
||||
eicDebug("Error generating CredentialKey and attestation");
|
||||
return false;
|
||||
}
|
||||
keyPair = std::move(ret->first);
|
||||
flatChain = android::hardware::identity::support::certificateChainJoin(ret->second);
|
||||
}
|
||||
|
||||
// Extract certificate chain.
|
||||
vector<uint8_t> flatChain =
|
||||
android::hardware::identity::support::certificateChainJoin(ret.value().second);
|
||||
if (*certSize < flatChain.size()) {
|
||||
eicDebug("Buffer for certificate is only %zd bytes long, need %zd bytes", *certSize,
|
||||
flatChain.size());
|
||||
@@ -296,7 +313,7 @@ bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const
|
||||
|
||||
// Extract private key.
|
||||
optional<vector<uint8_t>> privKey =
|
||||
android::hardware::identity::support::ecKeyPairGetPrivateKey(ret.value().first);
|
||||
android::hardware::identity::support::ecKeyPairGetPrivateKey(keyPair);
|
||||
if (!privKey) {
|
||||
eicDebug("Error extracting private key");
|
||||
return false;
|
||||
@@ -520,10 +537,12 @@ bool eicOpsHkdf(const uint8_t* sharedSecret, size_t sharedSecretSize, const uint
|
||||
#ifdef EIC_DEBUG
|
||||
|
||||
void eicPrint(const char* format, ...) {
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
vsnprintf(buf, sizeof(buf), format, args);
|
||||
va_end(args);
|
||||
LOG(INFO) << buf;
|
||||
}
|
||||
|
||||
void eicHexdump(const char* message, const uint8_t* data, size_t dataSize) {
|
||||
|
||||
@@ -155,7 +155,11 @@ optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialK
|
||||
size_t publicKeyCertSize = sizeof publicKeyCert;
|
||||
if (!eicProvisioningCreateCredentialKey(&ctx_, challenge.data(), challenge.size(),
|
||||
applicationId.data(), applicationId.size(),
|
||||
publicKeyCert, &publicKeyCertSize)) {
|
||||
/*attestationKeyBlob=*/nullptr,
|
||||
/*attestationKeyBlobSize=*/0,
|
||||
/*attestationKeyCert=*/nullptr,
|
||||
/*attestationKeyCertSize=*/0, publicKeyCert,
|
||||
&publicKeyCertSize)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
vector<uint8_t> pubKeyCert(publicKeyCertSize);
|
||||
@@ -163,6 +167,23 @@ optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialK
|
||||
return pubKeyCert;
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialKeyUsingRkp(
|
||||
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
|
||||
const vector<uint8_t>& attestationKeyBlob, const vector<uint8_t>& attstationKeyCert) {
|
||||
size_t publicKeyCertSize = 4096;
|
||||
vector<uint8_t> publicKeyCert(publicKeyCertSize);
|
||||
if (!eicProvisioningCreateCredentialKey(&ctx_, challenge.data(), challenge.size(),
|
||||
applicationId.data(), applicationId.size(),
|
||||
attestationKeyBlob.data(), attestationKeyBlob.size(),
|
||||
attstationKeyCert.data(), attstationKeyCert.size(),
|
||||
publicKeyCert.data(), &publicKeyCertSize)) {
|
||||
LOG(ERROR) << "error creating credential key";
|
||||
return std::nullopt;
|
||||
}
|
||||
publicKeyCert.resize(publicKeyCertSize);
|
||||
return publicKeyCert;
|
||||
}
|
||||
|
||||
bool FakeSecureHardwareProvisioningProxy::startPersonalization(
|
||||
int accessControlProfileCount, const vector<int>& entryCounts, const string& docType,
|
||||
size_t expectedProofOfProvisioningSize) {
|
||||
|
||||
@@ -43,6 +43,11 @@ class FakeSecureHardwareProvisioningProxy : public SecureHardwareProvisioningPro
|
||||
optional<vector<uint8_t>> createCredentialKey(const vector<uint8_t>& challenge,
|
||||
const vector<uint8_t>& applicationId) override;
|
||||
|
||||
optional<vector<uint8_t>> createCredentialKeyUsingRkp(
|
||||
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
|
||||
const vector<uint8_t>& attestationKeyBlob,
|
||||
const vector<uint8_t>& attestationKeyCert) override;
|
||||
|
||||
bool startPersonalization(int accessControlProfileCount, const vector<int>& entryCounts,
|
||||
const string& docType,
|
||||
size_t expectedProofOfProvisioningSize) override;
|
||||
|
||||
@@ -1012,8 +1012,8 @@ ndk::ScopedAStatus IdentityCredential::updateCredential(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Error creating provisioning proxy"));
|
||||
}
|
||||
shared_ptr<WritableIdentityCredential> wc =
|
||||
ndk::SharedRefBase::make<WritableIdentityCredential>(provisioningHwProxy, docType_,
|
||||
testCredential_);
|
||||
ndk::SharedRefBase::make<WritableIdentityCredential>(
|
||||
provisioningHwProxy, docType_, testCredential_, hardwareInformation_);
|
||||
if (!wc->initializeForUpdate(encryptedCredentialKeys_)) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
|
||||
@@ -48,11 +48,13 @@ class IdentityCredential : public BnIdentityCredential {
|
||||
public:
|
||||
IdentityCredential(sp<SecureHardwareProxyFactory> hwProxyFactory,
|
||||
const vector<uint8_t>& credentialData,
|
||||
std::shared_ptr<PresentationSession> session)
|
||||
std::shared_ptr<PresentationSession> session,
|
||||
HardwareInformation hardwareInformation)
|
||||
: hwProxyFactory_(hwProxyFactory),
|
||||
credentialData_(credentialData),
|
||||
session_(std::move(session)),
|
||||
numStartRetrievalCalls_(0),
|
||||
hardwareInformation_(std::move(hardwareInformation)),
|
||||
expectedDeviceNameSpacesSize_(0) {}
|
||||
|
||||
// Parses and decrypts credentialData_, return a status code from
|
||||
@@ -103,6 +105,7 @@ class IdentityCredential : public BnIdentityCredential {
|
||||
vector<uint8_t> credentialData_;
|
||||
shared_ptr<PresentationSession> session_;
|
||||
int numStartRetrievalCalls_;
|
||||
HardwareInformation hardwareInformation_;
|
||||
|
||||
// Set by initialize()
|
||||
string docType_;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#define LOG_TAG "IdentityCredentialStore"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/binder_manager.h>
|
||||
|
||||
#include "IdentityCredential.h"
|
||||
#include "IdentityCredentialStore.h"
|
||||
@@ -25,15 +26,24 @@
|
||||
|
||||
namespace aidl::android::hardware::identity {
|
||||
|
||||
using ::aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent;
|
||||
|
||||
IdentityCredentialStore::IdentityCredentialStore(sp<SecureHardwareProxyFactory> hwProxyFactory,
|
||||
optional<string> remotelyProvisionedComponent)
|
||||
: hwProxyFactory_(hwProxyFactory),
|
||||
remotelyProvisionedComponentName_(remotelyProvisionedComponent) {
|
||||
hardwareInformation_.credentialStoreName = "Identity Credential Reference Implementation";
|
||||
hardwareInformation_.credentialStoreAuthorName = "Google";
|
||||
hardwareInformation_.dataChunkSize = kGcmChunkSize;
|
||||
hardwareInformation_.isDirectAccess = false;
|
||||
hardwareInformation_.supportedDocTypes = {};
|
||||
hardwareInformation_.isRemoteKeyProvisioningSupported =
|
||||
remotelyProvisionedComponentName_.has_value();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredentialStore::getHardwareInformation(
|
||||
HardwareInformation* hardwareInformation) {
|
||||
HardwareInformation hw;
|
||||
hw.credentialStoreName = "Identity Credential Reference Implementation";
|
||||
hw.credentialStoreAuthorName = "Google";
|
||||
hw.dataChunkSize = kGcmChunkSize;
|
||||
hw.isDirectAccess = false;
|
||||
hw.supportedDocTypes = {};
|
||||
*hardwareInformation = hw;
|
||||
*hardwareInformation = hardwareInformation_;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
@@ -42,7 +52,8 @@ ndk::ScopedAStatus IdentityCredentialStore::createCredential(
|
||||
shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
|
||||
sp<SecureHardwareProvisioningProxy> hwProxy = hwProxyFactory_->createProvisioningProxy();
|
||||
shared_ptr<WritableIdentityCredential> wc =
|
||||
ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType, testCredential);
|
||||
ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType, testCredential,
|
||||
hardwareInformation_);
|
||||
if (!wc->initialize()) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
@@ -63,7 +74,7 @@ ndk::ScopedAStatus IdentityCredentialStore::getCredential(
|
||||
}
|
||||
|
||||
shared_ptr<IdentityCredential> credential = ndk::SharedRefBase::make<IdentityCredential>(
|
||||
hwProxyFactory_, credentialData, nullptr /* session */);
|
||||
hwProxyFactory_, credentialData, nullptr /* session */, hardwareInformation_);
|
||||
auto ret = credential->initialize();
|
||||
if (ret != IIdentityCredentialStore::STATUS_OK) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
@@ -83,8 +94,8 @@ ndk::ScopedAStatus IdentityCredentialStore::createPresentationSession(
|
||||
}
|
||||
|
||||
sp<SecureHardwareSessionProxy> hwProxy = hwProxyFactory_->createSessionProxy();
|
||||
shared_ptr<PresentationSession> session =
|
||||
ndk::SharedRefBase::make<PresentationSession>(hwProxyFactory_, hwProxy);
|
||||
shared_ptr<PresentationSession> session = ndk::SharedRefBase::make<PresentationSession>(
|
||||
hwProxyFactory_, hwProxy, hardwareInformation_);
|
||||
auto ret = session->initialize();
|
||||
if (ret != IIdentityCredentialStore::STATUS_OK) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
@@ -94,4 +105,23 @@ ndk::ScopedAStatus IdentityCredentialStore::createPresentationSession(
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus IdentityCredentialStore::getRemotelyProvisionedComponent(
|
||||
shared_ptr<IRemotelyProvisionedComponent>* outRemotelyProvisionedComponent) {
|
||||
if (!remotelyProvisionedComponentName_) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCodeWithMessage(
|
||||
EX_UNSUPPORTED_OPERATION, "Remote key provisioning is not supported"));
|
||||
}
|
||||
|
||||
ndk::SpAIBinder binder(
|
||||
AServiceManager_waitForService(remotelyProvisionedComponentName_->c_str()));
|
||||
if (binder.get() == nullptr) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Unable to get remotely provisioned component"));
|
||||
}
|
||||
|
||||
*outRemotelyProvisionedComponent = IRemotelyProvisionedComponent::fromBinder(binder);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
|
||||
|
||||
#include <aidl/android/hardware/identity/BnIdentityCredentialStore.h>
|
||||
#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
|
||||
|
||||
#include "SecureHardwareProxy.h"
|
||||
|
||||
@@ -25,14 +26,18 @@ namespace aidl::android::hardware::identity {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::hardware::identity::SecureHardwareProxyFactory;
|
||||
using ::std::optional;
|
||||
using ::std::shared_ptr;
|
||||
using ::std::string;
|
||||
using ::std::vector;
|
||||
|
||||
class IdentityCredentialStore : public BnIdentityCredentialStore {
|
||||
public:
|
||||
IdentityCredentialStore(sp<SecureHardwareProxyFactory> hwProxyFactory)
|
||||
: hwProxyFactory_(hwProxyFactory) {}
|
||||
// If remote key provisioning is supported, pass the service name for the correct
|
||||
// IRemotelyProvisionedComponent to the remotelyProvisionedComponent parameter. Else
|
||||
// pass std::nullopt to indicate remote key provisioning is not supported.
|
||||
IdentityCredentialStore(sp<SecureHardwareProxyFactory> hwProxyFactory,
|
||||
optional<string> remotelyProvisionedComponent);
|
||||
|
||||
// The GCM chunk size used by this implementation is 64 KiB.
|
||||
static constexpr size_t kGcmChunkSize = 64 * 1024;
|
||||
@@ -50,8 +55,14 @@ class IdentityCredentialStore : public BnIdentityCredentialStore {
|
||||
ndk::ScopedAStatus createPresentationSession(
|
||||
CipherSuite cipherSuite, shared_ptr<IPresentationSession>* outSession) override;
|
||||
|
||||
ndk::ScopedAStatus getRemotelyProvisionedComponent(
|
||||
shared_ptr<::aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent>*
|
||||
outRemotelyProvisionedComponent) override;
|
||||
|
||||
private:
|
||||
sp<SecureHardwareProxyFactory> hwProxyFactory_;
|
||||
optional<string> remotelyProvisionedComponentName_;
|
||||
HardwareInformation hardwareInformation_;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
|
||||
@@ -122,8 +122,8 @@ ndk::ScopedAStatus PresentationSession::setSessionTranscript(
|
||||
ndk::ScopedAStatus PresentationSession::getCredential(
|
||||
const vector<uint8_t>& credentialData, shared_ptr<IIdentityCredential>* outCredential) {
|
||||
shared_ptr<PresentationSession> p = ref<PresentationSession>();
|
||||
shared_ptr<IdentityCredential> credential =
|
||||
ndk::SharedRefBase::make<IdentityCredential>(hwProxyFactory_, credentialData, p);
|
||||
shared_ptr<IdentityCredential> credential = ndk::SharedRefBase::make<IdentityCredential>(
|
||||
hwProxyFactory_, credentialData, p, hardwareInformation_);
|
||||
int ret = credential->initialize();
|
||||
if (ret != IIdentityCredentialStore::STATUS_OK) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
|
||||
@@ -38,8 +38,11 @@ using ::std::vector;
|
||||
class PresentationSession : public BnPresentationSession {
|
||||
public:
|
||||
PresentationSession(sp<SecureHardwareProxyFactory> hwProxyFactory,
|
||||
sp<SecureHardwareSessionProxy> hwProxy)
|
||||
: hwProxyFactory_(std::move(hwProxyFactory)), hwProxy_(std::move(hwProxy)) {}
|
||||
sp<SecureHardwareSessionProxy> hwProxy,
|
||||
HardwareInformation hardwareInformation)
|
||||
: hwProxyFactory_(std::move(hwProxyFactory)),
|
||||
hwProxy_(std::move(hwProxy)),
|
||||
hardwareInformation_(std::move(hardwareInformation)) {}
|
||||
|
||||
virtual ~PresentationSession();
|
||||
|
||||
@@ -65,6 +68,7 @@ class PresentationSession : public BnPresentationSession {
|
||||
// Set by constructor
|
||||
sp<SecureHardwareProxyFactory> hwProxyFactory_;
|
||||
sp<SecureHardwareSessionProxy> hwProxy_;
|
||||
HardwareInformation hardwareInformation_;
|
||||
|
||||
// Set by initialize()
|
||||
uint64_t id_;
|
||||
|
||||
@@ -82,6 +82,18 @@ class SecureHardwareProvisioningProxy : public RefBase {
|
||||
virtual optional<vector<uint8_t>> createCredentialKey(const vector<uint8_t>& challenge,
|
||||
const vector<uint8_t>& applicationId) = 0;
|
||||
|
||||
// Returns public key certificate with a remotely provisioned attestation key.
|
||||
//
|
||||
// This returns a single certificate that is signed by the given |attestationKeyBlob|.
|
||||
// The implementation of eicOpsCreateCredentialKey() on the TA side must coordinate
|
||||
// with its corresponding keymint implementation to sign using the attestation key. The
|
||||
// |attestationKeyCert| parameter is the certificates for |attestationKeyBlob|,
|
||||
// formatted as concatenated, DER-encoded, X.509 certificates.
|
||||
virtual optional<vector<uint8_t>> createCredentialKeyUsingRkp(
|
||||
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
|
||||
const vector<uint8_t>& attestationKeyBlob,
|
||||
const vector<uint8_t>& attestationKeyCert) = 0;
|
||||
|
||||
virtual bool startPersonalization(int accessControlProfileCount, const vector<int>& entryCounts,
|
||||
const string& docType,
|
||||
size_t expectedProofOfProvisioningSize) = 0;
|
||||
|
||||
@@ -79,8 +79,15 @@ ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
|
||||
IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty"));
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> certChain =
|
||||
hwProxy_->createCredentialKey(attestationChallenge, attestationApplicationId);
|
||||
optional<vector<uint8_t>> certChain;
|
||||
if (attestationKeyBlob_ && attestationCertificateChain_) {
|
||||
certChain = hwProxy_->createCredentialKeyUsingRkp(
|
||||
attestationChallenge, attestationApplicationId, *attestationKeyBlob_,
|
||||
attestationCertificateChain_->at(0));
|
||||
} else {
|
||||
certChain = hwProxy_->createCredentialKey(attestationChallenge, attestationApplicationId);
|
||||
}
|
||||
|
||||
if (!certChain) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
@@ -95,8 +102,14 @@ ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
|
||||
}
|
||||
|
||||
*outCertificateChain = vector<Certificate>();
|
||||
for (const vector<uint8_t>& cert : certs.value()) {
|
||||
Certificate c = Certificate();
|
||||
for (vector<uint8_t>& cert : certs.value()) {
|
||||
Certificate c;
|
||||
c.encodedCertificate = std::move(cert);
|
||||
outCertificateChain->push_back(std::move(c));
|
||||
}
|
||||
|
||||
for (const vector<uint8_t>& cert : *attestationCertificateChain_) {
|
||||
Certificate c;
|
||||
c.encodedCertificate = cert;
|
||||
outCertificateChain->push_back(std::move(c));
|
||||
}
|
||||
@@ -402,4 +415,36 @@ ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus WritableIdentityCredential::setRemotelyProvisionedAttestationKey(
|
||||
const vector<uint8_t>& attestationKeyBlob,
|
||||
const vector<uint8_t>& attestationCertificateChain) {
|
||||
if (!hardwareInformation_.isRemoteKeyProvisioningSupported) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCodeWithMessage(
|
||||
EX_UNSUPPORTED_OPERATION, "Remote key provisioning is not supported"));
|
||||
}
|
||||
|
||||
if (attestationKeyBlob.empty() || attestationCertificateChain.empty()) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Empty data passed to setRemotlyProvisionedAttestationKey"));
|
||||
}
|
||||
|
||||
if (attestationKeyBlob_.has_value()) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED, "Attestation key already set"));
|
||||
}
|
||||
|
||||
optional<vector<vector<uint8_t>>> certs =
|
||||
support::certificateChainSplit(attestationCertificateChain);
|
||||
if (!certs) {
|
||||
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
|
||||
IIdentityCredentialStore::STATUS_FAILED,
|
||||
"Error splitting chain into separate certificates"));
|
||||
}
|
||||
|
||||
attestationKeyBlob_ = attestationKeyBlob;
|
||||
attestationCertificateChain_ = *certs;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace aidl::android::hardware::identity {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::hardware::identity::SecureHardwareProvisioningProxy;
|
||||
using ::std::optional;
|
||||
using ::std::set;
|
||||
using ::std::string;
|
||||
using ::std::vector;
|
||||
@@ -41,8 +42,11 @@ class WritableIdentityCredential : public BnWritableIdentityCredential {
|
||||
// For an updated credential, call initializeForUpdate() right after construction.
|
||||
//
|
||||
WritableIdentityCredential(sp<SecureHardwareProvisioningProxy> hwProxy, const string& docType,
|
||||
bool testCredential)
|
||||
: hwProxy_(hwProxy), docType_(docType), testCredential_(testCredential) {}
|
||||
bool testCredential, HardwareInformation hardwareInformation)
|
||||
: hwProxy_(hwProxy),
|
||||
docType_(docType),
|
||||
testCredential_(testCredential),
|
||||
hardwareInformation_(std::move(hardwareInformation)) {}
|
||||
|
||||
~WritableIdentityCredential();
|
||||
|
||||
@@ -78,11 +82,16 @@ class WritableIdentityCredential : public BnWritableIdentityCredential {
|
||||
vector<uint8_t>* outCredentialData,
|
||||
vector<uint8_t>* outProofOfProvisioningSignature) override;
|
||||
|
||||
ndk::ScopedAStatus setRemotelyProvisionedAttestationKey(
|
||||
const vector<uint8_t>& attestationKeyBlob,
|
||||
const vector<uint8_t>& attestationCertificateChain) override;
|
||||
|
||||
private:
|
||||
// Set by constructor.
|
||||
sp<SecureHardwareProvisioningProxy> hwProxy_;
|
||||
string docType_;
|
||||
bool testCredential_;
|
||||
HardwareInformation hardwareInformation_;
|
||||
|
||||
// This is set in initialize().
|
||||
bool startPersonalizationCalled_;
|
||||
@@ -109,6 +118,10 @@ class WritableIdentityCredential : public BnWritableIdentityCredential {
|
||||
vector<int32_t> entryAccessControlProfileIds_;
|
||||
vector<uint8_t> entryBytes_;
|
||||
set<string> allNameSpaces_;
|
||||
|
||||
// Remotely provisioned attestation data, set via setRemotelyProvisionedAttestationKey
|
||||
optional<vector<uint8_t>> attestationKeyBlob_;
|
||||
optional<vector<vector<uint8_t>>> attestationCertificateChain_;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::identity
|
||||
|
||||
@@ -196,13 +196,19 @@ bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
|
||||
|
||||
// Generates CredentialKey plus an attestation certificate.
|
||||
//
|
||||
// The attestation certificate will be signed by the attestation keys the secure
|
||||
// area has been provisioned with. The given |challenge| and |applicationId|
|
||||
// will be used as will |testCredential|.
|
||||
// If |attestationKeyBlob| is non-NULL, the certificate must be signed by the
|
||||
// the provided attestation key. Else, the certificate must be signed by the
|
||||
// attestation key that the secure area has been factory provisioned with. The
|
||||
// given |challenge|, |applicationId|, and |testCredential| must be signed
|
||||
// into the attestation.
|
||||
//
|
||||
// The generated certificate will be in X.509 format and returned in |cert|
|
||||
// and |certSize| must be set to the size of this array and this function will
|
||||
// set it to the size of the certification chain on successfully return.
|
||||
// When |attestationKeyBlob| is non-NULL, then |attestationKeyCert| must
|
||||
// also be passed so that the underlying implementation can properly chain up
|
||||
// the newly-generated certificate to the existing chain.
|
||||
//
|
||||
// The generated certificate must be in X.509 format and returned in |cert|
|
||||
// and |certSize| must be set to the size of this array. This function must
|
||||
// set |certSize| to the size of the certification chain on successfully return.
|
||||
//
|
||||
// This may return either a single certificate or an entire certificate
|
||||
// chain. If it returns only a single certificate, the implementation of
|
||||
@@ -211,8 +217,10 @@ bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
|
||||
//
|
||||
bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const uint8_t* challenge,
|
||||
size_t challengeSize, const uint8_t* applicationId,
|
||||
size_t applicationIdSize, bool testCredential, uint8_t* cert,
|
||||
size_t* certSize); // inout
|
||||
size_t applicationIdSize, bool testCredential,
|
||||
const uint8_t* attestationKeyBlob, size_t attestationKeyBlobSize,
|
||||
const uint8_t* attestationKeyCert, size_t attestationKeyCertSize,
|
||||
uint8_t* /*out*/ cert, size_t* /*inout*/ certSize);
|
||||
|
||||
// Generate an X.509 certificate for the key identified by |publicKey| which
|
||||
// must be of the form returned by eicOpsCreateEcKey().
|
||||
|
||||
@@ -133,7 +133,10 @@ bool eicProvisioningGetId(EicProvisioning* ctx, uint32_t* outId) {
|
||||
|
||||
bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
|
||||
size_t challengeSize, const uint8_t* applicationId,
|
||||
size_t applicationIdSize, uint8_t* publicKeyCert,
|
||||
size_t applicationIdSize, const uint8_t* attestationKeyBlob,
|
||||
size_t attestationKeyBlobSize,
|
||||
const uint8_t* attestationKeyCert,
|
||||
size_t attestationKeyCertSize, uint8_t* publicKeyCert,
|
||||
size_t* publicKeyCertSize) {
|
||||
if (ctx->isUpdate) {
|
||||
eicDebug("Cannot create CredentialKey on update");
|
||||
@@ -142,7 +145,9 @@ bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* cha
|
||||
|
||||
if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge, challengeSize,
|
||||
applicationId, applicationIdSize, ctx->testCredential,
|
||||
publicKeyCert, publicKeyCertSize)) {
|
||||
attestationKeyBlob, attestationKeyBlobSize, attestationKeyCert,
|
||||
attestationKeyCertSize, publicKeyCert, publicKeyCertSize)) {
|
||||
eicDebug("Error creating credential key");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -77,7 +77,10 @@ bool eicProvisioningGetId(EicProvisioning* ctx, uint32_t* outId);
|
||||
|
||||
bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
|
||||
size_t challengeSize, const uint8_t* applicationId,
|
||||
size_t applicationIdSize, uint8_t* publicKeyCert,
|
||||
size_t applicationIdSize, const uint8_t* attestationKeyBlob,
|
||||
size_t attestationKeyBlobSize,
|
||||
const uint8_t* attestationKeyCert,
|
||||
size_t attestationKeyCertSize, uint8_t* publicKeyCert,
|
||||
size_t* publicKeyCertSize);
|
||||
|
||||
bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControlProfileCount,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#define LOG_TAG "android.hardware.identity-service"
|
||||
|
||||
#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.h>
|
||||
@@ -26,20 +27,35 @@
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::base::InitLogging;
|
||||
using ::android::base::LogdLogger;
|
||||
using ::android::base::LogId;
|
||||
using ::android::base::LogSeverity;
|
||||
using ::android::base::StderrLogger;
|
||||
|
||||
using ::aidl::android::hardware::identity::IdentityCredentialStore;
|
||||
using ::aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent;
|
||||
using ::android::hardware::identity::FakeSecureHardwareProxyFactory;
|
||||
using ::android::hardware::identity::SecureHardwareProxyFactory;
|
||||
|
||||
void ComboLogger(LogId id, LogSeverity severity, const char* tag, const char* file,
|
||||
unsigned int line, const char* message) {
|
||||
StderrLogger(id, severity, tag, file, line, message);
|
||||
|
||||
static LogdLogger logdLogger;
|
||||
logdLogger(id, severity, tag, file, line, message);
|
||||
}
|
||||
|
||||
int main(int /*argc*/, char* argv[]) {
|
||||
InitLogging(argv, StderrLogger);
|
||||
InitLogging(argv, ComboLogger);
|
||||
|
||||
sp<SecureHardwareProxyFactory> hwProxyFactory = new FakeSecureHardwareProxyFactory();
|
||||
const std::string remotelyProvisionedComponentName =
|
||||
std::string(IRemotelyProvisionedComponent::descriptor) + "/default";
|
||||
|
||||
ABinderProcess_setThreadPoolMaxThreadCount(0);
|
||||
std::shared_ptr<IdentityCredentialStore> store =
|
||||
ndk::SharedRefBase::make<IdentityCredentialStore>(hwProxyFactory);
|
||||
ndk::SharedRefBase::make<IdentityCredentialStore>(hwProxyFactory,
|
||||
remotelyProvisionedComponentName);
|
||||
|
||||
const std::string instance = std::string() + IdentityCredentialStore::descriptor + "/default";
|
||||
binder_status_t status = AServiceManager_addService(store->asBinder().get(), instance.c_str());
|
||||
|
||||
@@ -11,6 +11,8 @@ cc_test {
|
||||
name: "VtsHalIdentityTargetTest",
|
||||
defaults: [
|
||||
"VtsHalTargetTestDefaults",
|
||||
"keymint_use_latest_hal_aidl_cpp_static",
|
||||
"keymint_use_latest_hal_aidl_ndk_static",
|
||||
"use_libaidlvintf_gtest_helper_static",
|
||||
],
|
||||
cflags: [
|
||||
@@ -32,12 +34,15 @@ cc_test {
|
||||
],
|
||||
shared_libs: [
|
||||
"libbinder",
|
||||
"libbinder_ndk",
|
||||
"libcrypto",
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.security.secureclock-V1-ndk",
|
||||
"libcppbor_external",
|
||||
"libcppcose_rkp",
|
||||
"libkeymaster_portable",
|
||||
"libkeymint_vts_test_utils",
|
||||
"libpuresoftkeymasterdevice",
|
||||
"android.hardware.keymaster@4.0",
|
||||
"android.hardware.identity-support-lib",
|
||||
@@ -46,6 +51,7 @@ cc_test {
|
||||
"android.hardware.keymaster-V4-ndk",
|
||||
"libkeymaster4support",
|
||||
"libkeymaster4_1support",
|
||||
"libkeymint_remote_prov_support",
|
||||
],
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
|
||||
@@ -20,12 +20,16 @@
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <KeyMintAidlTestBase.h>
|
||||
#include <aidl/Gtest.h>
|
||||
#include <aidl/android/hardware/security/keymint/MacedPublicKey.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <keymaster/km_openssl/openssl_utils.h>
|
||||
#include <keymasterV4_1/attestation_record.h>
|
||||
#include <charconv>
|
||||
#include <keymint_support/openssl_utils.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include <charconv>
|
||||
#include <map>
|
||||
|
||||
namespace android::hardware::identity::test_utils {
|
||||
@@ -36,10 +40,13 @@ using std::optional;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
using ::aidl::android::hardware::security::keymint::test::check_maced_pubkey;
|
||||
using ::aidl::android::hardware::security::keymint::test::p256_pub_key;
|
||||
using ::android::sp;
|
||||
using ::android::String16;
|
||||
using ::android::base::StringPrintf;
|
||||
using ::android::binder::Status;
|
||||
using ::android::hardware::security::keymint::MacedPublicKey;
|
||||
using ::keymaster::X509_Ptr;
|
||||
|
||||
bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
|
||||
@@ -58,6 +65,77 @@ bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential
|
||||
}
|
||||
}
|
||||
|
||||
optional<vector<vector<uint8_t>>> createFakeRemotelyProvisionedCertificateChain(
|
||||
const MacedPublicKey& macedPublicKey) {
|
||||
// The helper library uses the NDK symbols, so play a little trickery here to convert
|
||||
// the data into the proper type so we can reuse the helper function to get the pubkey.
|
||||
::aidl::android::hardware::security::keymint::MacedPublicKey ndkMacedPublicKey;
|
||||
ndkMacedPublicKey.macedKey = macedPublicKey.macedKey;
|
||||
|
||||
vector<uint8_t> publicKeyBits;
|
||||
check_maced_pubkey(ndkMacedPublicKey, /*testMode=*/true, &publicKeyBits);
|
||||
|
||||
::aidl::android::hardware::security::keymint::EVP_PKEY_Ptr publicKey;
|
||||
p256_pub_key(publicKeyBits, &publicKey);
|
||||
|
||||
// Generate an arbitrary root key for our chain
|
||||
bssl::UniquePtr<EC_KEY> ecRootKey(EC_KEY_new());
|
||||
bssl::UniquePtr<EVP_PKEY> rootKey(EVP_PKEY_new());
|
||||
if (ecRootKey.get() == nullptr || rootKey.get() == nullptr) {
|
||||
LOG(ERROR) << "Memory allocation failed";
|
||||
return {};
|
||||
}
|
||||
|
||||
bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
|
||||
if (group.get() == nullptr) {
|
||||
LOG(ERROR) << "Error creating EC group by curve name";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (EC_KEY_set_group(ecRootKey.get(), group.get()) != 1 ||
|
||||
EC_KEY_generate_key(ecRootKey.get()) != 1 || EC_KEY_check_key(ecRootKey.get()) < 0) {
|
||||
LOG(ERROR) << "Error generating key";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (EVP_PKEY_set1_EC_KEY(rootKey.get(), ecRootKey.get()) != 1) {
|
||||
LOG(ERROR) << "Error getting private key";
|
||||
return {};
|
||||
}
|
||||
|
||||
// The VTS test does not fully validate the chain, so we're ok without the proper CA extensions.
|
||||
map<string, vector<uint8_t>> extensions;
|
||||
|
||||
// Now make a self-signed cert
|
||||
optional<vector<uint8_t>> root = support::ecPublicKeyGenerateCertificate(
|
||||
rootKey.get(), rootKey.get(),
|
||||
/*serialDecimal=*/"31415",
|
||||
/*subject=*/"Android IdentityCredential VTS Test Root Certificate",
|
||||
/*subject=*/"Android IdentityCredential VTS Test Root Certificate",
|
||||
/*validityNotBefore=*/time(nullptr),
|
||||
/*validityNotAfter=*/time(nullptr) + 365 * 24 * 3600, extensions);
|
||||
if (!root) {
|
||||
LOG(ERROR) << "Error generating root cert";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Now sign a CA cert so that we have a chain that's good enough to satisfy
|
||||
// the VTS tests.
|
||||
optional<vector<uint8_t>> intermediate = support::ecPublicKeyGenerateCertificate(
|
||||
publicKey.get(), rootKey.get(),
|
||||
/*serialDecimal=*/"42",
|
||||
/*subject=*/"Android IdentityCredential VTS Test Root Certificate",
|
||||
/*subject=*/"Android IdentityCredential VTS Test Attestation Certificate",
|
||||
/*validityNotBefore=*/time(nullptr),
|
||||
/*validityNotAfter=*/time(nullptr) + 365 * 24 * 3600, extensions);
|
||||
if (!intermediate) {
|
||||
LOG(ERROR) << "Error generating intermediate cert";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return vector<vector<uint8_t>>{std::move(*intermediate), std::move(*root)};
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal) {
|
||||
vector<uint8_t> privKey;
|
||||
return generateReaderCertificate(serialDecimal, &privKey);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <android/hardware/identity/IIdentityCredentialStore.h>
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
#include <android/hardware/security/keymint/MacedPublicKey.h>
|
||||
#include <cppbor.h>
|
||||
#include <cppbor_parse.h>
|
||||
#include <gtest/gtest.h>
|
||||
@@ -97,6 +98,9 @@ struct TestProfile {
|
||||
bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
|
||||
sp<IIdentityCredentialStore>& credentialStore, bool testCredential);
|
||||
|
||||
optional<vector<vector<uint8_t>>> createFakeRemotelyProvisionedCertificateChain(
|
||||
const ::android::hardware::security::keymint::MacedPublicKey& macedPublicKey);
|
||||
|
||||
optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal);
|
||||
|
||||
optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal,
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include <aidl/Gtest.h>
|
||||
#include <aidl/Vintf.h>
|
||||
#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
|
||||
#include <aidl/android/hardware/security/keymint/MacedPublicKey.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android/hardware/identity/IIdentityCredentialStore.h>
|
||||
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
||||
@@ -42,6 +44,8 @@ using std::vector;
|
||||
using ::android::sp;
|
||||
using ::android::String16;
|
||||
using ::android::binder::Status;
|
||||
using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
|
||||
using ::android::hardware::security::keymint::MacedPublicKey;
|
||||
|
||||
class IdentityCredentialTests : public testing::TestWithParam<string> {
|
||||
public:
|
||||
@@ -101,6 +105,103 @@ TEST_P(IdentityCredentialTests, verifyAttestationSuccessWithChallenge) {
|
||||
attestationApplicationId, false);
|
||||
}
|
||||
|
||||
TEST_P(IdentityCredentialTests, verifyAttestationSuccessWithRemoteProvisioning) {
|
||||
HardwareInformation hwInfo;
|
||||
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
|
||||
|
||||
if (!hwInfo.isRemoteKeyProvisioningSupported) {
|
||||
GTEST_SKIP() << "Remote provisioning is not supported";
|
||||
}
|
||||
|
||||
Status result;
|
||||
|
||||
sp<IWritableIdentityCredential> writableCredential;
|
||||
ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
|
||||
false /* testCredential */));
|
||||
|
||||
sp<IRemotelyProvisionedComponent> rpc;
|
||||
result = credentialStore_->getRemotelyProvisionedComponent(&rpc);
|
||||
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage();
|
||||
|
||||
MacedPublicKey macedPublicKey;
|
||||
std::vector<uint8_t> attestationKey;
|
||||
result = rpc->generateEcdsaP256KeyPair(/*testMode=*/true, &macedPublicKey, &attestationKey);
|
||||
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage();
|
||||
|
||||
optional<vector<vector<uint8_t>>> remotelyProvisionedCertChain =
|
||||
test_utils::createFakeRemotelyProvisionedCertificateChain(macedPublicKey);
|
||||
ASSERT_TRUE(remotelyProvisionedCertChain);
|
||||
|
||||
vector<uint8_t> concatenatedCerts;
|
||||
for (const vector<uint8_t>& cert : *remotelyProvisionedCertChain) {
|
||||
concatenatedCerts.insert(concatenatedCerts.end(), cert.begin(), cert.end());
|
||||
}
|
||||
result = writableCredential->setRemotelyProvisionedAttestationKey(attestationKey,
|
||||
concatenatedCerts);
|
||||
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage();
|
||||
|
||||
string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1";
|
||||
vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
|
||||
vector<Certificate> attestationCertificate;
|
||||
vector<uint8_t> attestationApplicationId = {1};
|
||||
|
||||
result = writableCredential->getAttestationCertificate(
|
||||
attestationApplicationId, attestationChallenge, &attestationCertificate);
|
||||
|
||||
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage();
|
||||
|
||||
test_utils::validateAttestationCertificate(attestationCertificate, attestationChallenge,
|
||||
attestationApplicationId, false);
|
||||
|
||||
ASSERT_EQ(remotelyProvisionedCertChain->size() + 1, attestationCertificate.size());
|
||||
for (size_t i = 0; i < remotelyProvisionedCertChain->size(); ++i) {
|
||||
ASSERT_EQ(remotelyProvisionedCertChain->at(i),
|
||||
attestationCertificate[i + 1].encodedCertificate)
|
||||
<< "Certificate mismatch (cert index " << i + 1 << " out of "
|
||||
<< attestationCertificate.size() << " total certs)";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(IdentityCredentialTests, verifyRemotelyProvisionedKeyMayOnlyBeSetOnce) {
|
||||
HardwareInformation hwInfo;
|
||||
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
|
||||
|
||||
if (!hwInfo.isRemoteKeyProvisioningSupported) {
|
||||
GTEST_SKIP() << "Remote provisioning is not supported";
|
||||
}
|
||||
|
||||
sp<IRemotelyProvisionedComponent> rpc;
|
||||
Status result = credentialStore_->getRemotelyProvisionedComponent(&rpc);
|
||||
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage();
|
||||
|
||||
MacedPublicKey macedPublicKey;
|
||||
std::vector<uint8_t> attestationKey;
|
||||
result = rpc->generateEcdsaP256KeyPair(/*testMode=*/true, &macedPublicKey, &attestationKey);
|
||||
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage();
|
||||
|
||||
optional<vector<vector<uint8_t>>> remotelyProvisionedCertChain =
|
||||
test_utils::createFakeRemotelyProvisionedCertificateChain(macedPublicKey);
|
||||
ASSERT_TRUE(remotelyProvisionedCertChain);
|
||||
|
||||
vector<uint8_t> concatenatedCerts;
|
||||
for (const vector<uint8_t>& cert : *remotelyProvisionedCertChain) {
|
||||
concatenatedCerts.insert(concatenatedCerts.end(), cert.begin(), cert.end());
|
||||
}
|
||||
|
||||
sp<IWritableIdentityCredential> writableCredential;
|
||||
ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
|
||||
/*testCredential=*/false));
|
||||
|
||||
result = writableCredential->setRemotelyProvisionedAttestationKey(attestationKey,
|
||||
concatenatedCerts);
|
||||
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage();
|
||||
|
||||
// Now try again, and verify that the implementation rejects it.
|
||||
result = writableCredential->setRemotelyProvisionedAttestationKey(attestationKey,
|
||||
concatenatedCerts);
|
||||
EXPECT_FALSE(result.isOk());
|
||||
}
|
||||
|
||||
TEST_P(IdentityCredentialTests, verifyAttestationDoubleCallFails) {
|
||||
Status result;
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
|
||||
#define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -128,6 +130,15 @@ optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAnd
|
||||
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
|
||||
bool isTestCredential);
|
||||
|
||||
// Alternate version of createEcKeyPairAndAttestation that accepts an attestation key
|
||||
// blob to sign the generated key. Only a single certificate is returned, rather than
|
||||
// a full chain.
|
||||
//
|
||||
optional<std::pair<vector<uint8_t>, vector<uint8_t>>> createEcKeyPairWithAttestationKey(
|
||||
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
|
||||
const vector<uint8_t>& attestationKeyBlob, const vector<uint8_t>& attestationKeyCert,
|
||||
bool isTestCredential);
|
||||
|
||||
// (TODO: remove when no longer used by 3rd party.)
|
||||
optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey(
|
||||
const vector<uint8_t>& publicKey, const vector<uint8_t>& challenge,
|
||||
@@ -240,6 +251,13 @@ optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
|
||||
time_t validityNotBefore, time_t validityNotAfter,
|
||||
const map<string, vector<uint8_t>>& extensions);
|
||||
|
||||
// Identical behavior to the above version of ecPublicKeyGenerateCertificate, except this
|
||||
// overload takes OpenSSL key parameters instead of key bitstrings as inputs.
|
||||
optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
|
||||
EVP_PKEY* publicKey, EVP_PKEY* signingKey, const string& serialDecimal,
|
||||
const string& issuer, const string& subject, time_t validityNotBefore,
|
||||
time_t validityNotAfter, const map<string, vector<uint8_t>>& extensions);
|
||||
|
||||
// Performs Elliptic-curve Diffie-Helman using |publicKey| (which must be in the
|
||||
// format returned by ecKeyPairGetPublicKey()) and |privateKey| (which must be
|
||||
// in the format returned by ecKeyPairGetPrivateKey()).
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include <keymaster/contexts/pure_soft_keymaster_context.h>
|
||||
#include <keymaster/contexts/soft_attestation_cert.h>
|
||||
#include <keymaster/keymaster_tags.h>
|
||||
#include <keymaster/km_openssl/asymmetric_key.h>
|
||||
#include <keymaster/km_openssl/attestation_utils.h>
|
||||
#include <keymaster/km_openssl/certificate_utils.h>
|
||||
|
||||
@@ -168,6 +169,286 @@ using ASN1_OBJECT_Ptr = bssl::UniquePtr<ASN1_OBJECT>;
|
||||
using X509_NAME_Ptr = bssl::UniquePtr<X509_NAME>;
|
||||
using X509_EXTENSION_Ptr = bssl::UniquePtr<X509_EXTENSION>;
|
||||
|
||||
namespace {
|
||||
|
||||
EVP_PKEY_Ptr generateP256Key() {
|
||||
EC_KEY_Ptr ec_key(EC_KEY_new());
|
||||
EVP_PKEY_Ptr pkey(EVP_PKEY_new());
|
||||
EC_GROUP_Ptr group(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 {};
|
||||
}
|
||||
|
||||
return pkey;
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> derEncodeKeyPair(const EVP_PKEY& pkey) {
|
||||
int size = i2d_PrivateKey(&pkey, nullptr);
|
||||
if (size == 0) {
|
||||
LOG(ERROR) << "Error generating public key encoding";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> keyPair(size);
|
||||
unsigned char* p = keyPair.data();
|
||||
i2d_PrivateKey(&pkey, &p);
|
||||
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
// Extract the issuer subject name from the leaf cert in the given chain,
|
||||
// returning it as DER-encoded bytes.
|
||||
optional<vector<uint8_t>> extractDerSubjectFromCertificate(const vector<uint8_t>& certificate) {
|
||||
const uint8_t* input = certificate.data();
|
||||
X509_Ptr cert(d2i_X509(/*cert=*/nullptr, &input, certificate.size()));
|
||||
if (!cert) {
|
||||
LOG(ERROR) << "Failed to parse certificate";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
X509_NAME* subject = X509_get_subject_name(cert.get());
|
||||
if (!subject) {
|
||||
LOG(ERROR) << "Failed to retrieve subject name";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int encodedSubjectLength = i2d_X509_NAME(subject, /*out=*/nullptr);
|
||||
if (encodedSubjectLength < 0) {
|
||||
LOG(ERROR) << "Error obtaining encoded subject name length";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> encodedSubject(encodedSubjectLength);
|
||||
uint8_t* out = encodedSubject.data();
|
||||
if (encodedSubjectLength != i2d_X509_NAME(subject, &out)) {
|
||||
LOG(ERROR) << "Error encoding subject name";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return encodedSubject;
|
||||
}
|
||||
|
||||
// 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>>> signAttestationCertificate(
|
||||
const ::keymaster::PureSoftKeymasterContext& context, const EVP_PKEY* key,
|
||||
const vector<uint8_t>& applicationId, const vector<uint8_t>& challenge,
|
||||
const vector<uint8_t>& attestationKeyBlob,
|
||||
const vector<uint8_t>& derAttestationCertSubjectName, uint64_t activeTimeMilliSeconds,
|
||||
uint64_t expireTimeMilliSeconds, bool isTestCredential) {
|
||||
::keymaster::X509_NAME_Ptr subjectName;
|
||||
if (KM_ERROR_OK !=
|
||||
::keymaster::make_name_from_str("Android Identity Credential Key", &subjectName)) {
|
||||
LOG(ERROR) << "Cannot create attestation subject";
|
||||
return {};
|
||||
}
|
||||
|
||||
vector<uint8_t> subject(i2d_X509_NAME(subjectName.get(), NULL));
|
||||
unsigned char* subjectPtr = subject.data();
|
||||
|
||||
i2d_X509_NAME(subjectName.get(), &subjectPtr);
|
||||
|
||||
::keymaster::AuthorizationSet auth_set(
|
||||
::keymaster::AuthorizationSetBuilder()
|
||||
.Authorization(::keymaster::TAG_CERTIFICATE_NOT_BEFORE, activeTimeMilliSeconds)
|
||||
.Authorization(::keymaster::TAG_CERTIFICATE_NOT_AFTER, expireTimeMilliSeconds)
|
||||
.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_CERTIFICATE_SUBJECT, subject.data(),
|
||||
subject.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::AuthorizationSetBuilder hwEnforcedBuilder =
|
||||
::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_OS_VERSION, 42)
|
||||
.Authorization(::keymaster::TAG_OS_PATCHLEVEL, 43);
|
||||
|
||||
// Only include TAG_IDENTITY_CREDENTIAL_KEY if it's not a test credential
|
||||
if (!isTestCredential) {
|
||||
hwEnforcedBuilder.Authorization(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY);
|
||||
}
|
||||
::keymaster::AuthorizationSet hwEnforced(hwEnforcedBuilder);
|
||||
|
||||
keymaster_error_t error;
|
||||
::keymaster::AttestKeyInfo attestKeyInfo;
|
||||
::keymaster::KeymasterBlob issuerSubjectNameBlob;
|
||||
if (!attestationKeyBlob.empty()) {
|
||||
::keymaster::KeymasterKeyBlob blob(attestationKeyBlob.data(), attestationKeyBlob.size());
|
||||
::keymaster::UniquePtr<::keymaster::Key> parsedKey;
|
||||
error = context.ParseKeyBlob(blob, /*additional_params=*/{}, &parsedKey);
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "Error loading attestation key: " << error;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
attestKeyInfo.signing_key =
|
||||
static_cast<::keymaster::AsymmetricKey&>(*parsedKey).InternalToEvp();
|
||||
issuerSubjectNameBlob = ::keymaster::KeymasterBlob(derAttestationCertSubjectName.data(),
|
||||
derAttestationCertSubjectName.size());
|
||||
attestKeyInfo.issuer_subject = &issuerSubjectNameBlob;
|
||||
}
|
||||
|
||||
::keymaster::CertificateChain certChain = generate_attestation(
|
||||
key, swEnforced, hwEnforced, auth_set, std::move(attestKeyInfo), context, &error);
|
||||
|
||||
if (KM_ERROR_OK != error) {
|
||||
LOG(ERROR) << "Error generating attestation from EVP key: " << error;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<vector<uint8_t>> vectors(certChain.entry_count);
|
||||
for (std::size_t i = 0; i < certChain.entry_count; i++) {
|
||||
vectors[i] = {certChain.entries[i].data,
|
||||
certChain.entries[i].data + certChain.entries[i].data_length};
|
||||
}
|
||||
return vectors;
|
||||
}
|
||||
|
||||
int parseDigits(const char** s, int numDigits) {
|
||||
int result;
|
||||
auto [_, ec] = std::from_chars(*s, *s + numDigits, result);
|
||||
if (ec != std::errc()) {
|
||||
LOG(ERROR) << "Error parsing " << numDigits << " digits "
|
||||
<< " from " << s;
|
||||
return 0;
|
||||
}
|
||||
*s += numDigits;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool parseAsn1Time(const ASN1_TIME* asn1Time, time_t* outTime) {
|
||||
struct tm tm;
|
||||
|
||||
memset(&tm, '\0', sizeof(tm));
|
||||
const char* timeStr = (const char*)asn1Time->data;
|
||||
const char* s = timeStr;
|
||||
if (asn1Time->type == V_ASN1_UTCTIME) {
|
||||
tm.tm_year = parseDigits(&s, 2);
|
||||
if (tm.tm_year < 70) {
|
||||
tm.tm_year += 100;
|
||||
}
|
||||
} else if (asn1Time->type == V_ASN1_GENERALIZEDTIME) {
|
||||
tm.tm_year = parseDigits(&s, 4) - 1900;
|
||||
tm.tm_year -= 1900;
|
||||
} else {
|
||||
LOG(ERROR) << "Unsupported ASN1_TIME type " << asn1Time->type;
|
||||
return false;
|
||||
}
|
||||
tm.tm_mon = parseDigits(&s, 2) - 1;
|
||||
tm.tm_mday = parseDigits(&s, 2);
|
||||
tm.tm_hour = parseDigits(&s, 2);
|
||||
tm.tm_min = parseDigits(&s, 2);
|
||||
tm.tm_sec = parseDigits(&s, 2);
|
||||
// This may need to be updated if someone create certificates using +/- instead of Z.
|
||||
//
|
||||
if (*s != 'Z') {
|
||||
LOG(ERROR) << "Expected Z in string '" << timeStr << "' at offset " << (s - timeStr);
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t t = timegm(&tm);
|
||||
if (t == -1) {
|
||||
LOG(ERROR) << "Error converting broken-down time to time_t";
|
||||
return false;
|
||||
}
|
||||
*outTime = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
optional<uint64_t> getCertificateExpiryAsMillis(const uint8_t* derCert, size_t derCertSize) {
|
||||
X509_Ptr x509Cert(d2i_X509(nullptr, &derCert, derCertSize));
|
||||
if (!x509Cert) {
|
||||
LOG(ERROR) << "Error parsing certificate";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
time_t notAfter;
|
||||
if (!parseAsn1Time(X509_get0_notAfter(x509Cert.get()), ¬After)) {
|
||||
LOG(ERROR) << "Error getting notAfter from batch certificate";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return notAfter * 1000;
|
||||
}
|
||||
|
||||
optional<vector<vector<uint8_t>>> createAttestation(EVP_PKEY* pkey,
|
||||
const vector<uint8_t>& challenge,
|
||||
const vector<uint8_t>& applicationId,
|
||||
bool isTestCredential) {
|
||||
// Pretend to be implemented in a trusted environment just so we can pass
|
||||
// the VTS tests. Of course, this is a pretend-only game since hopefully no
|
||||
// relying party is ever going to trust our batch key and those keys above
|
||||
// it.
|
||||
::keymaster::PureSoftKeymasterContext context(::keymaster::KmVersion::KEYMINT_1,
|
||||
KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
|
||||
|
||||
keymaster_error_t error;
|
||||
::keymaster::CertificateChain attestation_chain =
|
||||
context.GetAttestationChain(KM_ALGORITHM_EC, &error);
|
||||
if (KM_ERROR_OK != error) {
|
||||
LOG(ERROR) << "Error getting attestation chain " << error;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (attestation_chain.entry_count < 1) {
|
||||
LOG(ERROR) << "Expected at least one entry in attestation chain";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
uint64_t activeTimeMs = time(nullptr) * 1000;
|
||||
optional<uint64_t> expireTimeMs = getCertificateExpiryAsMillis(
|
||||
attestation_chain.entries[0].data, attestation_chain.entries[0].data_length);
|
||||
if (!expireTimeMs) {
|
||||
LOG(ERROR) << "Error getting expiration time for batch cert";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return signAttestationCertificate(context, pkey, applicationId, challenge,
|
||||
/*attestationKeyBlob=*/{},
|
||||
/*derAttestationCertSubjectName=*/{}, activeTimeMs,
|
||||
*expireTimeMs, isTestCredential);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// bool getRandom(size_t numBytes, vector<uint8_t>& output) {
|
||||
optional<vector<uint8_t>> getRandom(size_t numBytes) {
|
||||
vector<uint8_t> output;
|
||||
@@ -577,69 +858,30 @@ optional<vector<uint8_t>> hmacSha256(const vector<uint8_t>& key, const vector<ui
|
||||
return hmac;
|
||||
}
|
||||
|
||||
int parseDigits(const char** s, int numDigits) {
|
||||
int result;
|
||||
auto [_, ec] = std::from_chars(*s, *s + numDigits, result);
|
||||
if (ec != std::errc()) {
|
||||
LOG(ERROR) << "Error parsing " << numDigits << " digits "
|
||||
<< " from " << s;
|
||||
return 0;
|
||||
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
|
||||
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
|
||||
bool isTestCredential) {
|
||||
EVP_PKEY_Ptr pkey = generateP256Key();
|
||||
|
||||
optional<vector<vector<uint8_t>>> attestationCertChain =
|
||||
createAttestation(pkey.get(), challenge, applicationId, isTestCredential);
|
||||
if (!attestationCertChain) {
|
||||
LOG(ERROR) << "Error create attestation from key and challenge";
|
||||
return {};
|
||||
}
|
||||
*s += numDigits;
|
||||
return result;
|
||||
|
||||
optional<vector<uint8_t>> keyPair = derEncodeKeyPair(*pkey);
|
||||
if (!keyPair) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return make_pair(*keyPair, *attestationCertChain);
|
||||
}
|
||||
|
||||
bool parseAsn1Time(const ASN1_TIME* asn1Time, time_t* outTime) {
|
||||
struct tm tm;
|
||||
|
||||
memset(&tm, '\0', sizeof(tm));
|
||||
const char* timeStr = (const char*)asn1Time->data;
|
||||
const char* s = timeStr;
|
||||
if (asn1Time->type == V_ASN1_UTCTIME) {
|
||||
tm.tm_year = parseDigits(&s, 2);
|
||||
if (tm.tm_year < 70) {
|
||||
tm.tm_year += 100;
|
||||
}
|
||||
} else if (asn1Time->type == V_ASN1_GENERALIZEDTIME) {
|
||||
tm.tm_year = parseDigits(&s, 4) - 1900;
|
||||
tm.tm_year -= 1900;
|
||||
} else {
|
||||
LOG(ERROR) << "Unsupported ASN1_TIME type " << asn1Time->type;
|
||||
return false;
|
||||
}
|
||||
tm.tm_mon = parseDigits(&s, 2) - 1;
|
||||
tm.tm_mday = parseDigits(&s, 2);
|
||||
tm.tm_hour = parseDigits(&s, 2);
|
||||
tm.tm_min = parseDigits(&s, 2);
|
||||
tm.tm_sec = parseDigits(&s, 2);
|
||||
// This may need to be updated if someone create certificates using +/- instead of Z.
|
||||
//
|
||||
if (*s != 'Z') {
|
||||
LOG(ERROR) << "Expected Z in string '" << timeStr << "' at offset " << (s - timeStr);
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t t = timegm(&tm);
|
||||
if (t == -1) {
|
||||
LOG(ERROR) << "Error converting broken-down time to time_t";
|
||||
return false;
|
||||
}
|
||||
*outTime = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// If 0 is passed for expiration time, the expiration time from batch
|
||||
// certificate will be used.
|
||||
//
|
||||
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, bool isTestCredential) {
|
||||
optional<std::pair<vector<uint8_t>, vector<uint8_t>>> createEcKeyPairWithAttestationKey(
|
||||
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
|
||||
const vector<uint8_t>& attestationKeyBlob, const vector<uint8_t>& attestationKeyCert,
|
||||
bool isTestCredential) {
|
||||
// Pretend to be implemented in a trusted environment just so we can pass
|
||||
// the VTS tests. Of course, this is a pretend-only game since hopefully no
|
||||
// relying party is ever going to trust our batch key and those keys above
|
||||
@@ -647,148 +889,45 @@ optional<vector<vector<uint8_t>>> createAttestation(
|
||||
::keymaster::PureSoftKeymasterContext context(::keymaster::KmVersion::KEYMINT_1,
|
||||
KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
|
||||
|
||||
keymaster_error_t error;
|
||||
::keymaster::CertificateChain attestation_chain =
|
||||
context.GetAttestationChain(KM_ALGORITHM_EC, &error);
|
||||
if (KM_ERROR_OK != error) {
|
||||
LOG(ERROR) << "Error getting attestation chain " << error;
|
||||
return {};
|
||||
}
|
||||
if (expireTimeMilliSeconds == 0) {
|
||||
if (attestation_chain.entry_count < 1) {
|
||||
LOG(ERROR) << "Expected at least one entry in attestation chain";
|
||||
return {};
|
||||
}
|
||||
keymaster_blob_t* bcBlob = &(attestation_chain.entries[0]);
|
||||
const uint8_t* bcData = bcBlob->data;
|
||||
auto bc = X509_Ptr(d2i_X509(nullptr, &bcData, bcBlob->data_length));
|
||||
time_t bcNotAfter;
|
||||
if (!parseAsn1Time(X509_get0_notAfter(bc.get()), &bcNotAfter)) {
|
||||
LOG(ERROR) << "Error getting notAfter from batch certificate";
|
||||
return {};
|
||||
}
|
||||
expireTimeMilliSeconds = bcNotAfter * 1000;
|
||||
EVP_PKEY_Ptr pkey = generateP256Key();
|
||||
|
||||
uint64_t validFromMs = time(nullptr) * 1000;
|
||||
optional<uint64_t> notAfterMs =
|
||||
getCertificateExpiryAsMillis(attestationKeyCert.data(), attestationKeyCert.size());
|
||||
if (!notAfterMs) {
|
||||
LOG(ERROR) << "Error getting expiration time for attestation cert";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
::keymaster::X509_NAME_Ptr subjectName;
|
||||
if (KM_ERROR_OK !=
|
||||
::keymaster::make_name_from_str("Android Identity Credential Key", &subjectName)) {
|
||||
LOG(ERROR) << "Cannot create attestation subject";
|
||||
return {};
|
||||
optional<vector<uint8_t>> derIssuerSubject =
|
||||
extractDerSubjectFromCertificate(attestationKeyCert);
|
||||
if (!derIssuerSubject) {
|
||||
LOG(ERROR) << "Error error extracting issuer name from the given certificate chain";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> subject(i2d_X509_NAME(subjectName.get(), NULL));
|
||||
unsigned char* subjectPtr = subject.data();
|
||||
|
||||
i2d_X509_NAME(subjectName.get(), &subjectPtr);
|
||||
|
||||
::keymaster::AuthorizationSet auth_set(
|
||||
::keymaster::AuthorizationSetBuilder()
|
||||
.Authorization(::keymaster::TAG_CERTIFICATE_NOT_BEFORE, activeTimeMilliSeconds)
|
||||
.Authorization(::keymaster::TAG_CERTIFICATE_NOT_AFTER, expireTimeMilliSeconds)
|
||||
.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_CERTIFICATE_SUBJECT, subject.data(),
|
||||
subject.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::AuthorizationSetBuilder hwEnforcedBuilder =
|
||||
::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_OS_VERSION, 42)
|
||||
.Authorization(::keymaster::TAG_OS_PATCHLEVEL, 43);
|
||||
|
||||
// Only include TAG_IDENTITY_CREDENTIAL_KEY if it's not a test credential
|
||||
if (!isTestCredential) {
|
||||
hwEnforcedBuilder.Authorization(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY);
|
||||
optional<vector<vector<uint8_t>>> attestationCertChain = signAttestationCertificate(
|
||||
context, pkey.get(), applicationId, challenge, attestationKeyBlob, *derIssuerSubject,
|
||||
validFromMs, *notAfterMs, isTestCredential);
|
||||
if (!attestationCertChain) {
|
||||
LOG(ERROR) << "Error signing attestation certificate";
|
||||
return std::nullopt;
|
||||
}
|
||||
::keymaster::AuthorizationSet hwEnforced(hwEnforcedBuilder);
|
||||
|
||||
::keymaster::CertificateChain cert_chain_out = generate_attestation(
|
||||
key, swEnforced, hwEnforced, auth_set, {} /* attest_key */, context, &error);
|
||||
|
||||
if (KM_ERROR_OK != error) {
|
||||
LOG(ERROR) << "Error generating attestation from EVP key: " << error;
|
||||
return {};
|
||||
}
|
||||
|
||||
// translate certificate format from keymaster_cert_chain_t to vector<vector<uint8_t>>.
|
||||
vector<vector<uint8_t>> attestationCertificate;
|
||||
for (std::size_t 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,
|
||||
bool isTestCredential) {
|
||||
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 nowMs = time(nullptr) * 1000;
|
||||
uint64_t expireTimeMs = 0; // Set to same as batch certificate
|
||||
|
||||
optional<vector<vector<uint8_t>>> attestationCert = createAttestation(
|
||||
pkey.get(), applicationId, challenge, nowMs, expireTimeMs, isTestCredential);
|
||||
if (!attestationCert) {
|
||||
if (!attestationCertChain) {
|
||||
LOG(ERROR) << "Error create attestation from key and challenge";
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
if (attestationCertChain->size() != 1) {
|
||||
LOG(ERROR) << "Expected exactly one attestation cert, got " << attestationCertChain->size();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int size = i2d_PrivateKey(pkey.get(), nullptr);
|
||||
if (size == 0) {
|
||||
LOG(ERROR) << "Error generating public key encoding";
|
||||
return {};
|
||||
optional<vector<uint8_t>> keyPair = derEncodeKeyPair(*pkey);
|
||||
if (!keyPair) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vector<uint8_t> keyPair(size);
|
||||
unsigned char* p = keyPair.data();
|
||||
i2d_PrivateKey(pkey.get(), &p);
|
||||
|
||||
return make_pair(keyPair, attestationCert.value());
|
||||
return make_pair(*keyPair, attestationCertChain->at(0));
|
||||
}
|
||||
|
||||
optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey(
|
||||
@@ -820,12 +959,8 @@ optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey(
|
||||
return {};
|
||||
}
|
||||
|
||||
uint64_t nowMs = time(nullptr) * 1000;
|
||||
uint64_t expireTimeMs = 0; // Set to same as batch certificate
|
||||
|
||||
optional<vector<vector<uint8_t>>> attestationCert =
|
||||
createAttestation(pkey.get(), applicationId, challenge, nowMs, expireTimeMs,
|
||||
false /* isTestCredential */);
|
||||
createAttestation(pkey.get(), applicationId, challenge, false /* isTestCredential */);
|
||||
if (!attestationCert) {
|
||||
LOG(ERROR) << "Error create attestation from key and challenge";
|
||||
return {};
|
||||
@@ -1134,6 +1269,14 @@ optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
|
||||
return {};
|
||||
}
|
||||
|
||||
return ecPublicKeyGenerateCertificate(pkey.get(), privPkey.get(), serialDecimal, issuer,
|
||||
subject, validityNotBefore, validityNotAfter, extensions);
|
||||
}
|
||||
|
||||
optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
|
||||
EVP_PKEY* publicKey, EVP_PKEY* signingKey, const string& serialDecimal,
|
||||
const string& issuer, const string& subject, time_t validityNotBefore,
|
||||
time_t validityNotAfter, const map<string, vector<uint8_t>>& extensions) {
|
||||
auto x509 = X509_Ptr(X509_new());
|
||||
if (!x509.get()) {
|
||||
LOG(ERROR) << "Error creating X509 certificate";
|
||||
@@ -1145,7 +1288,7 @@ optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
|
||||
return {};
|
||||
}
|
||||
|
||||
if (X509_set_pubkey(x509.get(), pkey.get()) != 1) {
|
||||
if (X509_set_pubkey(x509.get(), publicKey) != 1) {
|
||||
LOG(ERROR) << "Error setting public key";
|
||||
return {};
|
||||
}
|
||||
@@ -1220,7 +1363,7 @@ optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
|
||||
}
|
||||
}
|
||||
|
||||
if (X509_sign(x509.get(), privPkey.get(), EVP_sha256()) == 0) {
|
||||
if (X509_sign(x509.get(), signingKey, EVP_sha256()) == 0) {
|
||||
LOG(ERROR) << "Error signing X509 certificate";
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -56,6 +56,13 @@ cc_defaults {
|
||||
],
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "keymint_use_latest_hal_aidl_cpp_static",
|
||||
static_libs: [
|
||||
"android.hardware.security.keymint-V2-cpp",
|
||||
],
|
||||
}
|
||||
|
||||
// A rust_defaults that includes the latest KeyMint AIDL library.
|
||||
// Modules that depend on KeyMint directly can include this cc_defaults to avoid
|
||||
// managing dependency versions explicitly.
|
||||
|
||||
@@ -75,6 +75,9 @@ cc_test_library {
|
||||
export_include_dirs: [
|
||||
".",
|
||||
],
|
||||
export_static_lib_headers: [
|
||||
"libkeymint_support",
|
||||
],
|
||||
static_libs: [
|
||||
"libgmock_ndk",
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user