Merge changes from topic "add rkp to identity-default" am: 79fdf4d688 am: acc7215686 am: 7897aabec2

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/1956689

Change-Id: I235b7950c7e564212d2883b5fec2112fb15fb9d8
This commit is contained in:
Seth Moore 2022-01-25 21:12:19 +00:00 committed by Automerger Merge Worker
commit 16a2b06647
30 changed files with 518 additions and 59 deletions

View file

@ -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: [

View file

@ -39,4 +39,5 @@ parcelable HardwareInformation {
int dataChunkSize;
boolean isDirectAccess;
@utf8InCpp String[] supportedDocTypes;
boolean isRemoteKeyProvisioningSupported = false;
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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.
@ -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();
}

View file

@ -335,4 +335,36 @@ interface IWritableIdentityCredential {
* @param expectedProofOfProvisioningSize the expected size of ProofOfProvisioning.
*/
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);
}

View file

@ -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",

View file

@ -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) {

View file

@ -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) {

View file

@ -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;

View file

@ -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,

View file

@ -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_;

View file

@ -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

View file

@ -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

View file

@ -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(

View file

@ -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_;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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().

View file

@ -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;

View file

@ -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,

View file

@ -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>
@ -32,6 +33,7 @@ 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;
@ -47,10 +49,13 @@ int main(int /*argc*/, char* argv[]) {
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());

View file

@ -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",

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -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.

View file

@ -75,6 +75,9 @@ cc_test_library {
export_include_dirs: [
".",
],
export_static_lib_headers: [
"libkeymint_support",
],
static_libs: [
"libgmock_ndk",
],