diff options
Diffstat (limited to 'identity')
-rw-r--r-- | identity/aidl/vts/VtsAttestationParserSupport.cpp | 187 | ||||
-rw-r--r-- | identity/aidl/vts/VtsAttestationParserSupport.h | 122 | ||||
-rw-r--r-- | identity/aidl/vts/VtsAttestationTests.cpp | 188 |
3 files changed, 497 insertions, 0 deletions
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 <aidl/Gtest.h> +#include <map> + +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<keymaster_cert_chain_t> AttestationCertificateParser::certificateChainToKeymasterChain( + const vector<Certificate>& 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<keymaster_cert_chain_t> 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<uint8_t> 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 <aidl/Gtest.h> +#include <android/hardware/identity/IIdentityCredentialStore.h> +#include <android/hardware/identity/support/IdentityCredentialSupport.h> +#include <android/hardware/keymaster/4.0/types.h> +#include <hardware/keymaster_defs.h> +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/authorization_set.h> +#include <keymaster/contexts/pure_soft_keymaster_context.h> +#include <keymaster/contexts/soft_attestation_cert.h> +#include <keymaster/keymaster_tags.h> +#include <keymaster/km_openssl/attestation_utils.h> +#include <vector> + +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<Certificate>& certChain) + : origCertChain_(certChain) {} + + bool parse(); + + uint32_t getKeymasterVersion(); + uint32_t getAttestationVersion(); + vector<uint8_t> getAttestationChallenge(); + keymaster_security_level_t getKeymasterSecurityLevel(); + keymaster_security_level_t getAttestationSecurityLevel(); + + template <keymaster_tag_t Tag> + bool getSwEnforcedBool(TypedTag<KM_BOOL, Tag> tag) { + if (att_sw_enforced_.GetTagValue(tag)) { + return true; + } + + return false; + } + + template <keymaster_tag_t Tag> + bool getHwEnforcedBool(TypedTag<KM_BOOL, Tag> tag) { + if (att_hw_enforced_.GetTagValue(tag)) { + return true; + } + + return false; + } + + template <keymaster_tag_t Tag> + optional<vector<uint8_t>> getHwEnforcedBlob(TypedTag<KM_BYTES, Tag> tag) { + keymaster_blob_t blob; + if (att_hw_enforced_.GetTagValue(tag, &blob)) { + return {}; + } + + vector<uint8_t> ret(blob.data, blob.data + blob.data_length); + return ret; + } + + template <keymaster_tag_t Tag> + optional<vector<uint8_t>> getSwEnforcedBlob(TypedTag<KM_BYTES, Tag> tag) { + keymaster_blob_t blob; + if (!att_sw_enforced_.GetTagValue(tag, &blob)) { + return {}; + } + + vector<uint8_t> 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<keymaster_cert_chain_t> certificateChainToKeymasterChain( + const vector<Certificate>& certificates); + + // Private variables. + vector<Certificate> 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<uint8_t> 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 <aidl/Gtest.h> +#include <aidl/Vintf.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 "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<std::string> { + public: + virtual void SetUp() override { + credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>( + String16(GetParam().c_str())); + ASSERT_NE(credentialStore_, nullptr); + } + + sp<IIdentityCredentialStore> credentialStore_; +}; + +TEST_P(VtsAttestationTests, verifyAttestationWithEmptyChallengeEmptyId) { + Status result; + + HardwareInformation hwInfo; + ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); + + sp<IWritableIdentityCredential> writableCredential; + ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_)); + + vector<uint8_t> attestationChallenge; + vector<Certificate> attestationCertificate; + vector<uint8_t> 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<IWritableIdentityCredential> writableCredential; + ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_)); + + vector<uint8_t> attestationChallenge; + vector<Certificate> attestationCertificate; + string applicationId = "Attestation Verification"; + vector<uint8_t> 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<IWritableIdentityCredential> writableCredential; + ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_)); + + string challenge = "NotSoRandomChallenge"; + vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end()); + vector<Certificate> attestationCertificate; + vector<uint8_t> 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<IWritableIdentityCredential> writableCredential; + ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_)); + + string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1"; + vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end()); + vector<Certificate> attestationCertificate; + string applicationId = "Attestation Verification"; + vector<uint8_t> 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<IWritableIdentityCredential> writableCredential; + ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_)); + + string challenge = "c"; + vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end()); + vector<Certificate> attestationCertificate; + string applicationId = "i"; + vector<uint8_t> 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 |