Merge changes from topic "revert-1959803-revert-1956689-add rkp to identity-default-ENFHZTRTBV-OLKYWRVSFZ"
* changes: Revert^2 "Add remote key provisioning to the IC HAL" Revert^2 "Refactor IC support for RKP" Revert^2 "Log to logd in the default identity service" Revert^2 "Fix formatting of identity credential aidl"
This commit is contained in:
commit
ff225d91c0
32 changed files with 903 additions and 273 deletions
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -55,6 +55,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",
|
||||
],
|
||||
|
|
Loading…
Reference in a new issue