| /* |
| * 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 "VtsIWritableIdentityCredentialTests" |
| |
| #include <aidl/Gtest.h> |
| #include <aidl/Vintf.h> |
| #include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h> |
| #include <aidl/android/hardware/security/keymint/MacedPublicKey.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 "Util.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 ::android::hardware::security::keymint::IRemotelyProvisionedComponent; |
| using ::android::hardware::security::keymint::MacedPublicKey; |
| |
| class IdentityCredentialTests : public testing::TestWithParam<string> { |
| public: |
| virtual void SetUp() override { |
| credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>( |
| String16(GetParam().c_str())); |
| ASSERT_NE(credentialStore_, nullptr); |
| } |
| |
| sp<IIdentityCredentialStore> credentialStore_; |
| }; |
| |
| TEST_P(IdentityCredentialTests, verifyAttestationWithEmptyChallenge) { |
| Status result; |
| |
| HardwareInformation hwInfo; |
| ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); |
| |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| vector<uint8_t> attestationChallenge; |
| vector<Certificate> attestationCertificate; |
| vector<uint8_t> attestationApplicationId = {}; |
| result = writableCredential->getAttestationCertificate( |
| attestationApplicationId, attestationChallenge, &attestationCertificate); |
| |
| EXPECT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); |
| EXPECT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode()); |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyAttestationSuccessWithChallenge) { |
| Status result; |
| |
| HardwareInformation hwInfo; |
| ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); |
| |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1"; |
| vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end()); |
| vector<Certificate> attestationCertificate; |
| vector<uint8_t> attestationApplicationId = {1}; |
| |
| result = writableCredential->getAttestationCertificate( |
| attestationApplicationId, attestationChallenge, &attestationCertificate); |
| |
| EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| |
| test_utils::validateAttestationCertificate(attestationCertificate, attestationChallenge, |
| attestationApplicationId, false); |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyAttestationSuccessWithRemoteProvisioning) { |
| HardwareInformation hwInfo; |
| ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); |
| |
| if (!hwInfo.isRemoteKeyProvisioningSupported) { |
| GTEST_SKIP() << "Remote provisioning is not supported"; |
| } |
| |
| Status result; |
| |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| sp<IRemotelyProvisionedComponent> rpc; |
| result = credentialStore_->getRemotelyProvisionedComponent(&rpc); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); |
| |
| MacedPublicKey macedPublicKey; |
| std::vector<uint8_t> attestationKey; |
| // Start by RPC version 3, we don't support testMode=true. So just verify testMode=false here. |
| result = rpc->generateEcdsaP256KeyPair(/*testMode=*/false, &macedPublicKey, &attestationKey); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); |
| |
| optional<vector<vector<uint8_t>>> remotelyProvisionedCertChain = |
| test_utils::createFakeRemotelyProvisionedCertificateChain(macedPublicKey); |
| ASSERT_TRUE(remotelyProvisionedCertChain); |
| |
| vector<uint8_t> concatenatedCerts; |
| for (const vector<uint8_t>& cert : *remotelyProvisionedCertChain) { |
| concatenatedCerts.insert(concatenatedCerts.end(), cert.begin(), cert.end()); |
| } |
| result = writableCredential->setRemotelyProvisionedAttestationKey(attestationKey, |
| concatenatedCerts); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); |
| |
| string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1"; |
| vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end()); |
| vector<Certificate> attestationCertificate; |
| vector<uint8_t> attestationApplicationId = {1}; |
| |
| result = writableCredential->getAttestationCertificate( |
| attestationApplicationId, attestationChallenge, &attestationCertificate); |
| |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); |
| |
| test_utils::validateAttestationCertificate(attestationCertificate, attestationChallenge, |
| attestationApplicationId, false); |
| |
| ASSERT_EQ(remotelyProvisionedCertChain->size() + 1, attestationCertificate.size()); |
| for (size_t i = 0; i < remotelyProvisionedCertChain->size(); ++i) { |
| ASSERT_EQ(remotelyProvisionedCertChain->at(i), |
| attestationCertificate[i + 1].encodedCertificate) |
| << "Certificate mismatch (cert index " << i + 1 << " out of " |
| << attestationCertificate.size() << " total certs)"; |
| } |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyRemotelyProvisionedKeyMayOnlyBeSetOnce) { |
| HardwareInformation hwInfo; |
| ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); |
| |
| if (!hwInfo.isRemoteKeyProvisioningSupported) { |
| GTEST_SKIP() << "Remote provisioning is not supported"; |
| } |
| |
| sp<IRemotelyProvisionedComponent> rpc; |
| Status result = credentialStore_->getRemotelyProvisionedComponent(&rpc); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); |
| |
| MacedPublicKey macedPublicKey; |
| std::vector<uint8_t> attestationKey; |
| // Start by RPC version 3, we don't support testMode=true. So just verify testMode=false here. |
| result = rpc->generateEcdsaP256KeyPair(/*testMode=*/false, &macedPublicKey, &attestationKey); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); |
| |
| optional<vector<vector<uint8_t>>> remotelyProvisionedCertChain = |
| test_utils::createFakeRemotelyProvisionedCertificateChain(macedPublicKey); |
| ASSERT_TRUE(remotelyProvisionedCertChain); |
| |
| vector<uint8_t> concatenatedCerts; |
| for (const vector<uint8_t>& cert : *remotelyProvisionedCertChain) { |
| concatenatedCerts.insert(concatenatedCerts.end(), cert.begin(), cert.end()); |
| } |
| |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| /*testCredential=*/false)); |
| |
| result = writableCredential->setRemotelyProvisionedAttestationKey(attestationKey, |
| concatenatedCerts); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); |
| |
| // Now try again, and verify that the implementation rejects it. |
| result = writableCredential->setRemotelyProvisionedAttestationKey(attestationKey, |
| concatenatedCerts); |
| EXPECT_FALSE(result.isOk()); |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyAttestationDoubleCallFails) { |
| Status result; |
| |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| string challenge = "NotSoRandomChallenge1"; |
| test_utils::AttestationData attData(writableCredential, challenge, |
| {1} /* atteestationApplicationId */); |
| test_utils::validateAttestationCertificate(attData.attestationCertificate, |
| attData.attestationChallenge, |
| attData.attestationApplicationId, false); |
| |
| string challenge2 = "NotSoRandomChallenge2"; |
| test_utils::AttestationData attData2(writableCredential, challenge2, |
| {} /* atteestationApplicationId */); |
| EXPECT_FALSE(attData2.result.isOk()) << attData2.result.exceptionCode() << "; " |
| << attData2.result.exceptionMessage() << endl; |
| EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, attData2.result.exceptionCode()); |
| EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, attData2.result.serviceSpecificErrorCode()); |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyStartPersonalization) { |
| Status result; |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| // First call should go through |
| const vector<int32_t> entryCounts = {2, 4}; |
| writableCredential->setExpectedProofOfProvisioningSize(123456); |
| result = writableCredential->startPersonalization(5, entryCounts); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| |
| // Call personalization again to check if repeat call is allowed. |
| result = writableCredential->startPersonalization(7, entryCounts); |
| |
| // Second call to startPersonalization should have failed. |
| EXPECT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); |
| EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode()); |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyStartPersonalizationMin) { |
| Status result; |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| // Verify minimal number of profile count and entry count |
| const vector<int32_t> entryCounts = {1, 1}; |
| writableCredential->setExpectedProofOfProvisioningSize(123456); |
| result = writableCredential->startPersonalization(1, entryCounts); |
| EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyStartPersonalizationOne) { |
| Status result; |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| // Verify minimal number of profile count and entry count |
| const vector<int32_t> entryCounts = {1}; |
| writableCredential->setExpectedProofOfProvisioningSize(123456); |
| result = writableCredential->startPersonalization(1, entryCounts); |
| EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyStartPersonalizationLarge) { |
| Status result; |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| // Verify set a large number of profile count and entry count is ok |
| const vector<int32_t> entryCounts = {255}; |
| writableCredential->setExpectedProofOfProvisioningSize(123456); |
| result = writableCredential->startPersonalization(25, entryCounts); |
| EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyProfileNumberMismatchShouldFail) { |
| Status result; |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| // Enter mismatched entry and profile numbers |
| const vector<int32_t> entryCounts = {5, 6}; |
| writableCredential->setExpectedProofOfProvisioningSize(123456); |
| result = writableCredential->startPersonalization(5, entryCounts); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| |
| optional<vector<uint8_t>> readerCertificate = test_utils::generateReaderCertificate("12345"); |
| ASSERT_TRUE(readerCertificate); |
| |
| const vector<test_utils::TestProfile> testProfiles = {// Profile 0 (reader authentication) |
| {1, readerCertificate.value(), false, 0}, |
| {2, readerCertificate.value(), true, 1}, |
| // Profile 4 (no authentication) |
| {4, {}, false, 0}}; |
| |
| optional<vector<SecureAccessControlProfile>> secureProfiles = |
| test_utils::addAccessControlProfiles(writableCredential, testProfiles); |
| ASSERT_TRUE(secureProfiles); |
| |
| vector<uint8_t> credentialData; |
| vector<uint8_t> proofOfProvisioningSignature; |
| result = |
| writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature); |
| |
| // finishAddingEntries should fail because the number of addAccessControlProfile mismatched with |
| // 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()); |
| EXPECT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode()); |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyDuplicateProfileId) { |
| Status result; |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| const vector<int32_t> entryCounts = {3, 6}; |
| writableCredential->setExpectedProofOfProvisioningSize(123456); |
| result = writableCredential->startPersonalization(3, entryCounts); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| |
| const vector<test_utils::TestProfile> testProfiles = {// first profile should go though |
| {1, {}, true, 2}, |
| // same id, different |
| // authentication requirement |
| {1, {}, true, 1}, |
| // same id, different certificate |
| {1, {}, false, 0}}; |
| |
| bool expectOk = true; |
| for (const auto& testProfile : testProfiles) { |
| SecureAccessControlProfile profile; |
| Certificate cert; |
| cert.encodedCertificate = testProfile.readerCertificate; |
| int64_t secureUserId = testProfile.userAuthenticationRequired ? 66 : 0; |
| result = writableCredential->addAccessControlProfile( |
| testProfile.id, cert, testProfile.userAuthenticationRequired, |
| testProfile.timeoutMillis, secureUserId, &profile); |
| |
| if (expectOk) { |
| expectOk = false; |
| // for profile should be allowed though as there are no duplications |
| // yet. |
| ASSERT_TRUE(result.isOk()) |
| << result.exceptionCode() << "; " << result.exceptionMessage() |
| << "test profile id = " << testProfile.id << endl; |
| |
| ASSERT_EQ(testProfile.id, profile.id); |
| ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate); |
| ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired); |
| ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis); |
| ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size()); |
| } else { |
| // should not allow duplicate id profiles. |
| ASSERT_FALSE(result.isOk()) |
| << result.exceptionCode() << "; " << result.exceptionMessage() |
| << ". Test profile id = " << testProfile.id |
| << ", timeout=" << testProfile.timeoutMillis << endl; |
| ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); |
| ASSERT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, |
| result.serviceSpecificErrorCode()); |
| } |
| } |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyOneProfileAndEntryPass) { |
| Status result; |
| |
| HardwareInformation hwInfo; |
| ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); |
| |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| string challenge = "NotSoRandomChallenge1"; |
| test_utils::AttestationData attData(writableCredential, challenge, |
| {} /* atteestationApplicationId */); |
| EXPECT_TRUE(attData.result.isOk()) |
| << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl; |
| |
| optional<vector<uint8_t>> readerCertificate1 = test_utils::generateReaderCertificate("123456"); |
| ASSERT_TRUE(readerCertificate1); |
| |
| const vector<int32_t> entryCounts = {1u}; |
| size_t expectedPoPSize = 185 + readerCertificate1.value().size(); |
| // OK to fail, not available in v1 HAL |
| writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize); |
| result = writableCredential->startPersonalization(1, entryCounts); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| |
| const vector<test_utils::TestProfile> testProfiles = {{1, readerCertificate1.value(), true, 1}}; |
| |
| optional<vector<SecureAccessControlProfile>> secureProfiles = |
| test_utils::addAccessControlProfiles(writableCredential, testProfiles); |
| ASSERT_TRUE(secureProfiles); |
| |
| const vector<test_utils::TestEntryData> testEntries1 = { |
| {"Name Space", "Last name", string("Turing"), vector<int32_t>{1}}, |
| }; |
| |
| map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs; |
| for (const auto& entry : testEntries1) { |
| ASSERT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize, |
| encryptedBlobs, true)); |
| } |
| |
| vector<uint8_t> credentialData; |
| vector<uint8_t> proofOfProvisioningSignature; |
| result = |
| writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature); |
| |
| EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| |
| optional<vector<uint8_t>> proofOfProvisioning = |
| support::coseSignGetPayload(proofOfProvisioningSignature); |
| ASSERT_TRUE(proofOfProvisioning); |
| string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"}); |
| EXPECT_EQ( |
| "[\n" |
| " 'ProofOfProvisioning',\n" |
| " 'org.iso.18013-5.2019.mdl',\n" |
| " [\n" |
| " {\n" |
| " 'id' : 1,\n" |
| " 'readerCertificate' : <not printed>,\n" |
| " 'userAuthenticationRequired' : true,\n" |
| " 'timeoutMillis' : 1,\n" |
| " },\n" |
| " ],\n" |
| " {\n" |
| " 'Name Space' : [\n" |
| " {\n" |
| " 'name' : 'Last name',\n" |
| " 'value' : 'Turing',\n" |
| " 'accessControlProfiles' : [1, ],\n" |
| " },\n" |
| " ],\n" |
| " },\n" |
| " false,\n" |
| "]", |
| cborPretty); |
| |
| optional<vector<uint8_t>> credentialPubKey = support::certificateChainGetTopMostKey( |
| attData.attestationCertificate[0].encodedCertificate); |
| ASSERT_TRUE(credentialPubKey); |
| EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature, |
| {}, // Additional data |
| credentialPubKey.value())); |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyManyProfilesAndEntriesPass) { |
| Status result; |
| |
| HardwareInformation hwInfo; |
| ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); |
| |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| string challenge = "NotSoRandomChallenge"; |
| test_utils::AttestationData attData(writableCredential, challenge, |
| {} /* atteestationApplicationId */); |
| EXPECT_TRUE(attData.result.isOk()) |
| << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl; |
| |
| optional<vector<uint8_t>> readerCertificate1 = test_utils::generateReaderCertificate("123456"); |
| ASSERT_TRUE(readerCertificate1); |
| |
| optional<vector<uint8_t>> readerCertificate2 = test_utils::generateReaderCertificate("1256"); |
| ASSERT_TRUE(readerCertificate2); |
| |
| const vector<test_utils::TestProfile> testProfiles = { |
| {1, readerCertificate1.value(), true, 1}, |
| {2, readerCertificate2.value(), true, 2}, |
| }; |
| const vector<int32_t> entryCounts = {1u, 3u, 1u, 1u, 2u}; |
| size_t expectedPoPSize = |
| 525021 + readerCertificate1.value().size() + readerCertificate2.value().size(); |
| // OK to fail, not available in v1 HAL |
| writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize); |
| result = writableCredential->startPersonalization(testProfiles.size(), entryCounts); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| |
| optional<vector<SecureAccessControlProfile>> secureProfiles = |
| test_utils::addAccessControlProfiles(writableCredential, testProfiles); |
| ASSERT_TRUE(secureProfiles); |
| |
| vector<uint8_t> portraitImage1; |
| test_utils::setImageData(portraitImage1); |
| |
| vector<uint8_t> portraitImage2; |
| test_utils::setImageData(portraitImage2); |
| |
| const vector<test_utils::TestEntryData> testEntries1 = { |
| {"Name Space 1", "Last name", string("Turing"), vector<int32_t>{1, 2}}, |
| {"Name Space2", "Home address", string("Maida Vale, London, England"), |
| vector<int32_t>{1}}, |
| {"Name Space2", "Work address", string("Maida Vale2, London, England"), |
| vector<int32_t>{2}}, |
| {"Name Space2", "Trailer address", string("Maida, London, England"), |
| vector<int32_t>{1}}, |
| {"Image", "Portrait image", portraitImage1, vector<int32_t>{1}}, |
| {"Image2", "Work image", portraitImage2, vector<int32_t>{1, 2}}, |
| {"Name Space3", "xyzw", string("random stuff"), vector<int32_t>{1, 2}}, |
| {"Name Space3", "Something", string("Some string"), vector<int32_t>{2}}, |
| }; |
| |
| map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs; |
| for (const auto& entry : testEntries1) { |
| EXPECT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize, |
| encryptedBlobs, true)); |
| } |
| |
| vector<uint8_t> credentialData; |
| vector<uint8_t> proofOfProvisioningSignature; |
| result = |
| writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature); |
| |
| EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| |
| optional<vector<uint8_t>> proofOfProvisioning = |
| support::coseSignGetPayload(proofOfProvisioningSignature); |
| ASSERT_TRUE(proofOfProvisioning); |
| string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), |
| 32, // |
| {"readerCertificate"}); |
| EXPECT_EQ( |
| "[\n" |
| " 'ProofOfProvisioning',\n" |
| " 'org.iso.18013-5.2019.mdl',\n" |
| " [\n" |
| " {\n" |
| " 'id' : 1,\n" |
| " 'readerCertificate' : <not printed>,\n" |
| " 'userAuthenticationRequired' : true,\n" |
| " 'timeoutMillis' : 1,\n" |
| " },\n" |
| " {\n" |
| " 'id' : 2,\n" |
| " 'readerCertificate' : <not printed>,\n" |
| " 'userAuthenticationRequired' : true,\n" |
| " 'timeoutMillis' : 2,\n" |
| " },\n" |
| " ],\n" |
| " {\n" |
| " 'Name Space 1' : [\n" |
| " {\n" |
| " 'name' : 'Last name',\n" |
| " 'value' : 'Turing',\n" |
| " 'accessControlProfiles' : [1, 2, ],\n" |
| " },\n" |
| " ],\n" |
| " 'Name Space2' : [\n" |
| " {\n" |
| " 'name' : 'Home address',\n" |
| " 'value' : 'Maida Vale, London, England',\n" |
| " 'accessControlProfiles' : [1, ],\n" |
| " },\n" |
| " {\n" |
| " 'name' : 'Work address',\n" |
| " 'value' : 'Maida Vale2, London, England',\n" |
| " 'accessControlProfiles' : [2, ],\n" |
| " },\n" |
| " {\n" |
| " 'name' : 'Trailer address',\n" |
| " 'value' : 'Maida, London, England',\n" |
| " 'accessControlProfiles' : [1, ],\n" |
| " },\n" |
| " ],\n" |
| " 'Image' : [\n" |
| " {\n" |
| " 'name' : 'Portrait image',\n" |
| " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n" |
| " 'accessControlProfiles' : [1, ],\n" |
| " },\n" |
| " ],\n" |
| " 'Image2' : [\n" |
| " {\n" |
| " 'name' : 'Work image',\n" |
| " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n" |
| " 'accessControlProfiles' : [1, 2, ],\n" |
| " },\n" |
| " ],\n" |
| " 'Name Space3' : [\n" |
| " {\n" |
| " 'name' : 'xyzw',\n" |
| " 'value' : 'random stuff',\n" |
| " 'accessControlProfiles' : [1, 2, ],\n" |
| " },\n" |
| " {\n" |
| " 'name' : 'Something',\n" |
| " 'value' : 'Some string',\n" |
| " 'accessControlProfiles' : [2, ],\n" |
| " },\n" |
| " ],\n" |
| " },\n" |
| " false,\n" |
| "]", |
| cborPretty); |
| |
| optional<vector<uint8_t>> credentialPubKey = support::certificateChainGetTopMostKey( |
| attData.attestationCertificate[0].encodedCertificate); |
| ASSERT_TRUE(credentialPubKey); |
| EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature, |
| {}, // Additional data |
| credentialPubKey.value())); |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyEmptyNameSpaceMixedWithNonEmptyWorks) { |
| Status result; |
| |
| HardwareInformation hwInfo; |
| ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); |
| |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| string challenge = "NotSoRandomChallenge"; |
| test_utils::AttestationData attData(writableCredential, challenge, |
| {} /* atteestationApplicationId */); |
| ASSERT_TRUE(attData.result.isOk()) |
| << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl; |
| |
| optional<vector<uint8_t>> readerCertificate1 = test_utils::generateReaderCertificate("123456"); |
| ASSERT_TRUE(readerCertificate1); |
| |
| optional<vector<uint8_t>> readerCertificate2 = |
| test_utils::generateReaderCertificate("123456987987987987987987"); |
| ASSERT_TRUE(readerCertificate2); |
| |
| const vector<int32_t> entryCounts = {2u, 2u}; |
| size_t expectedPoPSize = |
| 377 + readerCertificate1.value().size() + readerCertificate2.value().size(); |
| ; |
| // OK to fail, not available in v1 HAL |
| writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize); |
| result = writableCredential->startPersonalization(3, entryCounts); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| |
| const vector<test_utils::TestProfile> testProfiles = {{0, readerCertificate1.value(), false, 0}, |
| {1, readerCertificate2.value(), true, 1}, |
| {2, {}, false, 0}}; |
| |
| optional<vector<SecureAccessControlProfile>> secureProfiles = |
| test_utils::addAccessControlProfiles(writableCredential, testProfiles); |
| ASSERT_TRUE(secureProfiles); |
| |
| const vector<test_utils::TestEntryData> testEntries1 = { |
| // test empty name space |
| {"", "t name", string("Turing"), vector<int32_t>{2}}, |
| {"", "Birth", string("19120623"), vector<int32_t>{2}}, |
| {"Name Space", "Last name", string("Turing"), vector<int32_t>{0, 1}}, |
| {"Name Space", "Birth date", string("19120623"), vector<int32_t>{0, 1}}, |
| }; |
| |
| map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs; |
| for (const auto& entry : testEntries1) { |
| EXPECT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize, |
| encryptedBlobs, true)); |
| } |
| |
| vector<uint8_t> credentialData; |
| vector<uint8_t> proofOfProvisioningSignature; |
| result = |
| writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature); |
| |
| EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyInterleavingEntryNameSpaceOrderingFails) { |
| Status result; |
| |
| HardwareInformation hwInfo; |
| ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk()); |
| |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| string challenge = "NotSoRandomChallenge"; |
| test_utils::AttestationData attData(writableCredential, challenge, |
| {} /* atteestationApplicationId */); |
| ASSERT_TRUE(attData.result.isOk()) |
| << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl; |
| |
| // Enter mismatched entry and profile numbers. |
| // Technically the 2nd name space of "Name Space" occurs intermittently, 2 |
| // before "Image" and 2 after image, which is not correct. All of same name |
| // space should occur together. Let's see if this fails. |
| const vector<int32_t> entryCounts = {2u, 1u, 2u}; |
| writableCredential->setExpectedProofOfProvisioningSize(123456); |
| result = writableCredential->startPersonalization(3, entryCounts); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| |
| optional<vector<uint8_t>> readerCertificate1 = test_utils::generateReaderCertificate("123456"); |
| ASSERT_TRUE(readerCertificate1); |
| |
| optional<vector<uint8_t>> readerCertificate2 = |
| test_utils::generateReaderCertificate("123456987987987987987987"); |
| ASSERT_TRUE(readerCertificate2); |
| |
| const vector<test_utils::TestProfile> testProfiles = {{0, readerCertificate1.value(), false, 0}, |
| {1, readerCertificate2.value(), true, 1}, |
| {2, {}, false, 0}}; |
| |
| optional<vector<SecureAccessControlProfile>> secureProfiles = |
| test_utils::addAccessControlProfiles(writableCredential, testProfiles); |
| ASSERT_TRUE(secureProfiles); |
| |
| const vector<test_utils::TestEntryData> testEntries1 = { |
| // test empty name space |
| {"Name Space", "Last name", string("Turing"), vector<int32_t>{0, 1}}, |
| {"Name Space", "Birth date", string("19120623"), vector<int32_t>{0, 1}}, |
| }; |
| |
| map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs; |
| for (const auto& entry : testEntries1) { |
| EXPECT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize, |
| encryptedBlobs, true)); |
| } |
| const test_utils::TestEntryData testEntry2 = {"Image", "Portrait image", string("asdfs"), |
| vector<int32_t>{0, 1}}; |
| |
| 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" |
| // should have been called together |
| const vector<test_utils::TestEntryData> testEntries3 = { |
| {"Name Space", "First name", string("Alan"), vector<int32_t>{0, 1}}, |
| {"Name Space", "Home address", string("Maida Vale, London, England"), |
| vector<int32_t>{0}}, |
| }; |
| |
| for (const auto& entry : testEntries3) { |
| EXPECT_FALSE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize, |
| encryptedBlobs, false)); |
| } |
| |
| vector<uint8_t> credentialData; |
| vector<uint8_t> proofOfProvisioningSignature; |
| result = |
| writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature); |
| |
| // 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()); |
| EXPECT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode()); |
| } |
| |
| TEST_P(IdentityCredentialTests, verifyAccessControlProfileIdOutOfRange) { |
| sp<IWritableIdentityCredential> writableCredential; |
| ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_, |
| false /* testCredential */)); |
| |
| const vector<int32_t> entryCounts = {1}; |
| writableCredential->setExpectedProofOfProvisioningSize(123456); |
| Status result = writableCredential->startPersonalization(1, entryCounts); |
| ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage() |
| << endl; |
| |
| SecureAccessControlProfile profile; |
| |
| // This should fail because the id is >= 32 |
| result = writableCredential->addAccessControlProfile(32, // id |
| {}, // readerCertificate |
| false, // userAuthenticationRequired |
| 0, // timeoutMillis |
| 42, // secureUserId |
| &profile); |
| ASSERT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); |
| ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); |
| ASSERT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode()); |
| |
| // This should fail because the id is < 0 |
| result = writableCredential->addAccessControlProfile(-1, // id |
| {}, // readerCertificate |
| false, // userAuthenticationRequired |
| 0, // timeoutMillis |
| 42, // secureUserId |
| &profile); |
| ASSERT_FALSE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage(); |
| ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); |
| ASSERT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode()); |
| } |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(IdentityCredentialTests); |
| INSTANTIATE_TEST_SUITE_P( |
| Identity, IdentityCredentialTests, |
| testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)), |
| android::PrintInstanceNameToString); |
| |
| } // namespace android::hardware::identity |