diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp index dad3b8d74c..e3b819125a 100644 --- a/identity/aidl/Android.bp +++ b/identity/aidl/Android.bp @@ -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: [ diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl index cd8d56b5bd..9b96ea8a68 100644 --- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl +++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl @@ -39,4 +39,5 @@ parcelable HardwareInformation { int dataChunkSize; boolean isDirectAccess; @utf8InCpp String[] supportedDocTypes; + boolean isRemoteKeyProvisioningSupported = false; } diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl index c912c526ab..31ca8b10f3 100644 --- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl +++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl @@ -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; diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl index 9a0fa9e9e5..5377349a52 100644 --- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl +++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl @@ -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); } diff --git a/identity/aidl/android/hardware/identity/HardwareInformation.aidl b/identity/aidl/android/hardware/identity/HardwareInformation.aidl index d67739d94a..acd13b6da6 100644 --- a/identity/aidl/android/hardware/identity/HardwareInformation.aidl +++ b/identity/aidl/android/hardware/identity/HardwareInformation.aidl @@ -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; } diff --git a/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl index 86be7f5879..d3e4da04a0 100644 --- a/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl +++ b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl @@ -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(); } diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl index 22bcf61f64..756b008aa5 100644 --- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl +++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl @@ -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); } diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp index ca24afa6cc..32b35439ef 100644 --- a/identity/aidl/default/Android.bp +++ b/identity/aidl/default/Android.bp @@ -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", diff --git a/identity/aidl/default/EicOpsImpl.cc b/identity/aidl/default/EicOpsImpl.cc index c98a91ebc3..3fd9f1dcee 100644 --- a/identity/aidl/default/EicOpsImpl.cc +++ b/identity/aidl/default/EicOpsImpl.cc @@ -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 challengeVec(challengeSize); - memcpy(challengeVec.data(), challenge, challengeSize); - - vector applicationIdVec(applicationIdSize); - memcpy(applicationIdVec.data(), applicationId, applicationIdSize); - - optional, vector>>> 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 flatChain; + vector keyPair; + vector challengeVec(challenge, challenge + challengeSize); + vector applicationIdVec(applicationId, applicationId + applicationIdSize); + if (attestationKeyBlob && attestationKeyBlobSize > 0 && attestationKeyCert && + attestationKeyCertSize > 0) { + vector attestationKeyBlobVec(attestationKeyBlob, + attestationKeyBlob + attestationKeyBlobSize); + vector attestationKeyCertVec(attestationKeyCert, + attestationKeyCert + attestationKeyCertSize); + optional, vector>> 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, vector>>> 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 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> 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) { diff --git a/identity/aidl/default/FakeSecureHardwareProxy.cpp b/identity/aidl/default/FakeSecureHardwareProxy.cpp index 91e634c0c3..9b9a749427 100644 --- a/identity/aidl/default/FakeSecureHardwareProxy.cpp +++ b/identity/aidl/default/FakeSecureHardwareProxy.cpp @@ -155,7 +155,11 @@ optional> 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 pubKeyCert(publicKeyCertSize); @@ -163,6 +167,23 @@ optional> FakeSecureHardwareProvisioningProxy::createCredentialK return pubKeyCert; } +optional> FakeSecureHardwareProvisioningProxy::createCredentialKeyUsingRkp( + const vector& challenge, const vector& applicationId, + const vector& attestationKeyBlob, const vector& attstationKeyCert) { + size_t publicKeyCertSize = 4096; + vector 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& entryCounts, const string& docType, size_t expectedProofOfProvisioningSize) { diff --git a/identity/aidl/default/FakeSecureHardwareProxy.h b/identity/aidl/default/FakeSecureHardwareProxy.h index df98c7a121..2512074b5f 100644 --- a/identity/aidl/default/FakeSecureHardwareProxy.h +++ b/identity/aidl/default/FakeSecureHardwareProxy.h @@ -43,6 +43,11 @@ class FakeSecureHardwareProvisioningProxy : public SecureHardwareProvisioningPro optional> createCredentialKey(const vector& challenge, const vector& applicationId) override; + optional> createCredentialKeyUsingRkp( + const vector& challenge, const vector& applicationId, + const vector& attestationKeyBlob, + const vector& attestationKeyCert) override; + bool startPersonalization(int accessControlProfileCount, const vector& entryCounts, const string& docType, size_t expectedProofOfProvisioningSize) override; diff --git a/identity/aidl/default/common/IdentityCredential.cpp b/identity/aidl/default/common/IdentityCredential.cpp index 7678ecb918..ff80752ee7 100644 --- a/identity/aidl/default/common/IdentityCredential.cpp +++ b/identity/aidl/default/common/IdentityCredential.cpp @@ -1012,8 +1012,8 @@ ndk::ScopedAStatus IdentityCredential::updateCredential( IIdentityCredentialStore::STATUS_FAILED, "Error creating provisioning proxy")); } shared_ptr wc = - ndk::SharedRefBase::make(provisioningHwProxy, docType_, - testCredential_); + ndk::SharedRefBase::make( + provisioningHwProxy, docType_, testCredential_, hardwareInformation_); if (!wc->initializeForUpdate(encryptedCredentialKeys_)) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( IIdentityCredentialStore::STATUS_FAILED, diff --git a/identity/aidl/default/common/IdentityCredential.h b/identity/aidl/default/common/IdentityCredential.h index 2935fb80a7..592982991d 100644 --- a/identity/aidl/default/common/IdentityCredential.h +++ b/identity/aidl/default/common/IdentityCredential.h @@ -48,11 +48,13 @@ class IdentityCredential : public BnIdentityCredential { public: IdentityCredential(sp hwProxyFactory, const vector& credentialData, - std::shared_ptr session) + std::shared_ptr 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 credentialData_; shared_ptr session_; int numStartRetrievalCalls_; + HardwareInformation hardwareInformation_; // Set by initialize() string docType_; diff --git a/identity/aidl/default/common/IdentityCredentialStore.cpp b/identity/aidl/default/common/IdentityCredentialStore.cpp index 4703ffe646..bbc2cefb8f 100644 --- a/identity/aidl/default/common/IdentityCredentialStore.cpp +++ b/identity/aidl/default/common/IdentityCredentialStore.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "IdentityCredentialStore" #include +#include #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 hwProxyFactory, + optional 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* outWritableCredential) { sp hwProxy = hwProxyFactory_->createProvisioningProxy(); shared_ptr wc = - ndk::SharedRefBase::make(hwProxy, docType, testCredential); + ndk::SharedRefBase::make(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 credential = ndk::SharedRefBase::make( - 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 hwProxy = hwProxyFactory_->createSessionProxy(); - shared_ptr session = - ndk::SharedRefBase::make(hwProxyFactory_, hwProxy); + shared_ptr session = ndk::SharedRefBase::make( + 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* 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 diff --git a/identity/aidl/default/common/IdentityCredentialStore.h b/identity/aidl/default/common/IdentityCredentialStore.h index 77b894dbd6..dd1261b750 100644 --- a/identity/aidl/default/common/IdentityCredentialStore.h +++ b/identity/aidl/default/common/IdentityCredentialStore.h @@ -18,6 +18,7 @@ #define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H #include +#include #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 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 hwProxyFactory, + optional 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* outSession) override; + ndk::ScopedAStatus getRemotelyProvisionedComponent( + shared_ptr<::aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent>* + outRemotelyProvisionedComponent) override; + private: sp hwProxyFactory_; + optional remotelyProvisionedComponentName_; + HardwareInformation hardwareInformation_; }; } // namespace aidl::android::hardware::identity diff --git a/identity/aidl/default/common/PresentationSession.cpp b/identity/aidl/default/common/PresentationSession.cpp index fbd897281a..2eb7f2ea16 100644 --- a/identity/aidl/default/common/PresentationSession.cpp +++ b/identity/aidl/default/common/PresentationSession.cpp @@ -122,8 +122,8 @@ ndk::ScopedAStatus PresentationSession::setSessionTranscript( ndk::ScopedAStatus PresentationSession::getCredential( const vector& credentialData, shared_ptr* outCredential) { shared_ptr p = ref(); - shared_ptr credential = - ndk::SharedRefBase::make(hwProxyFactory_, credentialData, p); + shared_ptr credential = ndk::SharedRefBase::make( + hwProxyFactory_, credentialData, p, hardwareInformation_); int ret = credential->initialize(); if (ret != IIdentityCredentialStore::STATUS_OK) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( diff --git a/identity/aidl/default/common/PresentationSession.h b/identity/aidl/default/common/PresentationSession.h index 76ca67b675..4cb174a82c 100644 --- a/identity/aidl/default/common/PresentationSession.h +++ b/identity/aidl/default/common/PresentationSession.h @@ -38,8 +38,11 @@ using ::std::vector; class PresentationSession : public BnPresentationSession { public: PresentationSession(sp hwProxyFactory, - sp hwProxy) - : hwProxyFactory_(std::move(hwProxyFactory)), hwProxy_(std::move(hwProxy)) {} + sp 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 hwProxyFactory_; sp hwProxy_; + HardwareInformation hardwareInformation_; // Set by initialize() uint64_t id_; diff --git a/identity/aidl/default/common/SecureHardwareProxy.h b/identity/aidl/default/common/SecureHardwareProxy.h index a580444230..9f63ad809b 100644 --- a/identity/aidl/default/common/SecureHardwareProxy.h +++ b/identity/aidl/default/common/SecureHardwareProxy.h @@ -82,6 +82,18 @@ class SecureHardwareProvisioningProxy : public RefBase { virtual optional> createCredentialKey(const vector& challenge, const vector& 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> createCredentialKeyUsingRkp( + const vector& challenge, const vector& applicationId, + const vector& attestationKeyBlob, + const vector& attestationKeyCert) = 0; + virtual bool startPersonalization(int accessControlProfileCount, const vector& entryCounts, const string& docType, size_t expectedProofOfProvisioningSize) = 0; diff --git a/identity/aidl/default/common/WritableIdentityCredential.cpp b/identity/aidl/default/common/WritableIdentityCredential.cpp index 200ee61df4..e420a7b74b 100644 --- a/identity/aidl/default/common/WritableIdentityCredential.cpp +++ b/identity/aidl/default/common/WritableIdentityCredential.cpp @@ -79,8 +79,15 @@ ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate( IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty")); } - optional> certChain = - hwProxy_->createCredentialKey(attestationChallenge, attestationApplicationId); + optional> 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(); - for (const vector& cert : certs.value()) { - Certificate c = Certificate(); + for (vector& cert : certs.value()) { + Certificate c; + c.encodedCertificate = std::move(cert); + outCertificateChain->push_back(std::move(c)); + } + + for (const vector& 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& attestationKeyBlob, + const vector& 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>> 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 diff --git a/identity/aidl/default/common/WritableIdentityCredential.h b/identity/aidl/default/common/WritableIdentityCredential.h index 36ad4300d1..39d32c9dc7 100644 --- a/identity/aidl/default/common/WritableIdentityCredential.h +++ b/identity/aidl/default/common/WritableIdentityCredential.h @@ -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 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* outCredentialData, vector* outProofOfProvisioningSignature) override; + ndk::ScopedAStatus setRemotelyProvisionedAttestationKey( + const vector& attestationKeyBlob, + const vector& attestationCertificateChain) override; + private: // Set by constructor. sp hwProxy_; string docType_; bool testCredential_; + HardwareInformation hardwareInformation_; // This is set in initialize(). bool startPersonalizationCalled_; @@ -109,6 +118,10 @@ class WritableIdentityCredential : public BnWritableIdentityCredential { vector entryAccessControlProfileIds_; vector entryBytes_; set allNameSpaces_; + + // Remotely provisioned attestation data, set via setRemotelyProvisionedAttestationKey + optional> attestationKeyBlob_; + optional>> attestationCertificateChain_; }; } // namespace aidl::android::hardware::identity diff --git a/identity/aidl/default/libeic/EicOps.h b/identity/aidl/default/libeic/EicOps.h index aa26e6202a..df96c7db48 100644 --- a/identity/aidl/default/libeic/EicOps.h +++ b/identity/aidl/default/libeic/EicOps.h @@ -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(). diff --git a/identity/aidl/default/libeic/EicProvisioning.c b/identity/aidl/default/libeic/EicProvisioning.c index a241b71b50..ff009dde6b 100644 --- a/identity/aidl/default/libeic/EicProvisioning.c +++ b/identity/aidl/default/libeic/EicProvisioning.c @@ -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; diff --git a/identity/aidl/default/libeic/EicProvisioning.h b/identity/aidl/default/libeic/EicProvisioning.h index d94f8f18c2..2619bfc45e 100644 --- a/identity/aidl/default/libeic/EicProvisioning.h +++ b/identity/aidl/default/libeic/EicProvisioning.h @@ -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, diff --git a/identity/aidl/default/service.cpp b/identity/aidl/default/service.cpp index 78f4fbc4b6..ed3c4cbcce 100644 --- a/identity/aidl/default/service.cpp +++ b/identity/aidl/default/service.cpp @@ -16,6 +16,7 @@ #define LOG_TAG "android.hardware.identity-service" +#include #include #include #include @@ -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 hwProxyFactory = new FakeSecureHardwareProxyFactory(); + const std::string remotelyProvisionedComponentName = + std::string(IRemotelyProvisionedComponent::descriptor) + "/default"; ABinderProcess_setThreadPoolMaxThreadCount(0); std::shared_ptr store = - ndk::SharedRefBase::make(hwProxyFactory); + ndk::SharedRefBase::make(hwProxyFactory, + remotelyProvisionedComponentName); const std::string instance = std::string() + IdentityCredentialStore::descriptor + "/default"; binder_status_t status = AServiceManager_addService(store->asBinder().get(), instance.c_str()); diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp index 7b6f2c81ee..c5b84a16c5 100644 --- a/identity/aidl/vts/Android.bp +++ b/identity/aidl/vts/Android.bp @@ -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", diff --git a/identity/aidl/vts/Util.cpp b/identity/aidl/vts/Util.cpp index 1148cb0b60..f3d7c30548 100644 --- a/identity/aidl/vts/Util.cpp +++ b/identity/aidl/vts/Util.cpp @@ -20,12 +20,16 @@ #include +#include #include +#include #include #include #include -#include +#include +#include +#include #include 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& writableCredential, @@ -58,6 +65,77 @@ bool setupWritableCredential(sp& writableCredential } } +optional>> 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 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 ecRootKey(EC_KEY_new()); + bssl::UniquePtr rootKey(EVP_PKEY_new()); + if (ecRootKey.get() == nullptr || rootKey.get() == nullptr) { + LOG(ERROR) << "Memory allocation failed"; + return {}; + } + + bssl::UniquePtr 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> extensions; + + // Now make a self-signed cert + optional> 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> 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>{std::move(*intermediate), std::move(*root)}; +} + optional> generateReaderCertificate(string serialDecimal) { vector privKey; return generateReaderCertificate(serialDecimal, &privKey); diff --git a/identity/aidl/vts/Util.h b/identity/aidl/vts/Util.h index 80e52a21da..b120dc9d19 100644 --- a/identity/aidl/vts/Util.h +++ b/identity/aidl/vts/Util.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -97,6 +98,9 @@ struct TestProfile { bool setupWritableCredential(sp& writableCredential, sp& credentialStore, bool testCredential); +optional>> createFakeRemotelyProvisionedCertificateChain( + const ::android::hardware::security::keymint::MacedPublicKey& macedPublicKey); + optional> generateReaderCertificate(string serialDecimal); optional> generateReaderCertificate(string serialDecimal, diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp index bc37020293..94d4c881b8 100644 --- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp +++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include #include @@ -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 { 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 writableCredential; + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, + false /* testCredential */)); + + sp rpc; + result = credentialStore_->getRemotelyProvisionedComponent(&rpc); + ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); + + MacedPublicKey macedPublicKey; + std::vector attestationKey; + result = rpc->generateEcdsaP256KeyPair(/*testMode=*/true, &macedPublicKey, &attestationKey); + ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); + + optional>> remotelyProvisionedCertChain = + test_utils::createFakeRemotelyProvisionedCertificateChain(macedPublicKey); + ASSERT_TRUE(remotelyProvisionedCertChain); + + vector concatenatedCerts; + for (const vector& 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 attestationChallenge(challenge.begin(), challenge.end()); + vector attestationCertificate; + vector 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 rpc; + Status result = credentialStore_->getRemotelyProvisionedComponent(&rpc); + ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); + + MacedPublicKey macedPublicKey; + std::vector attestationKey; + result = rpc->generateEcdsaP256KeyPair(/*testMode=*/true, &macedPublicKey, &attestationKey); + ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); + + optional>> remotelyProvisionedCertChain = + test_utils::createFakeRemotelyProvisionedCertificateChain(macedPublicKey); + ASSERT_TRUE(remotelyProvisionedCertChain); + + vector concatenatedCerts; + for (const vector& cert : *remotelyProvisionedCertChain) { + concatenatedCerts.insert(concatenatedCerts.end(), cert.begin(), cert.end()); + } + + sp 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; diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h index 3b91de6dcc..82746d6da5 100644 --- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h +++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h @@ -17,6 +17,8 @@ #ifndef IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_ #define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_ +#include + #include #include #include @@ -128,6 +130,15 @@ optional, vector>>> createEcKeyPairAnd const vector& challenge, const vector& 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, vector>> createEcKeyPairWithAttestationKey( + const vector& challenge, const vector& applicationId, + const vector& attestationKeyBlob, const vector& attestationKeyCert, + bool isTestCredential); + // (TODO: remove when no longer used by 3rd party.) optional>> createAttestationForEcPublicKey( const vector& publicKey, const vector& challenge, @@ -240,6 +251,13 @@ optional> ecPublicKeyGenerateCertificate( time_t validityNotBefore, time_t validityNotAfter, const map>& extensions); +// Identical behavior to the above version of ecPublicKeyGenerateCertificate, except this +// overload takes OpenSSL key parameters instead of key bitstrings as inputs. +optional> ecPublicKeyGenerateCertificate( + EVP_PKEY* publicKey, EVP_PKEY* signingKey, const string& serialDecimal, + const string& issuer, const string& subject, time_t validityNotBefore, + time_t validityNotAfter, const map>& 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()). diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp index 7f4674d24a..36ecdb04e5 100644 --- a/identity/support/src/IdentityCredentialSupport.cpp +++ b/identity/support/src/IdentityCredentialSupport.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -168,6 +169,286 @@ using ASN1_OBJECT_Ptr = bssl::UniquePtr; using X509_NAME_Ptr = bssl::UniquePtr; using X509_EXTENSION_Ptr = bssl::UniquePtr; +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> 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 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> extractDerSubjectFromCertificate(const vector& 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 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>> signAttestationCertificate( + const ::keymaster::PureSoftKeymasterContext& context, const EVP_PKEY* key, + const vector& applicationId, const vector& challenge, + const vector& attestationKeyBlob, + const vector& 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 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> 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 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>> createAttestation(EVP_PKEY* pkey, + const vector& challenge, + const vector& 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 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& output) { optional> getRandom(size_t numBytes) { vector output; @@ -577,69 +858,30 @@ optional> hmacSha256(const vector& key, const vector, vector>>> createEcKeyPairAndAttestation( + const vector& challenge, const vector& applicationId, + bool isTestCredential) { + EVP_PKEY_Ptr pkey = generateP256Key(); + + optional>> attestationCertChain = + createAttestation(pkey.get(), challenge, applicationId, isTestCredential); + if (!attestationCertChain) { + LOG(ERROR) << "Error create attestation from key and challenge"; + return {}; } - *s += numDigits; - return result; + + optional> 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>> createAttestation( - const EVP_PKEY* key, const vector& applicationId, const vector& challenge, - uint64_t activeTimeMilliSeconds, uint64_t expireTimeMilliSeconds, bool isTestCredential) { +optional, vector>> createEcKeyPairWithAttestationKey( + const vector& challenge, const vector& applicationId, + const vector& attestationKeyBlob, const vector& 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>> 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 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> derIssuerSubject = + extractDerSubjectFromCertificate(attestationKeyCert); + if (!derIssuerSubject) { + LOG(ERROR) << "Error error extracting issuer name from the given certificate chain"; + return std::nullopt; } - vector 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>> 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> attestationCertificate; - for (std::size_t i = 0; i < cert_chain_out.entry_count; i++) { - attestationCertificate.insert( - attestationCertificate.end(), - vector( - cert_chain_out.entries[i].data, - cert_chain_out.entries[i].data + cert_chain_out.entries[i].data_length)); - } - - return attestationCertificate; -} - -optional, vector>>> createEcKeyPairAndAttestation( - const vector& challenge, const vector& 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>> 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> keyPair = derEncodeKeyPair(*pkey); + if (!keyPair) { + return std::nullopt; } - vector 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>> createAttestationForEcPublicKey( @@ -820,12 +959,8 @@ optional>> createAttestationForEcPublicKey( return {}; } - uint64_t nowMs = time(nullptr) * 1000; - uint64_t expireTimeMs = 0; // Set to same as batch certificate - optional>> 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> ecPublicKeyGenerateCertificate( return {}; } + return ecPublicKeyGenerateCertificate(pkey.get(), privPkey.get(), serialDecimal, issuer, + subject, validityNotBefore, validityNotAfter, extensions); +} + +optional> ecPublicKeyGenerateCertificate( + EVP_PKEY* publicKey, EVP_PKEY* signingKey, const string& serialDecimal, + const string& issuer, const string& subject, time_t validityNotBefore, + time_t validityNotAfter, const map>& extensions) { auto x509 = X509_Ptr(X509_new()); if (!x509.get()) { LOG(ERROR) << "Error creating X509 certificate"; @@ -1145,7 +1288,7 @@ optional> 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> 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 {}; } diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp index fb736e3c46..c9ee1b3d27 100644 --- a/security/keymint/aidl/Android.bp +++ b/security/keymint/aidl/Android.bp @@ -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. diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp index 2d2d701588..91db3c8954 100644 --- a/security/keymint/aidl/vts/functional/Android.bp +++ b/security/keymint/aidl/vts/functional/Android.bp @@ -75,6 +75,9 @@ cc_test_library { export_include_dirs: [ ".", ], + export_static_lib_headers: [ + "libkeymint_support", + ], static_libs: [ "libgmock_ndk", ],