blob: eb74fa2b7cd7138bb558ab990c617bbbe3fb3196 [file] [log] [blame]
/*
* 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