mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-02 23:48:30 +00:00
This change removes hardware/interfaces/identity's dependency on its own libcppbor copy. The copy can not be fully removed until various vendor dependencies are cleaned up. Superficial changes are made to the VTS tests to match the slightly altered namespace on some of the functions. This migration is a prerequisite for getting the IRemotelyProvisionedComponent functionality into system/keymaster. Without migrating to the same library, the build system runs into issues since there are "two" libcppbor libraries with conflicting namespaces otherwise. Bug: 182445123 Test: atest VtsHalIdentityTargetTest Change-Id: I854ffa31c4adb5a3d1df06539fe66075ccc4625d
604 lines
26 KiB
C++
604 lines
26 KiB
C++
/*
|
|
* 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 "ReaderAuthTests"
|
|
|
|
#include <aidl/Gtest.h>
|
|
#include <aidl/Vintf.h>
|
|
#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
|
|
#include <aidl/android/hardware/keymaster/VerificationToken.h>
|
|
#include <android-base/logging.h>
|
|
#include <android/hardware/identity/IIdentityCredentialStore.h>
|
|
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
|
|
#include <binder/IServiceManager.h>
|
|
#include <binder/ProcessState.h>
|
|
#include <cppbor.h>
|
|
#include <cppbor_parse.h>
|
|
#include <gtest/gtest.h>
|
|
#include <future>
|
|
#include <map>
|
|
#include <utility>
|
|
|
|
#include "Util.h"
|
|
|
|
namespace android::hardware::identity {
|
|
|
|
using std::endl;
|
|
using std::make_pair;
|
|
using std::map;
|
|
using std::optional;
|
|
using std::pair;
|
|
using std::string;
|
|
using std::tie;
|
|
using std::vector;
|
|
|
|
using ::android::sp;
|
|
using ::android::String16;
|
|
using ::android::binder::Status;
|
|
|
|
using ::android::hardware::keymaster::HardwareAuthToken;
|
|
using ::android::hardware::keymaster::VerificationToken;
|
|
|
|
class ReaderAuthTests : public testing::TestWithParam<string> {
|
|
public:
|
|
virtual void SetUp() override {
|
|
credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
|
|
String16(GetParam().c_str()));
|
|
ASSERT_NE(credentialStore_, nullptr);
|
|
}
|
|
|
|
void provisionData();
|
|
void retrieveData(const vector<uint8_t>& readerPrivateKey,
|
|
const vector<vector<uint8_t>>& readerCertChain, bool expectSuccess,
|
|
bool leaveOutAccessibleToAllFromRequestMessage);
|
|
|
|
// Set by provisionData
|
|
vector<uint8_t> readerPublicKey_;
|
|
vector<uint8_t> readerPrivateKey_;
|
|
vector<uint8_t> intermediateAPublicKey_;
|
|
vector<uint8_t> intermediateAPrivateKey_;
|
|
vector<uint8_t> intermediateBPublicKey_;
|
|
vector<uint8_t> intermediateBPrivateKey_;
|
|
vector<uint8_t> intermediateCPublicKey_;
|
|
vector<uint8_t> intermediateCPrivateKey_;
|
|
|
|
vector<uint8_t> cert_A_SelfSigned_;
|
|
|
|
vector<uint8_t> cert_B_SelfSigned_;
|
|
|
|
vector<uint8_t> cert_B_SignedBy_C_;
|
|
|
|
vector<uint8_t> cert_C_SelfSigned_;
|
|
|
|
vector<uint8_t> cert_reader_SelfSigned_;
|
|
vector<uint8_t> cert_reader_SignedBy_A_;
|
|
vector<uint8_t> cert_reader_SignedBy_B_;
|
|
|
|
SecureAccessControlProfile sacp0_;
|
|
SecureAccessControlProfile sacp1_;
|
|
SecureAccessControlProfile sacp2_;
|
|
SecureAccessControlProfile sacp3_;
|
|
|
|
vector<uint8_t> encContentAccessibleByA_;
|
|
vector<uint8_t> encContentAccessibleByAorB_;
|
|
vector<uint8_t> encContentAccessibleByB_;
|
|
vector<uint8_t> encContentAccessibleByC_;
|
|
vector<uint8_t> encContentAccessibleByAll_;
|
|
vector<uint8_t> encContentAccessibleByNone_;
|
|
|
|
vector<uint8_t> credentialData_;
|
|
|
|
// Set by retrieveData()
|
|
bool canGetAccessibleByA_;
|
|
bool canGetAccessibleByAorB_;
|
|
bool canGetAccessibleByB_;
|
|
bool canGetAccessibleByC_;
|
|
bool canGetAccessibleByAll_;
|
|
bool canGetAccessibleByNone_;
|
|
|
|
sp<IIdentityCredentialStore> credentialStore_;
|
|
};
|
|
|
|
pair<vector<uint8_t>, vector<uint8_t>> generateReaderKey() {
|
|
optional<vector<uint8_t>> keyPKCS8 = support::createEcKeyPair();
|
|
optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPKCS8.value());
|
|
optional<vector<uint8_t>> privateKey = support::ecKeyPairGetPrivateKey(keyPKCS8.value());
|
|
return make_pair(publicKey.value(), privateKey.value());
|
|
}
|
|
|
|
vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey,
|
|
const vector<uint8_t>& signingKey) {
|
|
time_t validityNotBefore = 0;
|
|
time_t validityNotAfter = 0xffffffff;
|
|
optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate(
|
|
publicKey, signingKey, "24601", "Issuer", "Subject", validityNotBefore,
|
|
validityNotAfter, {});
|
|
return cert.value();
|
|
}
|
|
|
|
void ReaderAuthTests::provisionData() {
|
|
// Keys and certificates for intermediates.
|
|
tie(intermediateAPublicKey_, intermediateAPrivateKey_) = generateReaderKey();
|
|
tie(intermediateBPublicKey_, intermediateBPrivateKey_) = generateReaderKey();
|
|
tie(intermediateCPublicKey_, intermediateCPrivateKey_) = generateReaderKey();
|
|
|
|
cert_A_SelfSigned_ = generateReaderCert(intermediateAPublicKey_, intermediateAPrivateKey_);
|
|
|
|
cert_B_SelfSigned_ = generateReaderCert(intermediateBPublicKey_, intermediateBPrivateKey_);
|
|
|
|
cert_B_SignedBy_C_ = generateReaderCert(intermediateBPublicKey_, intermediateCPrivateKey_);
|
|
|
|
cert_C_SelfSigned_ = generateReaderCert(intermediateCPublicKey_, intermediateCPrivateKey_);
|
|
|
|
// Key and self-signed certificate reader
|
|
tie(readerPublicKey_, readerPrivateKey_) = generateReaderKey();
|
|
cert_reader_SelfSigned_ = generateReaderCert(readerPublicKey_, readerPrivateKey_);
|
|
|
|
// Certificate for reader signed by intermediates
|
|
cert_reader_SignedBy_A_ = generateReaderCert(readerPublicKey_, intermediateAPrivateKey_);
|
|
cert_reader_SignedBy_B_ = generateReaderCert(readerPublicKey_, intermediateBPrivateKey_);
|
|
|
|
string docType = "org.iso.18013-5.2019.mdl";
|
|
bool testCredential = true;
|
|
sp<IWritableIdentityCredential> wc;
|
|
ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
|
|
|
|
vector<uint8_t> attestationApplicationId = {};
|
|
vector<uint8_t> attestationChallenge = {1};
|
|
vector<Certificate> certChain;
|
|
ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
|
|
&certChain)
|
|
.isOk());
|
|
|
|
size_t proofOfProvisioningSize =
|
|
465 + cert_A_SelfSigned_.size() + cert_B_SelfSigned_.size() + cert_C_SelfSigned_.size();
|
|
ASSERT_TRUE(wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize).isOk());
|
|
|
|
// Not in v1 HAL, may fail
|
|
wc->startPersonalization(4 /* numAccessControlProfiles */,
|
|
{6} /* numDataElementsPerNamespace */);
|
|
|
|
// AIDL expects certificates wrapped in the Certificate type...
|
|
Certificate cert_A;
|
|
Certificate cert_B;
|
|
Certificate cert_C;
|
|
cert_A.encodedCertificate = cert_A_SelfSigned_;
|
|
cert_B.encodedCertificate = cert_B_SelfSigned_;
|
|
cert_C.encodedCertificate = cert_C_SelfSigned_;
|
|
|
|
// Access control profile 0: accessible by A
|
|
ASSERT_TRUE(wc->addAccessControlProfile(0, cert_A, false, 0, 0, &sacp0_).isOk());
|
|
|
|
// Access control profile 1: accessible by B
|
|
ASSERT_TRUE(wc->addAccessControlProfile(1, cert_B, false, 0, 0, &sacp1_).isOk());
|
|
|
|
// Access control profile 2: accessible by C
|
|
ASSERT_TRUE(wc->addAccessControlProfile(2, cert_C, false, 0, 0, &sacp2_).isOk());
|
|
|
|
// Access control profile 3: open access
|
|
ASSERT_TRUE(wc->addAccessControlProfile(3, {}, false, 0, 0, &sacp3_).isOk());
|
|
|
|
// Data Element: "Accessible by A"
|
|
ASSERT_TRUE(wc->beginAddEntry({0}, "ns", "Accessible by A", 1).isOk());
|
|
ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByA_).isOk());
|
|
|
|
// Data Element: "Accessible by A or B"
|
|
ASSERT_TRUE(wc->beginAddEntry({0, 1}, "ns", "Accessible by A or B", 1).isOk());
|
|
ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByAorB_).isOk());
|
|
|
|
// Data Element: "Accessible by B"
|
|
ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Accessible by B", 1).isOk());
|
|
ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByB_).isOk());
|
|
|
|
// Data Element: "Accessible by C"
|
|
ASSERT_TRUE(wc->beginAddEntry({2}, "ns", "Accessible by C", 1).isOk());
|
|
ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByC_).isOk());
|
|
|
|
// Data Element: "Accessible by All"
|
|
ASSERT_TRUE(wc->beginAddEntry({3}, "ns", "Accessible by All", 1).isOk());
|
|
ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByAll_).isOk());
|
|
|
|
// Data Element: "Accessible by None"
|
|
ASSERT_TRUE(wc->beginAddEntry({}, "ns", "Accessible by None", 1).isOk());
|
|
ASSERT_TRUE(wc->addEntryValue({9}, &encContentAccessibleByNone_).isOk());
|
|
|
|
vector<uint8_t> proofOfProvisioningSignature;
|
|
ASSERT_TRUE(wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature).isOk());
|
|
}
|
|
|
|
RequestDataItem buildRequestDataItem(const string& name, size_t size,
|
|
vector<int32_t> accessControlProfileIds) {
|
|
RequestDataItem item;
|
|
item.name = name;
|
|
item.size = size;
|
|
item.accessControlProfileIds = accessControlProfileIds;
|
|
return item;
|
|
}
|
|
|
|
void ReaderAuthTests::retrieveData(const vector<uint8_t>& readerPrivateKey,
|
|
const vector<vector<uint8_t>>& readerCertChain,
|
|
bool expectSuccess,
|
|
bool leaveOutAccessibleToAllFromRequestMessage) {
|
|
canGetAccessibleByA_ = false;
|
|
canGetAccessibleByAorB_ = false;
|
|
canGetAccessibleByB_ = false;
|
|
canGetAccessibleByC_ = false;
|
|
canGetAccessibleByAll_ = false;
|
|
canGetAccessibleByNone_ = false;
|
|
|
|
sp<IIdentityCredential> c;
|
|
ASSERT_TRUE(credentialStore_
|
|
->getCredential(
|
|
CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
|
|
credentialData_, &c)
|
|
.isOk());
|
|
|
|
optional<vector<uint8_t>> readerEKeyPair = support::createEcKeyPair();
|
|
optional<vector<uint8_t>> readerEPublicKey =
|
|
support::ecKeyPairGetPublicKey(readerEKeyPair.value());
|
|
ASSERT_TRUE(c->setReaderEphemeralPublicKey(readerEPublicKey.value()).isOk());
|
|
|
|
vector<uint8_t> eKeyPair;
|
|
ASSERT_TRUE(c->createEphemeralKeyPair(&eKeyPair).isOk());
|
|
optional<vector<uint8_t>> ePublicKey = support::ecKeyPairGetPublicKey(eKeyPair);
|
|
|
|
// Calculate requestData field and sign it with the reader key.
|
|
auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ePublicKey.value());
|
|
ASSERT_TRUE(getXYSuccess);
|
|
cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
|
|
vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
|
|
vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
|
|
cppbor::Array sessionTranscript = cppbor::Array()
|
|
.add(cppbor::SemanticTag(24, deviceEngagementBytes))
|
|
.add(cppbor::SemanticTag(24, eReaderPubBytes));
|
|
vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
|
|
|
|
vector<uint8_t> itemsRequestBytes;
|
|
if (leaveOutAccessibleToAllFromRequestMessage) {
|
|
itemsRequestBytes =
|
|
cppbor::Map("nameSpaces",
|
|
cppbor::Map().add("ns", cppbor::Map()
|
|
.add("Accessible by A", false)
|
|
.add("Accessible by A or B", false)
|
|
.add("Accessible by B", false)
|
|
.add("Accessible by C", false)
|
|
.add("Accessible by None", false)))
|
|
.encode();
|
|
} else {
|
|
itemsRequestBytes =
|
|
cppbor::Map("nameSpaces",
|
|
cppbor::Map().add("ns", cppbor::Map()
|
|
.add("Accessible by A", false)
|
|
.add("Accessible by A or B", false)
|
|
.add("Accessible by B", false)
|
|
.add("Accessible by C", false)
|
|
.add("Accessible by All", false)
|
|
.add("Accessible by None", false)))
|
|
.encode();
|
|
}
|
|
vector<uint8_t> encodedReaderAuthentication =
|
|
cppbor::Array()
|
|
.add("ReaderAuthentication")
|
|
.add(sessionTranscript.clone())
|
|
.add(cppbor::SemanticTag(24, itemsRequestBytes))
|
|
.encode();
|
|
vector<uint8_t> encodedReaderAuthenticationBytes =
|
|
cppbor::SemanticTag(24, encodedReaderAuthentication).encode();
|
|
|
|
optional<vector<uint8_t>> readerSignature =
|
|
support::coseSignEcDsa(readerPrivateKey, // private key for reader
|
|
{}, // content
|
|
encodedReaderAuthenticationBytes, // detached content
|
|
support::certificateChainJoin(readerCertChain));
|
|
ASSERT_TRUE(readerSignature);
|
|
|
|
// Generate the key that will be used to sign AuthenticatedData.
|
|
vector<uint8_t> signingKeyBlob;
|
|
Certificate signingKeyCertificate;
|
|
ASSERT_TRUE(c->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
|
|
|
|
RequestNamespace rns;
|
|
rns.namespaceName = "ns";
|
|
rns.items.push_back(buildRequestDataItem("Accessible by A", 1, {0}));
|
|
rns.items.push_back(buildRequestDataItem("Accessible by A or B", 1, {0, 1}));
|
|
rns.items.push_back(buildRequestDataItem("Accessible by B", 1, {1}));
|
|
rns.items.push_back(buildRequestDataItem("Accessible by C", 1, {2}));
|
|
rns.items.push_back(buildRequestDataItem("Accessible by All", 1, {3}));
|
|
rns.items.push_back(buildRequestDataItem("Accessible by None", 1, {}));
|
|
// OK to fail, not available in v1 HAL
|
|
c->setRequestedNamespaces({rns}).isOk();
|
|
|
|
// It doesn't matter since no user auth is needed in this particular test,
|
|
// but for good measure, clear out the tokens we pass to the HAL.
|
|
HardwareAuthToken authToken;
|
|
VerificationToken verificationToken;
|
|
authToken.challenge = 0;
|
|
authToken.userId = 0;
|
|
authToken.authenticatorId = 0;
|
|
authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
|
|
authToken.timestamp.milliSeconds = 0;
|
|
authToken.mac.clear();
|
|
verificationToken.challenge = 0;
|
|
verificationToken.timestamp.milliSeconds = 0;
|
|
verificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE;
|
|
verificationToken.mac.clear();
|
|
// OK to fail, not available in v1 HAL
|
|
c->setVerificationToken(verificationToken);
|
|
|
|
Status status = c->startRetrieval(
|
|
{sacp0_, sacp1_, sacp2_, sacp3_}, authToken, itemsRequestBytes, signingKeyBlob,
|
|
sessionTranscriptBytes, readerSignature.value(), {6 /* numDataElementsPerNamespace */});
|
|
if (expectSuccess) {
|
|
ASSERT_TRUE(status.isOk());
|
|
} else {
|
|
ASSERT_FALSE(status.isOk());
|
|
return;
|
|
}
|
|
|
|
vector<uint8_t> decrypted;
|
|
|
|
status = c->startRetrieveEntryValue("ns", "Accessible by A", 1, {0});
|
|
if (status.isOk()) {
|
|
canGetAccessibleByA_ = true;
|
|
ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByA_, &decrypted).isOk());
|
|
}
|
|
|
|
status = c->startRetrieveEntryValue("ns", "Accessible by A or B", 1, {0, 1});
|
|
if (status.isOk()) {
|
|
canGetAccessibleByAorB_ = true;
|
|
ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByAorB_, &decrypted).isOk());
|
|
}
|
|
|
|
status = c->startRetrieveEntryValue("ns", "Accessible by B", 1, {1});
|
|
if (status.isOk()) {
|
|
canGetAccessibleByB_ = true;
|
|
ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByB_, &decrypted).isOk());
|
|
}
|
|
|
|
status = c->startRetrieveEntryValue("ns", "Accessible by C", 1, {2});
|
|
if (status.isOk()) {
|
|
canGetAccessibleByC_ = true;
|
|
ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByC_, &decrypted).isOk());
|
|
}
|
|
|
|
status = c->startRetrieveEntryValue("ns", "Accessible by All", 1, {3});
|
|
if (status.isOk()) {
|
|
canGetAccessibleByAll_ = true;
|
|
ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByAll_, &decrypted).isOk());
|
|
}
|
|
|
|
status = c->startRetrieveEntryValue("ns", "Accessible by None", 1, {});
|
|
if (status.isOk()) {
|
|
canGetAccessibleByNone_ = true;
|
|
ASSERT_TRUE(c->retrieveEntryValue(encContentAccessibleByNone_, &decrypted).isOk());
|
|
}
|
|
|
|
vector<uint8_t> mac;
|
|
vector<uint8_t> deviceNameSpaces;
|
|
ASSERT_TRUE(c->finishRetrieval(&mac, &deviceNameSpaces).isOk());
|
|
}
|
|
|
|
TEST_P(ReaderAuthTests, presentingChain_Reader) {
|
|
provisionData();
|
|
retrieveData(readerPrivateKey_, {cert_reader_SelfSigned_}, true /* expectSuccess */,
|
|
false /* leaveOutAccessibleToAllFromRequestMessage */);
|
|
EXPECT_FALSE(canGetAccessibleByA_);
|
|
EXPECT_FALSE(canGetAccessibleByAorB_);
|
|
EXPECT_FALSE(canGetAccessibleByB_);
|
|
EXPECT_FALSE(canGetAccessibleByC_);
|
|
EXPECT_TRUE(canGetAccessibleByAll_);
|
|
EXPECT_FALSE(canGetAccessibleByNone_);
|
|
}
|
|
|
|
TEST_P(ReaderAuthTests, presentingChain_Reader_A) {
|
|
provisionData();
|
|
retrieveData(readerPrivateKey_, {cert_reader_SignedBy_A_, cert_A_SelfSigned_},
|
|
true /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
|
|
EXPECT_TRUE(canGetAccessibleByA_);
|
|
EXPECT_TRUE(canGetAccessibleByAorB_);
|
|
EXPECT_FALSE(canGetAccessibleByB_);
|
|
EXPECT_FALSE(canGetAccessibleByC_);
|
|
EXPECT_TRUE(canGetAccessibleByAll_);
|
|
EXPECT_FALSE(canGetAccessibleByNone_);
|
|
}
|
|
|
|
TEST_P(ReaderAuthTests, presentingChain_Reader_B) {
|
|
provisionData();
|
|
retrieveData(readerPrivateKey_, {cert_reader_SignedBy_B_, cert_B_SelfSigned_},
|
|
true /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
|
|
EXPECT_FALSE(canGetAccessibleByA_);
|
|
EXPECT_TRUE(canGetAccessibleByAorB_);
|
|
EXPECT_TRUE(canGetAccessibleByB_);
|
|
EXPECT_FALSE(canGetAccessibleByC_);
|
|
EXPECT_TRUE(canGetAccessibleByAll_);
|
|
EXPECT_FALSE(canGetAccessibleByNone_);
|
|
}
|
|
|
|
// This test proves that for the purpose of determining inclusion of an ACP certificate
|
|
// in a presented reader chain, certificate equality is done by comparing public keys,
|
|
// not bitwise comparison of the certificates.
|
|
//
|
|
// Specifically for this test, the ACP is configured with cert_B_SelfSigned_ and the
|
|
// reader is presenting cert_B_SignedBy_C_. Both certificates have the same public
|
|
// key - intermediateBPublicKey_ - but they are signed by different keys.
|
|
//
|
|
TEST_P(ReaderAuthTests, presentingChain_Reader_B_C) {
|
|
provisionData();
|
|
retrieveData(readerPrivateKey_,
|
|
{cert_reader_SignedBy_B_, cert_B_SignedBy_C_, cert_C_SelfSigned_},
|
|
true /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
|
|
EXPECT_FALSE(canGetAccessibleByA_);
|
|
EXPECT_TRUE(canGetAccessibleByAorB_);
|
|
EXPECT_TRUE(canGetAccessibleByB_);
|
|
EXPECT_TRUE(canGetAccessibleByC_);
|
|
EXPECT_TRUE(canGetAccessibleByAll_);
|
|
EXPECT_FALSE(canGetAccessibleByNone_);
|
|
}
|
|
|
|
// This test presents a reader chain where the chain is invalid because
|
|
// the 2nd certificate in the chain isn't signed by the 3rd one.
|
|
//
|
|
TEST_P(ReaderAuthTests, presentingInvalidChain) {
|
|
provisionData();
|
|
retrieveData(readerPrivateKey_,
|
|
{cert_reader_SignedBy_B_, cert_B_SelfSigned_, cert_C_SelfSigned_},
|
|
false /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
|
|
}
|
|
|
|
// This tests presents a valid reader chain but where requestMessage isn't
|
|
// signed by the private key corresponding to the public key in the top-level
|
|
// certificate.
|
|
//
|
|
TEST_P(ReaderAuthTests, presentingMessageSignedNotByTopLevel) {
|
|
provisionData();
|
|
retrieveData(intermediateBPrivateKey_,
|
|
{cert_reader_SignedBy_B_, cert_B_SignedBy_C_, cert_C_SelfSigned_},
|
|
false /* expectSuccess */, false /* leaveOutAccessibleToAllFromRequestMessage */);
|
|
}
|
|
|
|
// This test leaves out "Accessible by All" data element from the signed request
|
|
// message (the CBOR from the reader) while still including this data element at
|
|
// the API level. The call on the API level for said element will fail with
|
|
// STATUS_NOT_IN_REQUEST_MESSAGE but this doesn't prevent the other elements
|
|
// from being returned (if authorized, of course).
|
|
//
|
|
// This test verifies that.
|
|
//
|
|
TEST_P(ReaderAuthTests, limitedMessage) {
|
|
provisionData();
|
|
retrieveData(readerPrivateKey_, {cert_reader_SelfSigned_}, true /* expectSuccess */,
|
|
true /* leaveOutAccessibleToAllFromRequestMessage */);
|
|
EXPECT_FALSE(canGetAccessibleByA_);
|
|
EXPECT_FALSE(canGetAccessibleByAorB_);
|
|
EXPECT_FALSE(canGetAccessibleByB_);
|
|
EXPECT_FALSE(canGetAccessibleByC_);
|
|
EXPECT_FALSE(canGetAccessibleByAll_);
|
|
EXPECT_FALSE(canGetAccessibleByNone_);
|
|
}
|
|
|
|
TEST_P(ReaderAuthTests, ephemeralKeyNotInSessionTranscript) {
|
|
provisionData();
|
|
|
|
sp<IIdentityCredential> c;
|
|
ASSERT_TRUE(credentialStore_
|
|
->getCredential(
|
|
CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
|
|
credentialData_, &c)
|
|
.isOk());
|
|
|
|
optional<vector<uint8_t>> readerEKeyPair = support::createEcKeyPair();
|
|
optional<vector<uint8_t>> readerEPublicKey =
|
|
support::ecKeyPairGetPublicKey(readerEKeyPair.value());
|
|
ASSERT_TRUE(c->setReaderEphemeralPublicKey(readerEPublicKey.value()).isOk());
|
|
|
|
vector<uint8_t> eKeyPair;
|
|
ASSERT_TRUE(c->createEphemeralKeyPair(&eKeyPair).isOk());
|
|
optional<vector<uint8_t>> ePublicKey = support::ecKeyPairGetPublicKey(eKeyPair);
|
|
|
|
// Calculate requestData field and sign it with the reader key.
|
|
auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ePublicKey.value());
|
|
ASSERT_TRUE(getXYSuccess);
|
|
// Instead of include the X and Y coordinates (|ephX| and |ephY|), add NUL bytes instead.
|
|
vector<uint8_t> nulls(32);
|
|
cppbor::Map deviceEngagement = cppbor::Map().add("ephX", nulls).add("ephY", nulls);
|
|
vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
|
|
vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
|
|
cppbor::Array sessionTranscript = cppbor::Array()
|
|
.add(cppbor::SemanticTag(24, deviceEngagementBytes))
|
|
.add(cppbor::SemanticTag(24, eReaderPubBytes));
|
|
vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
|
|
|
|
vector<uint8_t> itemsRequestBytes;
|
|
itemsRequestBytes =
|
|
cppbor::Map("nameSpaces",
|
|
cppbor::Map().add("ns", cppbor::Map()
|
|
.add("Accessible by A", false)
|
|
.add("Accessible by A or B", false)
|
|
.add("Accessible by B", false)
|
|
.add("Accessible by C", false)
|
|
.add("Accessible by None", false)))
|
|
.encode();
|
|
vector<uint8_t> encodedReaderAuthentication =
|
|
cppbor::Array()
|
|
.add("ReaderAuthentication")
|
|
.add(sessionTranscript.clone())
|
|
.add(cppbor::SemanticTag(24, itemsRequestBytes))
|
|
.encode();
|
|
vector<uint8_t> encodedReaderAuthenticationBytes =
|
|
cppbor::SemanticTag(24, encodedReaderAuthentication).encode();
|
|
|
|
vector<vector<uint8_t>> readerCertChain = {cert_reader_SelfSigned_};
|
|
optional<vector<uint8_t>> readerSignature =
|
|
support::coseSignEcDsa(readerPrivateKey_, // private key for reader
|
|
{}, // content
|
|
encodedReaderAuthenticationBytes, // detached content
|
|
support::certificateChainJoin(readerCertChain));
|
|
ASSERT_TRUE(readerSignature);
|
|
|
|
// Generate the key that will be used to sign AuthenticatedData.
|
|
vector<uint8_t> signingKeyBlob;
|
|
Certificate signingKeyCertificate;
|
|
ASSERT_TRUE(c->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
|
|
|
|
RequestNamespace rns;
|
|
rns.namespaceName = "ns";
|
|
rns.items.push_back(buildRequestDataItem("Accessible by A", 1, {0}));
|
|
rns.items.push_back(buildRequestDataItem("Accessible by A or B", 1, {0, 1}));
|
|
rns.items.push_back(buildRequestDataItem("Accessible by B", 1, {1}));
|
|
rns.items.push_back(buildRequestDataItem("Accessible by C", 1, {2}));
|
|
rns.items.push_back(buildRequestDataItem("Accessible by All", 1, {3}));
|
|
rns.items.push_back(buildRequestDataItem("Accessible by None", 1, {}));
|
|
// OK to fail, not available in v1 HAL
|
|
c->setRequestedNamespaces({rns}).isOk();
|
|
|
|
// It doesn't matter since no user auth is needed in this particular test,
|
|
// but for good measure, clear out the tokens we pass to the HAL.
|
|
HardwareAuthToken authToken;
|
|
VerificationToken verificationToken;
|
|
authToken.challenge = 0;
|
|
authToken.userId = 0;
|
|
authToken.authenticatorId = 0;
|
|
authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
|
|
authToken.timestamp.milliSeconds = 0;
|
|
authToken.mac.clear();
|
|
verificationToken.challenge = 0;
|
|
verificationToken.timestamp.milliSeconds = 0;
|
|
verificationToken.securityLevel =
|
|
::android::hardware::keymaster::SecurityLevel::TRUSTED_ENVIRONMENT;
|
|
verificationToken.mac.clear();
|
|
// OK to fail, not available in v1 HAL
|
|
c->setVerificationToken(verificationToken);
|
|
|
|
// Finally check that STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND is returned.
|
|
// This proves that the TA checked for X and Y coordinatets and didn't find
|
|
// them.
|
|
Status status = c->startRetrieval(
|
|
{sacp0_, sacp1_, sacp2_, sacp3_}, authToken, itemsRequestBytes, signingKeyBlob,
|
|
sessionTranscriptBytes, readerSignature.value(), {6 /* numDataElementsPerNamespace */});
|
|
ASSERT_FALSE(status.isOk());
|
|
ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
|
|
ASSERT_EQ(IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
|
|
status.serviceSpecificErrorCode());
|
|
}
|
|
|
|
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReaderAuthTests);
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
Identity, ReaderAuthTests,
|
|
testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
|
|
android::PrintInstanceNameToString);
|
|
|
|
} // namespace android::hardware::identity
|