Identity Credential: Require passing applicationId when generating attestation.
Since the attestation format includes the applicationId, we need this
to be passed from credstore. Also clarify other requirements about
what needs to be in the attestation data.
Bug: 111446262
Test: atest android.security.identity.cts
Test: VtsHalIdentityCredentialTargetTest
Test: android.hardware.identity-support-lib-test
Change-Id: I623849bd61e55752a573002dc7a97c6658d94c91
diff --git a/identity/1.0/IWritableIdentityCredential.hal b/identity/1.0/IWritableIdentityCredential.hal
index b1ce00d..f26f763 100644
--- a/identity/1.0/IWritableIdentityCredential.hal
+++ b/identity/1.0/IWritableIdentityCredential.hal
@@ -26,20 +26,56 @@
* characteristics to an issuing authority. Must not be called more than once.
*
* The certificate chain must be generated using Keymaster Attestation
- * (see https://source.android.com/security/keystore/attestation) and must also
- * have the Tag::IDENTITY_CREDENTIAL_KEY tag from KeyMaster 4.1 set. This tag indicates
- * that this key is an Identity Credential key (which can only sign/MAC very
- * specific messages) and not an Android Keystore key (which can be used to sign/MAC
- * anything).
+ * (see https://source.android.com/security/keystore/attestation) with the
+ * following additional requirements:
+ *
+ * - The attestationVersion field in the attestation extension must be at least 3.
+ *
+ * - The attestationSecurityLevel field must be set to either Software (0),
+ * TrustedEnvironment (1), or StrongBox (2) depending on how attestation is
+ * implemented. Only the default AOSP implementation of this HAL may use
+ * value 0 (additionally, this implementation must not be used on production
+ * devices).
+ *
+ * - The keymasterVersion field in the attestation extension must be set to (10*major + minor)
+ * where major and minor are the Identity Credential interface major and minor versions.
+ * Specifically for this version of the interface (1.0) this value is 10.
+ *
+ * - The keymasterSecurityLevel field in the attestation extension must be set to
+ * either Software (0), TrustedEnvironment (1), or StrongBox (2) depending on how
+ * the Trusted Application backing the HAL implementation is implemented. Only
+ * the default AOSP implementation of this HAL may use value 0 (additionally, this
+ * implementation must not be used on production devices)
+ *
+ * - The attestationChallenge field must be set to the passed-in challenge.
+ *
+ * - The uniqueId field must be empty.
+ *
+ * - The softwareEnforced field in the attestation extension must include
+ * Tag::ATTESTATION_APPLICATION_ID which must be set to the bytes of the passed-in
+ * attestationApplicationId.
+ *
+ * - The teeEnforced field in the attestation extension must include
+ * Tag::IDENTITY_CREDENTIAL_KEY. This tag indicates that the key is an Identity
+ * Credential key (which can only sign/MAC very specific messages) and not an Android
+ * Keystore key (which can be used to sign/MAC anything).
+ *
+ * Additional authorizations may be needed in the softwareEnforced and teeEnforced
+ * fields - the above is not an exhaustive list.
+ *
+ * @param attestationApplicationId is the DER encoded value to be stored
+ * in Tag::ATTESTATION_APPLICATION_ID. This schema is described in
+ * https://developer.android.com/training/articles/security-key-attestation#certificate_schema_attestationid
*
* @param attestationChallenge a challenge set by the issuer to ensure freshness.
*
* @return result is OK on success, FAILED if an error occurred.
*
- * @return certificate is the X.509 certificate chain for the credentialKey
+ * @return certificateChain is the X.509 certificate chain for the credentialKey
*/
- getAttestationCertificate(vec<uint8_t> attestationChallenge)
- generates(Result result, vec<uint8_t> certificate);
+ getAttestationCertificate(vec<uint8_t> attestationApplicationId,
+ vec<uint8_t> attestationChallenge)
+ generates(Result result, vec<vec<uint8_t>> certificateChain);
/**
* Start the personalization process.
diff --git a/identity/1.0/default/WritableIdentityCredential.cpp b/identity/1.0/default/WritableIdentityCredential.cpp
index 548b4c0..4c39f85 100644
--- a/identity/1.0/default/WritableIdentityCredential.cpp
+++ b/identity/1.0/default/WritableIdentityCredential.cpp
@@ -108,7 +108,12 @@
return true;
}
+// TODO: use |attestationApplicationId| and |attestationChallenge| and also
+// ensure the returned certificate chain satisfy the requirements listed in
+// the docs for IWritableIdentityCredential::getAttestationCertificate()
+//
Return<void> WritableIdentityCredential::getAttestationCertificate(
+ const hidl_vec<uint8_t>& /* attestationApplicationId */,
const hidl_vec<uint8_t>& /* attestationChallenge */,
getAttestationCertificate_cb _hidl_cb) {
// For now, we dynamically generate an attestion key on each and every
@@ -181,7 +186,16 @@
certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
attestationKeyCertificate.value().end());
- _hidl_cb(support::resultOK(), certificateChain);
+ optional<vector<vector<uint8_t>>> splitCertChain =
+ support::certificateChainSplit(certificateChain);
+ if (!splitCertChain) {
+ _hidl_cb(support::result(ResultCode::FAILED, "Error splitting certificate chain"), {});
+ return Void();
+ }
+ hidl_vec<hidl_vec<uint8_t>> ret;
+ ret.resize(splitCertChain.value().size());
+ std::copy(splitCertChain.value().begin(), splitCertChain.value().end(), ret.begin());
+ _hidl_cb(support::resultOK(), ret);
return Void();
}
diff --git a/identity/1.0/default/WritableIdentityCredential.h b/identity/1.0/default/WritableIdentityCredential.h
index 9f4e303..b1deb16 100644
--- a/identity/1.0/default/WritableIdentityCredential.h
+++ b/identity/1.0/default/WritableIdentityCredential.h
@@ -51,7 +51,8 @@
// Methods from ::android::hardware::identity::IWritableIdentityCredential
// follow.
- Return<void> getAttestationCertificate(const hidl_vec<uint8_t>& attestationChallenge,
+ Return<void> getAttestationCertificate(const hidl_vec<uint8_t>& attestationApplicationId,
+ const hidl_vec<uint8_t>& attestationChallenge,
getAttestationCertificate_cb _hidl_cb) override;
Return<void> startPersonalization(uint16_t accessControlProfileCount,
diff --git a/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp b/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp
index 903e912..88b06df 100644
--- a/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp
+++ b/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp
@@ -201,13 +201,18 @@
ASSERT_NE(writableCredential, nullptr);
string challenge = "attestationChallenge";
+ // TODO: set it to something random and check it's in the cert chain
+ vector<uint8_t> attestationApplicationId = {};
vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
vector<uint8_t> attestationCertificate;
writableCredential->getAttestationCertificate(
- attestationChallenge,
- [&](const Result& _result, const hidl_vec<uint8_t>& _attestationCertificate) {
+ attestationApplicationId, attestationChallenge,
+ [&](const Result& _result, const hidl_vec<hidl_vec<uint8_t>>& _splitCertChain) {
result = _result;
- attestationCertificate = _attestationCertificate;
+ vector<vector<uint8_t>> splitCerts;
+ std::copy(_splitCertChain.begin(), _splitCertChain.end(),
+ std::back_inserter(splitCerts));
+ attestationCertificate = support::certificateChainJoin(splitCerts);
});
EXPECT_EQ("", result.message);
ASSERT_EQ(ResultCode::OK, result.code);