From cab019aae56b7f7cd329fe0d99231d29a661c64c Mon Sep 17 00:00:00 2001 From: Selene Huang Date: Wed, 11 Mar 2020 04:37:48 -0700 Subject: Add attestation certificate parsing and validate for IC vts. - Added attestation certificate parsing support. - Added various certificate conversion support. - Added certification verification support. - Added tests for the attestation certificate verification. - Updated the old tests to use the new attestation validation implementation. - Updated GenerateReaderCertificate to use pointer reader private key. Bug: 154909726 Test: VtsHalIdentityTargetTest Test: atest android.security.identity.cts Change-Id: Ibe770e6eaf0b0018d60876926d824204e4eaf732 --- identity/aidl/vts/Android.bp | 6 + identity/aidl/vts/VtsAttestationParserSupport.cpp | 187 ++++++++++++++++++++ identity/aidl/vts/VtsAttestationParserSupport.h | 122 +++++++++++++ identity/aidl/vts/VtsAttestationTests.cpp | 188 +++++++++++++++++++++ identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp | 21 +-- .../vts/VtsIWritableIdentityCredentialTests.cpp | 98 ++++++----- identity/aidl/vts/VtsIdentityTestUtils.cpp | 86 ++++++++-- identity/aidl/vts/VtsIdentityTestUtils.h | 19 ++- 8 files changed, 654 insertions(+), 73 deletions(-) create mode 100644 identity/aidl/vts/VtsAttestationParserSupport.cpp create mode 100644 identity/aidl/vts/VtsAttestationParserSupport.h create mode 100644 identity/aidl/vts/VtsAttestationTests.cpp diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp index e4780bf89c..5b075c6e67 100644 --- a/identity/aidl/vts/Android.bp +++ b/identity/aidl/vts/Android.bp @@ -8,10 +8,16 @@ cc_test { "VtsHalIdentityEndToEndTest.cpp", "VtsIWritableIdentityCredentialTests.cpp", "VtsIdentityTestUtils.cpp", + "VtsAttestationTests.cpp", + "VtsAttestationParserSupport.cpp", ], shared_libs: [ + "android.hardware.keymaster@4.0", "libbinder", "libcrypto", + "libkeymaster_portable", + "libsoft_attestation_cert", + "libpuresoftkeymasterdevice", ], static_libs: [ "libcppbor", diff --git a/identity/aidl/vts/VtsAttestationParserSupport.cpp b/identity/aidl/vts/VtsAttestationParserSupport.cpp new file mode 100644 index 0000000000..71fe7331ba --- /dev/null +++ b/identity/aidl/vts/VtsAttestationParserSupport.cpp @@ -0,0 +1,187 @@ +/* + * Copyright 2019, 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. + */ + +#include "VtsAttestationParserSupport.h" + +#include +#include + +namespace android::hardware::identity::test_utils { + +using std::endl; +using std::map; +using std::optional; +using std::string; +using std::vector; + +using ::android::sp; +using ::android::String16; +using ::android::binder::Status; + +using ::keymaster::ASN1_OBJECT_Ptr; +using ::keymaster::AuthorizationSet; +using ::keymaster::EVP_PKEY_Ptr; +using ::keymaster::kAttestionRecordOid; +using ::keymaster::TAG_ATTESTATION_APPLICATION_ID; +using ::keymaster::TAG_IDENTITY_CREDENTIAL_KEY; +using ::keymaster::TAG_INCLUDE_UNIQUE_ID; +using ::keymaster::TypedTag; +using ::keymaster::X509_Ptr; + +using support::certificateChainSplit; + +optional AttestationCertificateParser::certificateChainToKeymasterChain( + const vector& certificates) { + if (certificates.size() <= 0) { + return {}; + } + + keymaster_cert_chain_t kCert; + kCert.entry_count = certificates.size(); + kCert.entries = (keymaster_blob_t*)malloc(sizeof(keymaster_blob_t) * kCert.entry_count); + + int index = 0; + for (const auto& c : certificates) { + kCert.entries[index].data_length = c.encodedCertificate.size(); + uint8_t* data = (uint8_t*)malloc(c.encodedCertificate.size()); + + memcpy(data, c.encodedCertificate.data(), c.encodedCertificate.size()); + kCert.entries[index].data = (const uint8_t*)data; + index++; + } + + return kCert; +} + +bool AttestationCertificateParser::parse() { + optional cert_chain = certificateChainToKeymasterChain(origCertChain_); + if (!cert_chain) { + return false; + } + + if (cert_chain.value().entry_count < 3) { + return false; + } + + if (!verifyChain(cert_chain.value())) { + return false; + } + + if (!verifyAttestationRecord(cert_chain.value().entries[0])) { + return false; + } + + keymaster_free_cert_chain(&cert_chain.value()); + return true; +} + +ASN1_OCTET_STRING* AttestationCertificateParser::getAttestationRecord(X509* certificate) { + ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1)); + if (!oid.get()) return nullptr; + + int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1); + if (location == -1) return nullptr; + + X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location); + if (!attest_rec_ext) return nullptr; + + ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext); + return attest_rec; +} + +X509* AttestationCertificateParser::parseCertBlob(const keymaster_blob_t& blob) { + const uint8_t* p = blob.data; + return d2i_X509(nullptr, &p, blob.data_length); +} + +bool AttestationCertificateParser::verifyAttestationRecord( + const keymaster_blob_t& attestation_cert) { + X509_Ptr cert(parseCertBlob(attestation_cert)); + if (!cert.get()) { + return false; + } + + ASN1_OCTET_STRING* attest_rec = getAttestationRecord(cert.get()); + if (!attest_rec) { + return false; + } + + keymaster_blob_t att_unique_id = {}; + keymaster_blob_t att_challenge; + keymaster_error_t ret = parse_attestation_record( + attest_rec->data, attest_rec->length, &att_attestation_version_, + &att_attestation_security_level_, &att_keymaster_version_, + &att_keymaster_security_level_, &att_challenge, &att_sw_enforced_, &att_hw_enforced_, + &att_unique_id); + if (ret) { + return false; + } + + att_challenge_.assign(att_challenge.data, att_challenge.data + att_challenge.data_length); + return true; +} + +uint32_t AttestationCertificateParser::getKeymasterVersion() { + return att_keymaster_version_; +} + +uint32_t AttestationCertificateParser::getAttestationVersion() { + return att_attestation_version_; +} + +vector AttestationCertificateParser::getAttestationChallenge() { + return att_challenge_; +} + +keymaster_security_level_t AttestationCertificateParser::getKeymasterSecurityLevel() { + return att_keymaster_security_level_; +} + +keymaster_security_level_t AttestationCertificateParser::getAttestationSecurityLevel() { + return att_attestation_security_level_; +} + +// Verify the Attestation certificates are correctly chained. +bool AttestationCertificateParser::verifyChain(const keymaster_cert_chain_t& chain) { + for (size_t i = 0; i < chain.entry_count - 1; ++i) { + keymaster_blob_t& key_cert_blob = chain.entries[i]; + keymaster_blob_t& signing_cert_blob = chain.entries[i + 1]; + + X509_Ptr key_cert(parseCertBlob(key_cert_blob)); + X509_Ptr signing_cert(parseCertBlob(signing_cert_blob)); + if (!key_cert.get() || !signing_cert.get()) { + return false; + } + + EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get())); + if (!signing_pubkey.get()) return false; + + if (X509_verify(key_cert.get(), signing_pubkey.get()) != 1) { + return false; + } + + if (i + 1 == chain.entry_count - 1) { + // Last entry is self-signed. + if (X509_verify(signing_cert.get(), signing_pubkey.get()) != 1) { + return false; + } + } + } + + return true; +} + +} // namespace android::hardware::identity::test_utils diff --git a/identity/aidl/vts/VtsAttestationParserSupport.h b/identity/aidl/vts/VtsAttestationParserSupport.h new file mode 100644 index 0000000000..7c7e1b6ec1 --- /dev/null +++ b/identity/aidl/vts/VtsAttestationParserSupport.h @@ -0,0 +1,122 @@ + +/* + * Copyright 2019, 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. + */ + +#ifndef VTS_ATTESTATION_PARSER_SUPPORT_H +#define VTS_ATTESTATION_PARSER_SUPPORT_H + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android::hardware::identity::test_utils { + +using ::std::optional; +using ::std::string; +using ::std::vector; + +using ::keymaster::AuthorizationSet; +using ::keymaster::TypedTag; + +class AttestationCertificateParser { + public: + AttestationCertificateParser(const vector& certChain) + : origCertChain_(certChain) {} + + bool parse(); + + uint32_t getKeymasterVersion(); + uint32_t getAttestationVersion(); + vector getAttestationChallenge(); + keymaster_security_level_t getKeymasterSecurityLevel(); + keymaster_security_level_t getAttestationSecurityLevel(); + + template + bool getSwEnforcedBool(TypedTag tag) { + if (att_sw_enforced_.GetTagValue(tag)) { + return true; + } + + return false; + } + + template + bool getHwEnforcedBool(TypedTag tag) { + if (att_hw_enforced_.GetTagValue(tag)) { + return true; + } + + return false; + } + + template + optional> getHwEnforcedBlob(TypedTag tag) { + keymaster_blob_t blob; + if (att_hw_enforced_.GetTagValue(tag, &blob)) { + return {}; + } + + vector ret(blob.data, blob.data + blob.data_length); + return ret; + } + + template + optional> getSwEnforcedBlob(TypedTag tag) { + keymaster_blob_t blob; + if (!att_sw_enforced_.GetTagValue(tag, &blob)) { + return {}; + } + + vector ret(blob.data, blob.data + blob.data_length); + return ret; + } + + private: + // Helper functions. + bool verifyChain(const keymaster_cert_chain_t& chain); + + ASN1_OCTET_STRING* getAttestationRecord(X509* certificate); + + X509* parseCertBlob(const keymaster_blob_t& blob); + + bool verifyAttestationRecord(const keymaster_blob_t& attestation_cert); + + optional certificateChainToKeymasterChain( + const vector& certificates); + + // Private variables. + vector origCertChain_; + AuthorizationSet att_sw_enforced_; + AuthorizationSet att_hw_enforced_; + uint32_t att_attestation_version_; + uint32_t att_keymaster_version_; + keymaster_security_level_t att_attestation_security_level_; + keymaster_security_level_t att_keymaster_security_level_; + vector att_challenge_; +}; + +} // namespace android::hardware::identity::test_utils + +#endif // VTS_ATTESTATION_PARSER_SUPPORT_H diff --git a/identity/aidl/vts/VtsAttestationTests.cpp b/identity/aidl/vts/VtsAttestationTests.cpp new file mode 100644 index 0000000000..00b5893d47 --- /dev/null +++ b/identity/aidl/vts/VtsAttestationTests.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2019 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 "VtsAttestationTests" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VtsAttestationParserSupport.h" +#include "VtsIdentityTestUtils.h" + +namespace android::hardware::identity { + +using std::endl; +using std::map; +using std::optional; +using std::string; +using std::vector; + +using ::android::sp; +using ::android::String16; +using ::android::binder::Status; + +using test_utils::AttestationCertificateParser; +using test_utils::setupWritableCredential; +using test_utils::validateAttestationCertificate; + +// This file verifies the Identity Credential VTS Attestation Certificate +// generated. +class VtsAttestationTests : public testing::TestWithParam { + public: + virtual void SetUp() override { + credentialStore_ = android::waitForDeclaredService( + String16(GetParam().c_str())); + ASSERT_NE(credentialStore_, nullptr); + } + + sp credentialStore_; +}; + +TEST_P(VtsAttestationTests, verifyAttestationWithEmptyChallengeEmptyId) { + Status result; + + HardwareInformation hwInfo; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); + + sp writableCredential; + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); + + vector attestationChallenge; + vector attestationCertificate; + vector attestationApplicationId = {}; + result = writableCredential->getAttestationCertificate( + attestationApplicationId, attestationChallenge, &attestationCertificate); + + ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() + << endl; + + EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge, + attestationApplicationId, hwInfo)); +} + +TEST_P(VtsAttestationTests, verifyAttestationWithEmptyChallengeNonemptyId) { + Status result; + + HardwareInformation hwInfo; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); + + sp writableCredential; + ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_)); + + vector attestationChallenge; + vector attestationCertificate; + string applicationId = "Attestation Verification"; + vector attestationApplicationId = {applicationId.begin(), applicationId.end()}; + + result = writableCredential->getAttestationCertificate( + attestationApplicationId, attestationChallenge, &attestationCertificate); + + ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() + << endl; + EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge, + attestationApplicationId, hwInfo)); +} + +TEST_P(VtsAttestationTests, verifyAttestationWithNonemptyChallengeEmptyId) { + Status result; + + HardwareInformation hwInfo; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); + + sp writableCredential; + ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_)); + + string challenge = "NotSoRandomChallenge"; + vector attestationChallenge(challenge.begin(), challenge.end()); + vector attestationCertificate; + vector attestationApplicationId = {}; + + result = writableCredential->getAttestationCertificate( + attestationApplicationId, attestationChallenge, &attestationCertificate); + + ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() + << endl; + + EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge, + attestationApplicationId, hwInfo)); +} + +TEST_P(VtsAttestationTests, verifyAttestationWithNonemptyChallengeNonemptyId) { + Status result; + + HardwareInformation hwInfo; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); + + sp writableCredential; + ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_)); + + string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1"; + vector attestationChallenge(challenge.begin(), challenge.end()); + vector attestationCertificate; + string applicationId = "Attestation Verification"; + vector attestationApplicationId = {applicationId.begin(), applicationId.end()}; + + result = writableCredential->getAttestationCertificate( + attestationApplicationId, attestationChallenge, &attestationCertificate); + + ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() + << endl; + + EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge, + attestationApplicationId, hwInfo)); +} + +TEST_P(VtsAttestationTests, verifyAttestationWithVeryShortChallengeAndId) { + Status result; + + HardwareInformation hwInfo; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); + + sp writableCredential; + ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_)); + + string challenge = "c"; + vector attestationChallenge(challenge.begin(), challenge.end()); + vector attestationCertificate; + string applicationId = "i"; + vector attestationApplicationId = {applicationId.begin(), applicationId.end()}; + + result = writableCredential->getAttestationCertificate( + attestationApplicationId, attestationChallenge, &attestationCertificate); + + ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() + << endl; + + EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge, + attestationApplicationId, hwInfo)); +} + +INSTANTIATE_TEST_SUITE_P( + Identity, VtsAttestationTests, + testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)), + android::PrintInstanceNameToString); + +} // namespace android::hardware::identity diff --git a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp index 807feabf80..6b40473fa9 100644 --- a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp +++ b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define LOG_TAG "VtsHalIdentityTargetTest" +#define LOG_TAG "VtsHalIdentityEndToEndTest" #include #include @@ -44,6 +44,8 @@ using ::android::binder::Status; using ::android::hardware::keymaster::HardwareAuthToken; +using test_utils::validateAttestationCertificate; + class IdentityAidl : public testing::TestWithParam { public: virtual void SetUp() override { @@ -68,13 +70,13 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) { // part of the request data. vector readerKey; optional> readerCertificate = - test_utils::GenerateReaderCertificate("1234", readerKey); + test_utils::generateReaderCertificate("1234", &readerKey); ASSERT_TRUE(readerCertificate); // Make the portrait image really big (just shy of 256 KiB) to ensure that // the chunking code gets exercised. vector portraitImage; - test_utils::SetImageData(portraitImage); + test_utils::setImageData(portraitImage); // Access control profiles: const vector testProfiles = {// Profile 0 (reader authentication) @@ -100,17 +102,16 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) { string cborPretty; sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); string challenge = "attestationChallenge"; test_utils::AttestationData attData(writableCredential, challenge, {}); ASSERT_TRUE(attData.result.isOk()) << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl; - ASSERT_EQ(binder::Status::EX_NONE, attData.result.exceptionCode()); - ASSERT_EQ(IIdentityCredentialStore::STATUS_OK, attData.result.serviceSpecificErrorCode()); - // TODO: set it to something random and check it's in the cert chain - ASSERT_GE(attData.attestationCertificate.size(), 2); + EXPECT_TRUE(validateAttestationCertificate(attData.attestationCertificate, + attData.attestationChallenge, + attData.attestationApplicationId, hwInfo)); // This is kinda of a hack but we need to give the size of // ProofOfProvisioning that we'll expect to receive. @@ -122,7 +123,7 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) { .isOk()); optional> secureProfiles = - test_utils::AddAccessControlProfiles(writableCredential, testProfiles); + test_utils::addAccessControlProfiles(writableCredential, testProfiles); ASSERT_TRUE(secureProfiles); // Uses TestEntryData* pointer as key and values are the encrypted blobs. This @@ -130,7 +131,7 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) { map>> encryptedBlobs; for (const auto& entry : testEntries) { - ASSERT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize, + ASSERT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize, encryptedBlobs, true)); } diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp index 724aaa1643..8b0c050226 100644 --- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp +++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp @@ -56,8 +56,12 @@ class IdentityCredentialTests : public testing::TestWithParam { TEST_P(IdentityCredentialTests, verifyAttestationWithEmptyChallenge) { Status result; + + HardwareInformation hwInfo; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); + sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); vector attestationChallenge; vector attestationCertificate; @@ -68,13 +72,18 @@ TEST_P(IdentityCredentialTests, verifyAttestationWithEmptyChallenge) { EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() << endl; - EXPECT_TRUE(test_utils::ValidateAttestationCertificate(attestationCertificate)); + EXPECT_TRUE(test_utils::validateAttestationCertificate( + attestationCertificate, attestationChallenge, attestationApplicationId, hwInfo)); } TEST_P(IdentityCredentialTests, verifyAttestationSuccessWithChallenge) { Status result; + + HardwareInformation hwInfo; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); + sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1"; vector attestationChallenge(challenge.begin(), challenge.end()); @@ -87,17 +96,24 @@ TEST_P(IdentityCredentialTests, verifyAttestationSuccessWithChallenge) { EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() << endl; - EXPECT_TRUE(test_utils::ValidateAttestationCertificate(attestationCertificate)); + EXPECT_TRUE(test_utils::validateAttestationCertificate( + attestationCertificate, attestationChallenge, attestationApplicationId, hwInfo)); } TEST_P(IdentityCredentialTests, verifyAttestationDoubleCallFails) { Status result; + + HardwareInformation hwInfo; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); + sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); string challenge = "NotSoRandomChallenge1"; test_utils::AttestationData attData(writableCredential, challenge, {}); - ASSERT_TRUE(test_utils::ValidateAttestationCertificate(attData.attestationCertificate)); + ASSERT_TRUE(test_utils::validateAttestationCertificate( + attData.attestationCertificate, attData.attestationChallenge, + attData.attestationApplicationId, hwInfo)); string challenge2 = "NotSoRandomChallenge2"; test_utils::AttestationData attData2(writableCredential, challenge2, {}); @@ -110,7 +126,7 @@ TEST_P(IdentityCredentialTests, verifyAttestationDoubleCallFails) { TEST_P(IdentityCredentialTests, verifyStartPersonalization) { Status result; sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); // First call should go through const vector entryCounts = {2, 4}; @@ -131,7 +147,7 @@ TEST_P(IdentityCredentialTests, verifyStartPersonalization) { TEST_P(IdentityCredentialTests, verifyStartPersonalizationMin) { Status result; sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); // Verify minimal number of profile count and entry count const vector entryCounts = {1, 1}; @@ -143,7 +159,7 @@ TEST_P(IdentityCredentialTests, verifyStartPersonalizationMin) { TEST_P(IdentityCredentialTests, verifyStartPersonalizationZero) { Status result; sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); const vector entryCounts = {0}; writableCredential->startPersonalization(0, entryCounts); @@ -154,7 +170,7 @@ TEST_P(IdentityCredentialTests, verifyStartPersonalizationZero) { TEST_P(IdentityCredentialTests, verifyStartPersonalizationOne) { Status result; sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); // Verify minimal number of profile count and entry count const vector entryCounts = {1}; @@ -166,7 +182,7 @@ TEST_P(IdentityCredentialTests, verifyStartPersonalizationOne) { TEST_P(IdentityCredentialTests, verifyStartPersonalizationLarge) { Status result; sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); // Verify set a large number of profile count and entry count is ok const vector entryCounts = {3000}; @@ -178,7 +194,7 @@ TEST_P(IdentityCredentialTests, verifyStartPersonalizationLarge) { TEST_P(IdentityCredentialTests, verifyProfileNumberMismatchShouldFail) { Status result; sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); // Enter mismatched entry and profile numbers const vector entryCounts = {5, 6}; @@ -186,7 +202,7 @@ TEST_P(IdentityCredentialTests, verifyProfileNumberMismatchShouldFail) { ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() << endl; - optional> readerCertificate = test_utils::GenerateReaderCertificate("12345"); + optional> readerCertificate = test_utils::generateReaderCertificate("12345"); ASSERT_TRUE(readerCertificate); const vector testProfiles = {// Profile 0 (reader authentication) @@ -196,7 +212,7 @@ TEST_P(IdentityCredentialTests, verifyProfileNumberMismatchShouldFail) { {4, {}, false, 0}}; optional> secureProfiles = - test_utils::AddAccessControlProfiles(writableCredential, testProfiles); + test_utils::addAccessControlProfiles(writableCredential, testProfiles); ASSERT_TRUE(secureProfiles); vector credentialData; @@ -205,7 +221,7 @@ TEST_P(IdentityCredentialTests, verifyProfileNumberMismatchShouldFail) { writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature); // finishAddingEntries should fail because the number of addAccessControlProfile mismatched with - // startPersonalization, and begintest_utils::AddEntry was not called. + // startPersonalization, and begintest_utils::addEntry was not called. EXPECT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() << endl; EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); @@ -215,7 +231,7 @@ TEST_P(IdentityCredentialTests, verifyProfileNumberMismatchShouldFail) { TEST_P(IdentityCredentialTests, verifyDuplicateProfileId) { Status result; sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); const vector entryCounts = {3, 6}; writableCredential->startPersonalization(3, entryCounts); @@ -272,14 +288,14 @@ TEST_P(IdentityCredentialTests, verifyOneProfileAndEntryPass) { ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); string challenge = "NotSoRandomChallenge1"; test_utils::AttestationData attData(writableCredential, challenge, {}); EXPECT_TRUE(attData.result.isOk()) << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl; - optional> readerCertificate1 = test_utils::GenerateReaderCertificate("123456"); + optional> readerCertificate1 = test_utils::generateReaderCertificate("123456"); ASSERT_TRUE(readerCertificate1); const vector entryCounts = {1u}; @@ -293,7 +309,7 @@ TEST_P(IdentityCredentialTests, verifyOneProfileAndEntryPass) { const vector testProfiles = {{1, readerCertificate1.value(), true, 1}}; optional> secureProfiles = - test_utils::AddAccessControlProfiles(writableCredential, testProfiles); + test_utils::addAccessControlProfiles(writableCredential, testProfiles); ASSERT_TRUE(secureProfiles); const vector testEntries1 = { @@ -302,7 +318,7 @@ TEST_P(IdentityCredentialTests, verifyOneProfileAndEntryPass) { map>> encryptedBlobs; for (const auto& entry : testEntries1) { - ASSERT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize, + ASSERT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize, encryptedBlobs, true)); } @@ -359,17 +375,17 @@ TEST_P(IdentityCredentialTests, verifyManyProfilesAndEntriesPass) { ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); string challenge = "NotSoRandomChallenge"; test_utils::AttestationData attData(writableCredential, challenge, {}); EXPECT_TRUE(attData.result.isOk()) << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl; - optional> readerCertificate1 = test_utils::GenerateReaderCertificate("123456"); + optional> readerCertificate1 = test_utils::generateReaderCertificate("123456"); ASSERT_TRUE(readerCertificate1); - optional> readerCertificate2 = test_utils::GenerateReaderCertificate("1256"); + optional> readerCertificate2 = test_utils::generateReaderCertificate("1256"); ASSERT_TRUE(readerCertificate2); const vector testProfiles = { @@ -386,14 +402,14 @@ TEST_P(IdentityCredentialTests, verifyManyProfilesAndEntriesPass) { << endl; optional> secureProfiles = - test_utils::AddAccessControlProfiles(writableCredential, testProfiles); + test_utils::addAccessControlProfiles(writableCredential, testProfiles); ASSERT_TRUE(secureProfiles); vector portraitImage1; - test_utils::SetImageData(portraitImage1); + test_utils::setImageData(portraitImage1); vector portraitImage2; - test_utils::SetImageData(portraitImage2); + test_utils::setImageData(portraitImage2); const vector testEntries1 = { {"Name Space 1", "Last name", string("Turing"), vector{1, 2}}, @@ -411,7 +427,7 @@ TEST_P(IdentityCredentialTests, verifyManyProfilesAndEntriesPass) { map>> encryptedBlobs; for (const auto& entry : testEntries1) { - EXPECT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize, + EXPECT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize, encryptedBlobs, true)); } @@ -518,18 +534,18 @@ TEST_P(IdentityCredentialTests, verifyEmptyNameSpaceMixedWithNonEmptyWorks) { ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); string challenge = "NotSoRandomChallenge"; test_utils::AttestationData attData(writableCredential, challenge, {}); ASSERT_TRUE(attData.result.isOk()) << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl; - optional> readerCertificate1 = test_utils::GenerateReaderCertificate("123456"); + optional> readerCertificate1 = test_utils::generateReaderCertificate("123456"); ASSERT_TRUE(readerCertificate1); optional> readerCertificate2 = - test_utils::GenerateReaderCertificate("123456987987987987987987"); + test_utils::generateReaderCertificate("123456987987987987987987"); ASSERT_TRUE(readerCertificate2); const vector entryCounts = {2u, 2u}; @@ -547,7 +563,7 @@ TEST_P(IdentityCredentialTests, verifyEmptyNameSpaceMixedWithNonEmptyWorks) { {2, {}, false, 0}}; optional> secureProfiles = - test_utils::AddAccessControlProfiles(writableCredential, testProfiles); + test_utils::addAccessControlProfiles(writableCredential, testProfiles); ASSERT_TRUE(secureProfiles); const vector testEntries1 = { @@ -560,7 +576,7 @@ TEST_P(IdentityCredentialTests, verifyEmptyNameSpaceMixedWithNonEmptyWorks) { map>> encryptedBlobs; for (const auto& entry : testEntries1) { - EXPECT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize, + EXPECT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize, encryptedBlobs, true)); } @@ -580,7 +596,7 @@ TEST_P(IdentityCredentialTests, verifyInterleavingEntryNameSpaceOrderingFails) { ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); string challenge = "NotSoRandomChallenge"; test_utils::AttestationData attData(writableCredential, challenge, {}); @@ -596,11 +612,11 @@ TEST_P(IdentityCredentialTests, verifyInterleavingEntryNameSpaceOrderingFails) { ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() << endl; - optional> readerCertificate1 = test_utils::GenerateReaderCertificate("123456"); + optional> readerCertificate1 = test_utils::generateReaderCertificate("123456"); ASSERT_TRUE(readerCertificate1); optional> readerCertificate2 = - test_utils::GenerateReaderCertificate("123456987987987987987987"); + test_utils::generateReaderCertificate("123456987987987987987987"); ASSERT_TRUE(readerCertificate2); const vector testProfiles = {{0, readerCertificate1.value(), false, 0}, @@ -608,7 +624,7 @@ TEST_P(IdentityCredentialTests, verifyInterleavingEntryNameSpaceOrderingFails) { {2, {}, false, 0}}; optional> secureProfiles = - test_utils::AddAccessControlProfiles(writableCredential, testProfiles); + test_utils::addAccessControlProfiles(writableCredential, testProfiles); ASSERT_TRUE(secureProfiles); const vector testEntries1 = { @@ -619,13 +635,13 @@ TEST_P(IdentityCredentialTests, verifyInterleavingEntryNameSpaceOrderingFails) { map>> encryptedBlobs; for (const auto& entry : testEntries1) { - EXPECT_TRUE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize, + EXPECT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize, encryptedBlobs, true)); } const test_utils::TestEntryData testEntry2 = {"Image", "Portrait image", string("asdfs"), vector{0, 1}}; - EXPECT_TRUE(test_utils::AddEntry(writableCredential, testEntry2, hwInfo.dataChunkSize, + EXPECT_TRUE(test_utils::addEntry(writableCredential, testEntry2, hwInfo.dataChunkSize, encryptedBlobs, true)); // We expect this to fail because the namespace is out of order, all "Name Space" @@ -637,7 +653,7 @@ TEST_P(IdentityCredentialTests, verifyInterleavingEntryNameSpaceOrderingFails) { }; for (const auto& entry : testEntries3) { - EXPECT_FALSE(test_utils::AddEntry(writableCredential, entry, hwInfo.dataChunkSize, + EXPECT_FALSE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize, encryptedBlobs, false)); } @@ -646,7 +662,7 @@ TEST_P(IdentityCredentialTests, verifyInterleavingEntryNameSpaceOrderingFails) { result = writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature); - // should fail because test_utils::AddEntry should have failed earlier. + // should fail because test_utils::addEntry should have failed earlier. EXPECT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() << endl; EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); @@ -655,7 +671,7 @@ TEST_P(IdentityCredentialTests, verifyInterleavingEntryNameSpaceOrderingFails) { TEST_P(IdentityCredentialTests, verifyAccessControlProfileIdOutOfRange) { sp writableCredential; - ASSERT_TRUE(test_utils::SetupWritableCredential(writableCredential, credentialStore_)); + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); const vector entryCounts = {1}; Status result = writableCredential->startPersonalization(1, entryCounts); diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/VtsIdentityTestUtils.cpp index 48e47dc735..aaebcbe3c9 100644 --- a/identity/aidl/vts/VtsIdentityTestUtils.cpp +++ b/identity/aidl/vts/VtsIdentityTestUtils.cpp @@ -19,6 +19,8 @@ #include #include +#include "VtsAttestationParserSupport.h" + namespace android::hardware::identity::test_utils { using std::endl; @@ -31,7 +33,7 @@ using ::android::sp; using ::android::String16; using ::android::binder::Status; -bool SetupWritableCredential(sp& writableCredential, +bool setupWritableCredential(sp& writableCredential, sp& credentialStore) { if (credentialStore == nullptr) { return false; @@ -48,13 +50,13 @@ bool SetupWritableCredential(sp& writableCredential } } -optional> GenerateReaderCertificate(string serialDecimal) { +optional> generateReaderCertificate(string serialDecimal) { vector privKey; - return GenerateReaderCertificate(serialDecimal, privKey); + return generateReaderCertificate(serialDecimal, &privKey); } -optional> GenerateReaderCertificate(string serialDecimal, - vector& readerPrivateKey) { +optional> generateReaderCertificate(string serialDecimal, + vector* outReaderPrivateKey) { optional> readerKeyPKCS8 = support::createEcKeyPair(); if (!readerKeyPKCS8) { return {}; @@ -67,7 +69,11 @@ optional> GenerateReaderCertificate(string serialDecimal, return {}; } - readerPrivateKey = readerKey.value(); + if (outReaderPrivateKey == nullptr) { + return {}; + } + + *outReaderPrivateKey = readerKey.value(); string issuer = "Android Open Source Project"; string subject = "Android IdentityCredential VTS Test"; @@ -79,7 +85,7 @@ optional> GenerateReaderCertificate(string serialDecimal, validityNotBefore, validityNotAfter); } -optional> AddAccessControlProfiles( +optional> addAccessControlProfiles( sp& writableCredential, const vector& testProfiles) { Status result; @@ -120,7 +126,7 @@ optional> AddAccessControlProfiles( // Most test expects this function to pass. So we will print out additional // value if failed so more debug data can be provided. -bool AddEntry(sp& writableCredential, const TestEntryData& entry, +bool addEntry(sp& writableCredential, const TestEntryData& entry, int dataChunkSize, map>>& encryptedBlobs, bool expectSuccess) { Status result; @@ -164,18 +170,70 @@ bool AddEntry(sp& writableCredential, const TestEnt return true; } -bool ValidateAttestationCertificate(vector& inputCertificates) { - return (inputCertificates.size() >= 2); - // TODO: add parsing of the certificate and make sure it is genuine. -} - -void SetImageData(vector& image) { +void setImageData(vector& image) { image.resize(256 * 1024 - 10); for (size_t n = 0; n < image.size(); n++) { image[n] = (uint8_t)n; } } +bool validateAttestationCertificate(const vector& inputCertificates, + const vector& expectedChallenge, + const vector& expectedAppId, + const HardwareInformation& hwInfo) { + AttestationCertificateParser certParser_(inputCertificates); + bool ret = certParser_.parse(); + EXPECT_TRUE(ret); + if (!ret) { + return false; + } + + // As per the IC HAL, the version of the Identity + // Credential HAL is 1.0 - and this is encoded as major*10 + minor. This field is used by + // Keymaster which is known to report integers less than or equal to 4 (for KM up to 4.0) + // and integers greater or equal than 41 (for KM starting with 4.1). + // + // Since we won't get to version 4.0 of the IC HAL for a while, let's also check that a KM + // version isn't errornously returned. + EXPECT_LE(10, certParser_.getKeymasterVersion()); + EXPECT_GT(40, certParser_.getKeymasterVersion()); + EXPECT_LE(3, certParser_.getAttestationVersion()); + + // Verify the app id matches to whatever we set it to be. + optional> appId = + certParser_.getSwEnforcedBlob(::keymaster::TAG_ATTESTATION_APPLICATION_ID); + if (appId) { + EXPECT_EQ(expectedAppId.size(), appId.value().size()); + EXPECT_EQ(0, memcmp(expectedAppId.data(), appId.value().data(), expectedAppId.size())); + } else { + // app id not found + EXPECT_EQ(0, expectedAppId.size()); + } + + EXPECT_TRUE(certParser_.getHwEnforcedBool(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY)); + EXPECT_FALSE(certParser_.getHwEnforcedBool(::keymaster::TAG_INCLUDE_UNIQUE_ID)); + + // Verify the challenge always matches in size and data of what is passed + // in. + vector attChallenge = certParser_.getAttestationChallenge(); + EXPECT_EQ(expectedChallenge.size(), attChallenge.size()); + EXPECT_EQ(0, memcmp(expectedChallenge.data(), attChallenge.data(), expectedChallenge.size())); + + // Ensure the attestation conveys that it's implemented in secure hardware (with carve-out + // for the reference implementation which cannot be implemented in secure hardware). + if (hwInfo.credentialStoreName == "Identity Credential Reference Implementation" && + hwInfo.credentialStoreAuthorName == "Google") { + EXPECT_LE(KM_SECURITY_LEVEL_SOFTWARE, certParser_.getKeymasterSecurityLevel()); + EXPECT_LE(KM_SECURITY_LEVEL_SOFTWARE, certParser_.getAttestationSecurityLevel()); + + } else { + // Actual devices should use TrustedEnvironment or StrongBox. + EXPECT_LE(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT, certParser_.getKeymasterSecurityLevel()); + EXPECT_LE(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT, certParser_.getAttestationSecurityLevel()); + } + return true; +} + vector buildRequestNamespaces(const vector entries) { vector ret; RequestNamespace curNs; diff --git a/identity/aidl/vts/VtsIdentityTestUtils.h b/identity/aidl/vts/VtsIdentityTestUtils.h index 9e1f35271d..673b736842 100644 --- a/identity/aidl/vts/VtsIdentityTestUtils.h +++ b/identity/aidl/vts/VtsIdentityTestUtils.h @@ -93,25 +93,28 @@ struct TestProfile { uint64_t timeoutMillis; }; -bool SetupWritableCredential(sp& writableCredential, +bool setupWritableCredential(sp& writableCredential, sp& credentialStore); -optional> GenerateReaderCertificate(string serialDecimal); +optional> generateReaderCertificate(string serialDecimal); -optional> GenerateReaderCertificate(string serialDecimal, - vector& readerPrivateKey); +optional> generateReaderCertificate(string serialDecimal, + vector* outReaderPrivateKey); -optional> AddAccessControlProfiles( +optional> addAccessControlProfiles( sp& writableCredential, const vector& testProfiles); -bool AddEntry(sp& writableCredential, const TestEntryData& entry, +bool addEntry(sp& writableCredential, const TestEntryData& entry, int dataChunkSize, map>>& encryptedBlobs, bool expectSuccess); -bool ValidateAttestationCertificate(vector& inputCertificates); +void setImageData(vector& image); -void SetImageData(vector& image); +bool validateAttestationCertificate(const vector& inputCertificates, + const vector& expectedChallenge, + const vector& expectedAppId, + const HardwareInformation& hwInfo); vector buildRequestNamespaces(const vector entries); -- cgit v1.2.3