Identity Credential changes for Android 12
- Add IIdentityCredential.deleteCredentialWithChallenge()
- Deprecate IIdentityCredential.deleteCredential()
- Add IIdentityCredential.proveOwership()
- Add IIdentityCredential.updateCredential()
- Add ProofOfBinding CBOR to AuthenticationKey X.509 certificate
- Document which API versions new methods/features appeared in.
- Mention need to declare android.hardware.identity_credential system
feature (w/ feature version number) and do this for the default
implementation.
Bug: 170146643
Test: atest VtsHalIdentityTargetTest
Change-Id: Ib47c7caa5f3d6fff6919f019eee44a735dba9cf8
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index 03966de..f1b1bcf 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -4,13 +4,21 @@
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
],
+ cflags: [
+ "-Wno-deprecated-declarations",
+ ],
srcs: [
- "VtsHalIdentityEndToEndTest.cpp",
"VtsIWritableIdentityCredentialTests.cpp",
- "VtsIdentityTestUtils.cpp",
+ "Util.cpp",
"VtsAttestationTests.cpp",
"UserAuthTests.cpp",
"ReaderAuthTests.cpp",
+ "DeleteCredentialTests.cpp",
+ "ProveOwnershipTests.cpp",
+ "UpdateCredentialTests.cpp",
+ "EndToEndTests.cpp",
+ "TestCredentialTests.cpp",
+ "AuthenticationKeyTests.cpp",
],
shared_libs: [
"libbinder",
@@ -22,9 +30,9 @@
"libpuresoftkeymasterdevice",
"android.hardware.keymaster@4.0",
"android.hardware.identity-support-lib",
- "android.hardware.identity-cpp",
- "android.hardware.keymaster-cpp",
- "android.hardware.keymaster-ndk_platform",
+ "android.hardware.identity-unstable-cpp",
+ "android.hardware.keymaster-unstable-cpp",
+ "android.hardware.keymaster-unstable-ndk_platform",
"libkeymaster4support",
"libkeymaster4_1support",
],
diff --git a/identity/aidl/vts/AuthenticationKeyTests.cpp b/identity/aidl/vts/AuthenticationKeyTests.cpp
new file mode 100644
index 0000000..bda3e70
--- /dev/null
+++ b/identity/aidl/vts/AuthenticationKeyTests.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TestCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class AuthenticationKeyTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ string halInstanceName = GetParam();
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(halInstanceName.c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
+ }
+
+ sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
+};
+
+TEST_P(AuthenticationKeyTests, proofOfProvisionInAuthKeyCert) {
+ if (halApiVersion_ < 3) {
+ GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+ }
+
+ string docType = "org.iso.18013-5.2019.mdl";
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_
+ ->createCredential(docType,
+ true, // testCredential
+ &wc)
+ .isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ optional<vector<uint8_t>> optCredentialPubKey =
+ support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+ ASSERT_TRUE(optCredentialPubKey);
+ vector<uint8_t> credentialPubKey;
+ credentialPubKey = optCredentialPubKey.value();
+
+ size_t proofOfProvisioningSize = 112;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ vector<uint8_t> encryptedData;
+ vector<uint8_t> tstrLastName = cppbor::Tstr("Turing").encode();
+ ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Last name", tstrLastName.size()).isOk());
+ ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ vector<uint8_t> credentialData;
+ Status status = wc->finishAddingEntries(&credentialData, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+
+ optional<vector<uint8_t>> proofOfProvisioning =
+ support::coseSignGetPayload(proofOfProvisioningSignature);
+ ASSERT_TRUE(proofOfProvisioning);
+ string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ EXPECT_EQ(
+ "[\n"
+ " 'ProofOfProvisioning',\n"
+ " 'org.iso.18013-5.2019.mdl',\n"
+ " [\n"
+ " {\n"
+ " 'id' : 1,\n"
+ " },\n"
+ " ],\n"
+ " {\n"
+ " 'ns' : [\n"
+ " {\n"
+ " 'name' : 'Last name',\n"
+ " 'value' : 'Turing',\n"
+ " 'accessControlProfiles' : [1, ],\n"
+ " },\n"
+ " ],\n"
+ " },\n"
+ " true,\n"
+ "]",
+ cborPretty);
+ // Make sure it's signed by the CredentialKey in the returned cert chain.
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+ {}, // Additional data
+ credentialPubKey));
+
+ // Now get a credential and have it create AuthenticationKey so we can check
+ // the certificate.
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData, &credential)
+ .isOk());
+ vector<uint8_t> signingKeyBlob;
+ Certificate signingKeyCertificate;
+ ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+ optional<vector<uint8_t>> signingPubKey =
+ support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
+ EXPECT_TRUE(signingPubKey);
+
+ // SHA-256(ProofOfProvisioning) is embedded in CBOR with the following CDDL
+ //
+ // ProofOfBinding = [
+ // "ProofOfBinding",
+ // bstr, // Contains the SHA-256 of ProofOfProvisioning
+ // ]
+ //
+ // Check that.
+ //
+ optional<vector<uint8_t>> proofOfBinding = support::certificateGetExtension(
+ signingKeyCertificate.encodedCertificate, "1.3.6.1.4.1.11129.2.1.26");
+ ASSERT_TRUE(proofOfBinding);
+ auto [item, _, message] = cppbor::parse(proofOfBinding.value());
+ ASSERT_NE(item, nullptr) << message;
+ const cppbor::Array* arrayItem = item->asArray();
+ ASSERT_NE(arrayItem, nullptr);
+ ASSERT_EQ(arrayItem->size(), 2);
+ const cppbor::Tstr* strItem = (*arrayItem)[0]->asTstr();
+ ASSERT_NE(strItem, nullptr);
+ EXPECT_EQ(strItem->value(), "ProofOfBinding");
+ const cppbor::Bstr* popSha256Item = (*arrayItem)[1]->asBstr();
+ ASSERT_NE(popSha256Item, nullptr);
+ EXPECT_EQ(popSha256Item->value(), support::sha256(proofOfProvisioning.value()));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AuthenticationKeyTests);
+INSTANTIATE_TEST_SUITE_P(
+ Identity, AuthenticationKeyTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/DeleteCredentialTests.cpp b/identity/aidl/vts/DeleteCredentialTests.cpp
new file mode 100644
index 0000000..1d30067
--- /dev/null
+++ b/identity/aidl/vts/DeleteCredentialTests.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DeleteCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class DeleteCredentialTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(GetParam().c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
+ }
+
+ void provisionData();
+
+ // Set by provisionData
+ vector<uint8_t> credentialData_;
+ vector<uint8_t> credentialPubKey_;
+
+ sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
+};
+
+void DeleteCredentialTests::provisionData() {
+ string docType = "org.iso.18013-5.2019.mdl";
+ bool testCredential = true;
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ optional<vector<uint8_t>> optCredentialPubKey =
+ support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+ ASSERT_TRUE(optCredentialPubKey);
+ credentialPubKey_ = optCredentialPubKey.value();
+
+ size_t proofOfProvisioningSize = 106;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ ASSERT_TRUE(wc->beginAddEntry({0}, "ns", "Some Data", 1).isOk());
+ vector<uint8_t> encryptedData;
+ ASSERT_TRUE(wc->addEntryValue({9}, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+}
+
+TEST_P(DeleteCredentialTests, Delete) {
+ provisionData();
+
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData_, &credential)
+ .isOk());
+
+ vector<uint8_t> proofOfDeletionSignature;
+ ASSERT_TRUE(credential->deleteCredential(&proofOfDeletionSignature).isOk());
+ optional<vector<uint8_t>> proofOfDeletion =
+ support::coseSignGetPayload(proofOfDeletionSignature);
+ ASSERT_TRUE(proofOfDeletion);
+ string cborPretty = support::cborPrettyPrint(proofOfDeletion.value(), 32, {});
+ EXPECT_EQ("['ProofOfDeletion', 'org.iso.18013-5.2019.mdl', true, ]", cborPretty);
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfDeletionSignature, {}, // Additional data
+ credentialPubKey_));
+}
+
+TEST_P(DeleteCredentialTests, DeleteWithChallenge) {
+ if (halApiVersion_ < 3) {
+ GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+ }
+
+ provisionData();
+
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData_, &credential)
+ .isOk());
+
+ vector<uint8_t> challenge = {65, 66, 67};
+ vector<uint8_t> proofOfDeletionSignature;
+ ASSERT_TRUE(
+ credential->deleteCredentialWithChallenge(challenge, &proofOfDeletionSignature).isOk());
+ optional<vector<uint8_t>> proofOfDeletion =
+ support::coseSignGetPayload(proofOfDeletionSignature);
+ ASSERT_TRUE(proofOfDeletion);
+ string cborPretty = support::cborPrettyPrint(proofOfDeletion.value(), 32, {});
+ EXPECT_EQ("['ProofOfDeletion', 'org.iso.18013-5.2019.mdl', {0x41, 0x42, 0x43}, true, ]",
+ cborPretty);
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfDeletionSignature, {}, // Additional data
+ credentialPubKey_));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DeleteCredentialTests);
+INSTANTIATE_TEST_SUITE_P(
+ Identity, DeleteCredentialTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp b/identity/aidl/vts/EndToEndTests.cpp
similarity index 93%
rename from identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
rename to identity/aidl/vts/EndToEndTests.cpp
index cdecb97..5798b4c 100644
--- a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
+++ b/identity/aidl/vts/EndToEndTests.cpp
@@ -29,7 +29,7 @@
#include <map>
#include <tuple>
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
namespace android::hardware::identity {
@@ -50,18 +50,20 @@
using test_utils::validateAttestationCertificate;
-class IdentityAidl : public testing::TestWithParam<std::string> {
+class EndToEndTests : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
String16(GetParam().c_str()));
ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
}
sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
};
-TEST_P(IdentityAidl, hardwareInformation) {
+TEST_P(EndToEndTests, hardwareInformation) {
HardwareInformation info;
ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
ASSERT_GT(info.credentialStoreName.size(), 0);
@@ -69,20 +71,21 @@
ASSERT_GE(info.dataChunkSize, 256);
}
-tuple<bool, string, vector<uint8_t>, vector<uint8_t>> extractFromTestCredentialData(
- const vector<uint8_t>& credentialData) {
+tuple<bool, string, vector<uint8_t>, vector<uint8_t>, vector<uint8_t>>
+extractFromTestCredentialData(const vector<uint8_t>& credentialData) {
string docType;
vector<uint8_t> storageKey;
vector<uint8_t> credentialPrivKey;
+ vector<uint8_t> sha256Pop;
auto [item, _, message] = cppbor::parse(credentialData);
if (item == nullptr) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Array* arrayItem = item->asArray();
if (arrayItem == nullptr || arrayItem->size() != 3) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
@@ -92,7 +95,7 @@
const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
if (docTypeItem == nullptr || testCredentialItem == nullptr ||
encryptedCredentialKeysItem == nullptr) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
docType = docTypeItem->value();
@@ -103,28 +106,38 @@
optional<vector<uint8_t>> decryptedCredentialKeys =
support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
if (!decryptedCredentialKeys) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
if (dckItem == nullptr) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Array* dckArrayItem = dckItem->asArray();
- if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ if (dckArrayItem == nullptr) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+ }
+ if (dckArrayItem->size() < 2) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
- return make_tuple(false, docType, storageKey, credentialPrivKey);
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
storageKey = storageKeyItem->value();
credentialPrivKey = credentialPrivKeyItem->value();
- return make_tuple(true, docType, storageKey, credentialPrivKey);
+ if (dckArrayItem->size() == 3) {
+ const cppbor::Bstr* sha256PopItem = (*dckArrayItem)[2]->asBstr();
+ if (sha256PopItem == nullptr) {
+ return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+ }
+ sha256Pop = sha256PopItem->value();
+ }
+ return make_tuple(true, docType, storageKey, credentialPrivKey, sha256Pop);
}
-TEST_P(IdentityAidl, createAndRetrieveCredential) {
+TEST_P(EndToEndTests, createAndRetrieveCredential) {
// First, generate a key-pair for the reader since its public key will be
// part of the request data.
vector<uint8_t> readerKey;
@@ -277,8 +290,9 @@
// Extract doctype, storage key, and credentialPrivKey from credentialData... this works
// only because we asked for a test-credential meaning that the HBK is all zeroes.
- auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey] =
+ auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey, exSha256Pop] =
extractFromTestCredentialData(credentialData);
+
ASSERT_TRUE(exSuccess);
ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
// ... check that the public key derived from the private key matches what was
@@ -291,6 +305,13 @@
ASSERT_TRUE(exCredentialPubKey);
ASSERT_EQ(exCredentialPubKey.value(), credentialPubKey.value());
+ // Starting with API version 3 (feature version 202101) we require SHA-256(ProofOfProvisioning)
+ // to be in CredentialKeys (which is stored encrypted in CredentialData). Check
+ // that it's there with the expected value.
+ if (halApiVersion_ >= 3) {
+ ASSERT_EQ(exSha256Pop, support::sha256(proofOfProvisioning.value()));
+ }
+
// Now that the credential has been provisioned, read it back and check the
// correct data is returned.
sp<IIdentityCredential> credential;
@@ -498,13 +519,11 @@
EXPECT_EQ(mac, calculatedMac);
}
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(IdentityAidl);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EndToEndTests);
INSTANTIATE_TEST_SUITE_P(
- Identity, IdentityAidl,
+ Identity, EndToEndTests,
testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
android::PrintInstanceNameToString);
-// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl,
-// testing::Values("android.hardware.identity.IIdentityCredentialStore/default"));
} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/ProveOwnershipTests.cpp b/identity/aidl/vts/ProveOwnershipTests.cpp
new file mode 100644
index 0000000..d1a3d39
--- /dev/null
+++ b/identity/aidl/vts/ProveOwnershipTests.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ProveOwnershipTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class ProveOwnershipTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(GetParam().c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
+ }
+
+ void provisionData();
+
+ // Set by provisionData
+ vector<uint8_t> credentialData_;
+ vector<uint8_t> credentialPubKey_;
+
+ sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
+};
+
+void ProveOwnershipTests::provisionData() {
+ string docType = "org.iso.18013-5.2019.mdl";
+ bool testCredential = true;
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ optional<vector<uint8_t>> optCredentialPubKey =
+ support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+ ASSERT_TRUE(optCredentialPubKey);
+ credentialPubKey_ = optCredentialPubKey.value();
+
+ size_t proofOfProvisioningSize = 106;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ ASSERT_TRUE(wc->beginAddEntry({0}, "ns", "Some Data", 1).isOk());
+ vector<uint8_t> encryptedData;
+ ASSERT_TRUE(wc->addEntryValue({9}, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+}
+
+TEST_P(ProveOwnershipTests, proveOwnership) {
+ if (halApiVersion_ < 3) {
+ GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+ }
+
+ provisionData();
+
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData_, &credential)
+ .isOk());
+
+ vector<uint8_t> challenge = {17, 18};
+ vector<uint8_t> proofOfOwnershipSignature;
+ ASSERT_TRUE(credential->proveOwnership(challenge, &proofOfOwnershipSignature).isOk());
+ optional<vector<uint8_t>> proofOfOwnership =
+ support::coseSignGetPayload(proofOfOwnershipSignature);
+ ASSERT_TRUE(proofOfOwnership);
+ string cborPretty = support::cborPrettyPrint(proofOfOwnership.value(), 32, {});
+ EXPECT_EQ("['ProofOfOwnership', 'org.iso.18013-5.2019.mdl', {0x11, 0x12}, true, ]", cborPretty);
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfOwnershipSignature, {}, // Additional data
+ credentialPubKey_));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ProveOwnershipTests);
+INSTANTIATE_TEST_SUITE_P(
+ Identity, ProveOwnershipTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/ReaderAuthTests.cpp b/identity/aidl/vts/ReaderAuthTests.cpp
index 0a9fdc0..7656c8e 100644
--- a/identity/aidl/vts/ReaderAuthTests.cpp
+++ b/identity/aidl/vts/ReaderAuthTests.cpp
@@ -32,7 +32,7 @@
#include <map>
#include <utility>
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
namespace android::hardware::identity {
@@ -123,9 +123,9 @@
const vector<uint8_t>& signingKey) {
time_t validityNotBefore = 0;
time_t validityNotAfter = 0xffffffff;
- optional<vector<uint8_t>> cert =
- support::ecPublicKeyGenerateCertificate(publicKey, signingKey, "24601", "Issuer",
- "Subject", validityNotBefore, validityNotAfter);
+ optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate(
+ publicKey, signingKey, "24601", "Issuer", "Subject", validityNotBefore,
+ validityNotAfter, {});
return cert.value();
}
diff --git a/identity/aidl/vts/TestCredentialTests.cpp b/identity/aidl/vts/TestCredentialTests.cpp
new file mode 100644
index 0000000..d53de3b
--- /dev/null
+++ b/identity/aidl/vts/TestCredentialTests.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TestCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class TestCredentialTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ string halInstanceName = GetParam();
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(halInstanceName.c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
+ }
+
+ sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
+};
+
+TEST_P(TestCredentialTests, testCredential) {
+ string docType = "org.iso.18013-5.2019.mdl";
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_
+ ->createCredential(docType,
+ true, // testCredential
+ &wc)
+ .isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ optional<vector<uint8_t>> optCredentialPubKey =
+ support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+ ASSERT_TRUE(optCredentialPubKey);
+ vector<uint8_t> credentialPubKey;
+ credentialPubKey = optCredentialPubKey.value();
+
+ size_t proofOfProvisioningSize = 112;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ vector<uint8_t> encryptedData;
+ vector<uint8_t> tstrLastName = cppbor::Tstr("Turing").encode();
+ ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Last name", tstrLastName.size()).isOk());
+ ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ vector<uint8_t> credentialData;
+ Status status = wc->finishAddingEntries(&credentialData, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+
+ optional<vector<uint8_t>> proofOfProvisioning =
+ support::coseSignGetPayload(proofOfProvisioningSignature);
+ ASSERT_TRUE(proofOfProvisioning);
+ string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ EXPECT_EQ(
+ "[\n"
+ " 'ProofOfProvisioning',\n"
+ " 'org.iso.18013-5.2019.mdl',\n"
+ " [\n"
+ " {\n"
+ " 'id' : 1,\n"
+ " },\n"
+ " ],\n"
+ " {\n"
+ " 'ns' : [\n"
+ " {\n"
+ " 'name' : 'Last name',\n"
+ " 'value' : 'Turing',\n"
+ " 'accessControlProfiles' : [1, ],\n"
+ " },\n"
+ " ],\n"
+ " },\n"
+ " true,\n"
+ "]",
+ cborPretty);
+ // Make sure it's signed by the CredentialKey in the returned cert chain.
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+ {}, // Additional data
+ credentialPubKey));
+
+ // Now analyze credentialData..
+ auto [item, _, message] = cppbor::parse(credentialData);
+ ASSERT_NE(item, nullptr);
+ const cppbor::Array* arrayItem = item->asArray();
+ ASSERT_NE(arrayItem, nullptr);
+ ASSERT_EQ(arrayItem->size(), 3);
+ const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+ const cppbor::Bool* testCredentialItem =
+ ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
+ : nullptr);
+ EXPECT_EQ(docTypeItem->value(), docType);
+ EXPECT_EQ(testCredentialItem->value(), true);
+
+ vector<uint8_t> hardwareBoundKey = support::getTestHardwareBoundKey();
+ const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+ const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
+ const vector<uint8_t> docTypeVec(docType.begin(), docType.end());
+ optional<vector<uint8_t>> decryptedCredentialKeys =
+ support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
+ ASSERT_TRUE(decryptedCredentialKeys);
+ auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
+ ASSERT_NE(dckItem, nullptr) << dckMessage;
+ const cppbor::Array* dckArrayItem = dckItem->asArray();
+ ASSERT_NE(dckArrayItem, nullptr);
+ // In HAL API version 1 and 2 this array has two items, in version 3 and later it has three.
+ if (halApiVersion_ < 3) {
+ ASSERT_EQ(dckArrayItem->size(), 2);
+ } else {
+ ASSERT_EQ(dckArrayItem->size(), 3);
+ }
+ const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
+ const vector<uint8_t> storageKey = storageKeyItem->value();
+ // const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
+ // const vector<uint8_t> credentialPrivKey = credentialPrivKeyItem->value();
+
+ // Check storageKey can be used to decrypt |encryptedData| to |tstrLastName|
+ vector<uint8_t> additionalData = cppbor::Map()
+ .add("Namespace", "ns")
+ .add("Name", "Last name")
+ .add("AccessControlProfileIds", cppbor::Array().add(1))
+ .encode();
+ optional<vector<uint8_t>> decryptedDataItemValue =
+ support::decryptAes128Gcm(storageKey, encryptedData, additionalData);
+ ASSERT_TRUE(decryptedDataItemValue);
+ EXPECT_EQ(decryptedDataItemValue.value(), tstrLastName);
+
+ // Check that SHA-256(ProofOfProvisioning) matches (only in HAL API version 3)
+ if (halApiVersion_ >= 3) {
+ const cppbor::Bstr* popSha256Item = (*dckArrayItem)[2]->asBstr();
+ const vector<uint8_t> popSha256 = popSha256Item->value();
+ ASSERT_EQ(popSha256, support::sha256(proofOfProvisioning.value()));
+ }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestCredentialTests);
+INSTANTIATE_TEST_SUITE_P(
+ Identity, TestCredentialTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/UpdateCredentialTests.cpp b/identity/aidl/vts/UpdateCredentialTests.cpp
new file mode 100644
index 0000000..9c5ca55
--- /dev/null
+++ b/identity/aidl/vts/UpdateCredentialTests.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "UpdateCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class UpdateCredentialTests : public testing::TestWithParam<string> {
+ public:
+ virtual void SetUp() override {
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(GetParam().c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ halApiVersion_ = credentialStore_->getInterfaceVersion();
+ }
+
+ void provisionData();
+
+ // Set by provisionData
+ vector<uint8_t> credentialData_;
+ vector<uint8_t> credentialPubKey_;
+
+ sp<IIdentityCredentialStore> credentialStore_;
+ int halApiVersion_;
+};
+
+void UpdateCredentialTests::provisionData() {
+ string docType = "org.iso.18013-5.2019.mdl";
+ bool testCredential = true;
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain)
+ .isOk());
+
+ optional<vector<uint8_t>> optCredentialPubKey =
+ support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+ ASSERT_TRUE(optCredentialPubKey);
+ credentialPubKey_ = optCredentialPubKey.value();
+
+ size_t proofOfProvisioningSize = 112;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ vector<uint8_t> encryptedData;
+ vector<uint8_t> tstrLastName = cppbor::Tstr("Prince").encode();
+ ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Last name", tstrLastName.size()).isOk());
+ ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+
+ optional<vector<uint8_t>> proofOfProvisioning =
+ support::coseSignGetPayload(proofOfProvisioningSignature);
+ ASSERT_TRUE(proofOfProvisioning);
+ string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ EXPECT_EQ(
+ "[\n"
+ " 'ProofOfProvisioning',\n"
+ " 'org.iso.18013-5.2019.mdl',\n"
+ " [\n"
+ " {\n"
+ " 'id' : 1,\n"
+ " },\n"
+ " ],\n"
+ " {\n"
+ " 'ns' : [\n"
+ " {\n"
+ " 'name' : 'Last name',\n"
+ " 'value' : 'Prince',\n"
+ " 'accessControlProfiles' : [1, ],\n"
+ " },\n"
+ " ],\n"
+ " },\n"
+ " true,\n"
+ "]",
+ cborPretty);
+ // Make sure it's signed by the CredentialKey in the returned cert chain.
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+ {}, // Additional data
+ credentialPubKey_));
+}
+
+TEST_P(UpdateCredentialTests, updateCredential) {
+ if (halApiVersion_ < 3) {
+ GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+ }
+
+ provisionData();
+
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData_, &credential)
+ .isOk());
+
+ sp<IWritableIdentityCredential> wc;
+ ASSERT_TRUE(credential->updateCredential(&wc).isOk());
+
+ // Getting an attestation cert should fail (because it's an update).
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge = {1};
+ vector<Certificate> certChain;
+ Status result = wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &certChain);
+ ASSERT_FALSE(result.isOk());
+ EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
+ EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
+
+ // Now provision some new data...
+ //
+ size_t proofOfProvisioningSize = 117;
+ // Not in v1 HAL, may fail
+ wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+ ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+ {1} /* numDataElementsPerNamespace */)
+ .isOk());
+
+ // Access control profile 0: open access - don't care about the returned SACP
+ SecureAccessControlProfile sacp;
+ ASSERT_TRUE(wc->addAccessControlProfile(2, {}, false, 0, 0, &sacp).isOk());
+
+ // Single entry - don't care about the returned encrypted data
+ vector<uint8_t> encryptedData;
+ vector<uint8_t> tstrLastName = cppbor::Tstr("T.A.F.K.A.P").encode();
+ ASSERT_TRUE(wc->beginAddEntry({2}, "ns", "Last name", tstrLastName.size()).isOk());
+ ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+ vector<uint8_t> proofOfProvisioningSignature;
+ Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+ EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+ optional<vector<uint8_t>> proofOfProvisioning =
+ support::coseSignGetPayload(proofOfProvisioningSignature);
+ ASSERT_TRUE(proofOfProvisioning);
+ string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {});
+ EXPECT_EQ(
+ "[\n"
+ " 'ProofOfProvisioning',\n"
+ " 'org.iso.18013-5.2019.mdl',\n"
+ " [\n"
+ " {\n"
+ " 'id' : 2,\n"
+ " },\n"
+ " ],\n"
+ " {\n"
+ " 'ns' : [\n"
+ " {\n"
+ " 'name' : 'Last name',\n"
+ " 'value' : 'T.A.F.K.A.P',\n"
+ " 'accessControlProfiles' : [2, ],\n"
+ " },\n"
+ " ],\n"
+ " },\n"
+ " true,\n"
+ "]",
+ cborPretty);
+ // Make sure it's signed by the same CredentialKey we originally provisioned with.
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+ {}, // Additional data
+ credentialPubKey_));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UpdateCredentialTests);
+INSTANTIATE_TEST_SUITE_P(
+ Identity, UpdateCredentialTests,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+
+} // namespace android::hardware::identity
diff --git a/identity/aidl/vts/UserAuthTests.cpp b/identity/aidl/vts/UserAuthTests.cpp
index 327493c..ef89d1c 100644
--- a/identity/aidl/vts/UserAuthTests.cpp
+++ b/identity/aidl/vts/UserAuthTests.cpp
@@ -32,7 +32,7 @@
#include <map>
#include <utility>
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
namespace android::hardware::identity {
@@ -145,7 +145,7 @@
EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
}
-// From ReaderAuthTest.cpp - TODO: consolidate with VtsIdentityTestUtils.h
+// From ReaderAuthTest.cpp - TODO: consolidate with Util.h
pair<vector<uint8_t>, vector<uint8_t>> generateReaderKey();
vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey,
const vector<uint8_t>& signingKey);
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/Util.cpp
similarity index 98%
rename from identity/aidl/vts/VtsIdentityTestUtils.cpp
rename to identity/aidl/vts/Util.cpp
index 3b10651..1148cb0 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.cpp
+++ b/identity/aidl/vts/Util.cpp
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-#define LOG_TAG "VtsIdentityTestUtils"
+#define LOG_TAG "Util"
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
+
+#include <android-base/logging.h>
#include <aidl/Gtest.h>
-#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <keymaster/km_openssl/openssl_utils.h>
#include <keymasterV4_1/attestation_record.h>
#include <charconv>
+
#include <map>
namespace android::hardware::identity::test_utils {
@@ -35,6 +38,7 @@
using ::android::sp;
using ::android::String16;
+using ::android::base::StringPrintf;
using ::android::binder::Status;
using ::keymaster::X509_Ptr;
@@ -86,7 +90,7 @@
return support::ecPublicKeyGenerateCertificate(readerPublicKey.value(), readerKey.value(),
serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
+ validityNotBefore, validityNotAfter, {});
}
optional<vector<SecureAccessControlProfile>> addAccessControlProfiles(
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.h b/identity/aidl/vts/Util.h
similarity index 99%
rename from identity/aidl/vts/VtsIdentityTestUtils.h
rename to identity/aidl/vts/Util.h
index 85c24f8..80e52a2 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.h
+++ b/identity/aidl/vts/Util.h
@@ -21,6 +21,7 @@
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
#include <cppbor.h>
#include <cppbor_parse.h>
+#include <gtest/gtest.h>
namespace android::hardware::identity::test_utils {
diff --git a/identity/aidl/vts/VtsAttestationTests.cpp b/identity/aidl/vts/VtsAttestationTests.cpp
index 5529853..e12fe05 100644
--- a/identity/aidl/vts/VtsAttestationTests.cpp
+++ b/identity/aidl/vts/VtsAttestationTests.cpp
@@ -29,7 +29,7 @@
#include <future>
#include <map>
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
namespace android::hardware::identity {
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index 1629a0c..cc63c48 100644
--- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -29,7 +29,7 @@
#include <future>
#include <map>
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
namespace android::hardware::identity {