From 1eb12b29728adcbbe5b8694f671c67b8a624fe4a Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Sat, 11 Sep 2021 13:59:43 -0400 Subject: identity: Add multi-document presentation support. This new IPresentationSession interface enables an application to do a multi-document presentation, something which isn't possible with the existing API. As a practical example of this consider presenting both your Mobile Driving License and your Vaccination Certificate in a single transaction. Bug: 197965513 Test: New CTS tests and new screen in CtsVerifier Change-Id: I11712dca35df7f1224debf454731bc17ea9bfb37 --- identity/aidl/vts/PresentationSessionTests.cpp | 197 +++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 identity/aidl/vts/PresentationSessionTests.cpp (limited to 'identity/aidl/vts/PresentationSessionTests.cpp') diff --git a/identity/aidl/vts/PresentationSessionTests.cpp b/identity/aidl/vts/PresentationSessionTests.cpp new file mode 100644 index 0000000000..88acb269e2 --- /dev/null +++ b/identity/aidl/vts/PresentationSessionTests.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2020 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 "PresentationSessionTests" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 PresentationSessionTests : public testing::TestWithParam { + public: + virtual void SetUp() override { + credentialStore_ = android::waitForDeclaredService( + String16(GetParam().c_str())); + ASSERT_NE(credentialStore_, nullptr); + halApiVersion_ = credentialStore_->getInterfaceVersion(); + } + + void provisionData(); + + void provisionSingleDocument(const string& docType, vector* outCredentialData, + vector* outCredentialPubKey); + + // Set by provisionData + vector credential1Data_; + vector credential1PubKey_; + vector credential2Data_; + vector credential2PubKey_; + + sp credentialStore_; + int halApiVersion_; +}; + +void PresentationSessionTests::provisionData() { + provisionSingleDocument("org.iso.18013-5.2019.mdl", &credential1Data_, &credential1PubKey_); + provisionSingleDocument("org.blah.OtherhDocTypeXX", &credential2Data_, &credential2PubKey_); +} + +void PresentationSessionTests::provisionSingleDocument(const string& docType, + vector* outCredentialData, + vector* outCredentialPubKey) { + bool testCredential = true; + sp wc; + ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk()); + + vector attestationApplicationId; + vector attestationChallenge = {1}; + vector certChain; + ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge, + &certChain) + .isOk()); + + optional> optCredentialPubKey = + support::certificateChainGetTopMostKey(certChain[0].encodedCertificate); + ASSERT_TRUE(optCredentialPubKey); + *outCredentialPubKey = optCredentialPubKey.value(); + + size_t proofOfProvisioningSize = 106; + // Not in v1 HAL, may fail + wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize); + + ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */, + {1} /* numDataElementsPerNamespace */) + .isOk()); + + // Access control profile 0: open access - don't care about the returned SACP + SecureAccessControlProfile sacp; + ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk()); + + // Single entry - don't care about the returned encrypted data + ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Some Data", 1).isOk()); + vector encryptedData; + ASSERT_TRUE(wc->addEntryValue({9}, &encryptedData).isOk()); + + vector proofOfProvisioningSignature; + Status status = wc->finishAddingEntries(outCredentialData, &proofOfProvisioningSignature); + EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage(); +} + +// This checks that any methods called on an IIdentityCredential obtained via a session +// returns STATUS_FAILED except for startRetrieval(), startRetrieveEntryValue(), +// retrieveEntryValue(), finishRetrieval(), setRequestedNamespaces(), setVerificationToken() +// +TEST_P(PresentationSessionTests, returnsFailureOnUnsupportedMethods) { + if (halApiVersion_ < 4) { + GTEST_SKIP() << "Need HAL API version 4, have " << halApiVersion_; + } + + provisionData(); + + sp session; + ASSERT_TRUE(credentialStore_ + ->createPresentationSession( + CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256, + &session) + .isOk()); + + sp credential; + ASSERT_TRUE(session->getCredential(credential1Data_, &credential).isOk()); + + Status result; + + vector signatureProofOfDeletion; + result = credential->deleteCredential(&signatureProofOfDeletion); + EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); + EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode()); + + vector ephemeralKeyPair; + result = credential->createEphemeralKeyPair(&ephemeralKeyPair); + EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); + EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode()); + + result = credential->setReaderEphemeralPublicKey({}); + EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); + EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode()); + + int64_t authChallenge; + result = credential->createAuthChallenge(&authChallenge); + EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); + EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode()); + + Certificate certificate; + vector signingKeyBlob; + result = credential->generateSigningKeyPair(&signingKeyBlob, &certificate); + EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); + EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode()); + + result = credential->deleteCredentialWithChallenge({}, &signatureProofOfDeletion); + EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); + EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode()); + + vector signatureProofOfOwnership; + result = credential->proveOwnership({}, &signatureProofOfOwnership); + EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); + EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode()); + + sp writableCredential; + result = credential->updateCredential(&writableCredential); + EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode()); + EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode()); +} + +// TODO: need to add tests to check that the returned IIdentityCredential works +// as intended. + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PresentationSessionTests); +INSTANTIATE_TEST_SUITE_P( + Identity, PresentationSessionTests, + testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)), + android::PrintInstanceNameToString); + +} // namespace android::hardware::identity -- cgit v1.2.3