Files
hardware_interfaces/identity/aidl/vts/EndToEndTests.cpp

837 lines
38 KiB
C++
Raw Normal View History

/*
* 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 "VtsHalIdentityEndToEndTest"
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <android-base/logging.h>
#include <android/hardware/identity/IIdentityCredentialStore.h>
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <cppbor.h>
#include <cppbor_parse.h>
#include <gtest/gtest.h>
#include <future>
#include <map>
#include <tuple>
#include "Util.h"
namespace android::hardware::identity {
using std::endl;
using std::make_tuple;
using std::map;
using std::optional;
using std::string;
using std::tuple;
using std::vector;
using ::android::sp;
using ::android::String16;
using ::android::binder::Status;
using ::android::hardware::keymaster::HardwareAuthToken;
using ::android::hardware::keymaster::VerificationToken;
using test_utils::validateAttestationCertificate;
class EndToEndTests : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
String16(GetParam().c_str()));
ASSERT_NE(credentialStore_, nullptr);
halApiVersion_ = credentialStore_->getInterfaceVersion();
}
sp<IIdentityCredentialStore> credentialStore_;
int halApiVersion_;
};
TEST_P(EndToEndTests, hardwareInformation) {
HardwareInformation info;
ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
ASSERT_GT(info.credentialStoreName.size(), 0);
ASSERT_GT(info.credentialStoreAuthorName.size(), 0);
ASSERT_GE(info.dataChunkSize, 256);
}
tuple<bool, string, vector<uint8_t>, vector<uint8_t>, vector<uint8_t>>
extractFromTestCredentialData(const vector<uint8_t>& credentialData) {
string docType;
vector<uint8_t> storageKey;
vector<uint8_t> credentialPrivKey;
vector<uint8_t> sha256Pop;
auto [item, _, message] = cppbor::parse(credentialData);
if (item == nullptr) {
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Array* arrayItem = item->asArray();
if (arrayItem == nullptr || arrayItem->size() != 3) {
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
const cppbor::Bool* testCredentialItem =
((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
: nullptr);
const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
if (docTypeItem == nullptr || testCredentialItem == nullptr ||
encryptedCredentialKeysItem == nullptr) {
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
docType = docTypeItem->value();
vector<uint8_t> hardwareBoundKey = support::getTestHardwareBoundKey();
const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
const vector<uint8_t> docTypeVec(docType.begin(), docType.end());
optional<vector<uint8_t>> decryptedCredentialKeys =
support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
if (!decryptedCredentialKeys) {
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
if (dckItem == nullptr) {
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Array* dckArrayItem = dckItem->asArray();
if (dckArrayItem == nullptr) {
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
if (dckArrayItem->size() < 2) {
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
storageKey = storageKeyItem->value();
credentialPrivKey = credentialPrivKeyItem->value();
if (dckArrayItem->size() == 3) {
const cppbor::Bstr* sha256PopItem = (*dckArrayItem)[2]->asBstr();
if (sha256PopItem == nullptr) {
return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
}
sha256Pop = sha256PopItem->value();
}
return make_tuple(true, docType, storageKey, credentialPrivKey, sha256Pop);
}
TEST_P(EndToEndTests, createAndRetrieveCredential) {
// First, generate a key-pair for the reader since its public key will be
// part of the request data.
vector<uint8_t> readerKey;
optional<vector<uint8_t>> readerCertificate =
test_utils::generateReaderCertificate("1234", &readerKey);
ASSERT_TRUE(readerCertificate);
// Make the portrait image really big (just shy of 256 KiB) to ensure that
// the chunking code gets exercised.
vector<uint8_t> portraitImage;
test_utils::setImageData(portraitImage);
// Access control profiles:
const vector<test_utils::TestProfile> testProfiles = {// Profile 0 (reader authentication)
{0, readerCertificate.value(), false, 0},
// Profile 1 (no authentication)
{1, {}, false, 0}};
// 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();
// Here's the actual test data:
const vector<test_utils::TestEntryData> testEntries = {
{"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
{"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
{"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
{"PersonalData", "Home address", string("Maida Vale, London, England"),
vector<int32_t>{0}},
{"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}},
};
const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1),
1u};
HardwareInformation hwInfo;
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
string cborPretty;
sp<IWritableIdentityCredential> writableCredential;
identity: Fix attestation and documentation problems. - The docs said that IdentityCredential.createEphemeralKey() returned data encoded PKCS#8 which is wrong. It's supposed to be in DER format which is also what the VTS tests and credstore expects. - Clarify that createEphemeralKeyPair(), setReaderEphemeralPublicKey(), and createAuthChallenge() are all optional. - Avoid passing an invalid profile ID in the IdentityCredentialTests. verifyOneProfileAndEntryPass test. - Update requirements for which tags must be present in the attestation for CredentialKey as well as the requirements on expiration date and the issuer name. Update default implementation to satisfy these requirements. Update VTS tests to carefully verify these requrements are met. - Clarify requirements for X.509 cert for AuthenticationKey. Add VTS test to verify. - Mandate that TAG_IDENTITY_CREDENTIAL_KEY must not be set for test credentials. Add VTS test to verify this. - Make default implementation pretend to be implemented in a trusted environment and streamline VTS tests to not special-case for the default implementation. - Switch to using the attestation extension parser from the KM 4.1 support library instead of the one from system/keymaster. The latter one did not support the latest attestation extension and thus would fail for pretty much anything that wasn't the default HAL impl. - Fix a couple of bugs in keymaster::V4_1::parse_attestation_record(): - Report root_of_trust.security_level - Add support for Tag::IDENTITY_CREDENTIAL_KEY - Fix how EMacKey is calculated. - Add test vectors to verify how EMacKey and DeviceMac is calculated. Test: atest VtsHalIdentityTargetTest Test: atest android.security.identity.cts Bug: 171745570 Change-Id: I2f8bd772de078556733f769cec2021918d1d7de6
2020-10-26 20:26:36 -04:00
ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
true /* testCredential */));
string challenge = "attestationChallenge";
identity: Fix attestation and documentation problems. - The docs said that IdentityCredential.createEphemeralKey() returned data encoded PKCS#8 which is wrong. It's supposed to be in DER format which is also what the VTS tests and credstore expects. - Clarify that createEphemeralKeyPair(), setReaderEphemeralPublicKey(), and createAuthChallenge() are all optional. - Avoid passing an invalid profile ID in the IdentityCredentialTests. verifyOneProfileAndEntryPass test. - Update requirements for which tags must be present in the attestation for CredentialKey as well as the requirements on expiration date and the issuer name. Update default implementation to satisfy these requirements. Update VTS tests to carefully verify these requrements are met. - Clarify requirements for X.509 cert for AuthenticationKey. Add VTS test to verify. - Mandate that TAG_IDENTITY_CREDENTIAL_KEY must not be set for test credentials. Add VTS test to verify this. - Make default implementation pretend to be implemented in a trusted environment and streamline VTS tests to not special-case for the default implementation. - Switch to using the attestation extension parser from the KM 4.1 support library instead of the one from system/keymaster. The latter one did not support the latest attestation extension and thus would fail for pretty much anything that wasn't the default HAL impl. - Fix a couple of bugs in keymaster::V4_1::parse_attestation_record(): - Report root_of_trust.security_level - Add support for Tag::IDENTITY_CREDENTIAL_KEY - Fix how EMacKey is calculated. - Add test vectors to verify how EMacKey and DeviceMac is calculated. Test: atest VtsHalIdentityTargetTest Test: atest android.security.identity.cts Bug: 171745570 Change-Id: I2f8bd772de078556733f769cec2021918d1d7de6
2020-10-26 20:26:36 -04:00
test_utils::AttestationData attData(writableCredential, challenge,
{1} /* atteestationApplicationId */);
ASSERT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
identity: Fix attestation and documentation problems. - The docs said that IdentityCredential.createEphemeralKey() returned data encoded PKCS#8 which is wrong. It's supposed to be in DER format which is also what the VTS tests and credstore expects. - Clarify that createEphemeralKeyPair(), setReaderEphemeralPublicKey(), and createAuthChallenge() are all optional. - Avoid passing an invalid profile ID in the IdentityCredentialTests. verifyOneProfileAndEntryPass test. - Update requirements for which tags must be present in the attestation for CredentialKey as well as the requirements on expiration date and the issuer name. Update default implementation to satisfy these requirements. Update VTS tests to carefully verify these requrements are met. - Clarify requirements for X.509 cert for AuthenticationKey. Add VTS test to verify. - Mandate that TAG_IDENTITY_CREDENTIAL_KEY must not be set for test credentials. Add VTS test to verify this. - Make default implementation pretend to be implemented in a trusted environment and streamline VTS tests to not special-case for the default implementation. - Switch to using the attestation extension parser from the KM 4.1 support library instead of the one from system/keymaster. The latter one did not support the latest attestation extension and thus would fail for pretty much anything that wasn't the default HAL impl. - Fix a couple of bugs in keymaster::V4_1::parse_attestation_record(): - Report root_of_trust.security_level - Add support for Tag::IDENTITY_CREDENTIAL_KEY - Fix how EMacKey is calculated. - Add test vectors to verify how EMacKey and DeviceMac is calculated. Test: atest VtsHalIdentityTargetTest Test: atest android.security.identity.cts Bug: 171745570 Change-Id: I2f8bd772de078556733f769cec2021918d1d7de6
2020-10-26 20:26:36 -04:00
validateAttestationCertificate(attData.attestationCertificate, attData.attestationChallenge,
attData.attestationApplicationId, true);
// This is kinda of a hack but we need to give the size of
// ProofOfProvisioning that we'll expect to receive.
const int32_t expectedProofOfProvisioningSize = 262861 - 326 + readerCertificate.value().size();
// OK to fail, not available in v1 HAL
writableCredential->setExpectedProofOfProvisioningSize(expectedProofOfProvisioningSize);
ASSERT_TRUE(
writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
.isOk());
optional<vector<SecureAccessControlProfile>> secureProfiles =
test_utils::addAccessControlProfiles(writableCredential, testProfiles);
ASSERT_TRUE(secureProfiles);
// Uses TestEntryData* pointer as key and values are the encrypted blobs. This
// is a little hacky but it works well enough.
map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
for (const auto& entry : testEntries) {
ASSERT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize,
encryptedBlobs, true));
}
vector<uint8_t> credentialData;
vector<uint8_t> proofOfProvisioningSignature;
ASSERT_TRUE(
writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
.isOk());
// Validate the proofOfProvisioning which was returned
optional<vector<uint8_t>> proofOfProvisioning =
support::coseSignGetPayload(proofOfProvisioningSignature);
ASSERT_TRUE(proofOfProvisioning);
cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
EXPECT_EQ(
"[\n"
" 'ProofOfProvisioning',\n"
" 'org.iso.18013-5.2019.mdl',\n"
" [\n"
" {\n"
" 'id' : 0,\n"
" 'readerCertificate' : <not printed>,\n"
" },\n"
" {\n"
" 'id' : 1,\n"
" },\n"
" ],\n"
" {\n"
" 'PersonalData' : [\n"
" {\n"
" 'name' : 'Last name',\n"
" 'value' : 'Turing',\n"
" 'accessControlProfiles' : [0, 1, ],\n"
" },\n"
" {\n"
" 'name' : 'Birth date',\n"
" 'value' : '19120623',\n"
" 'accessControlProfiles' : [0, 1, ],\n"
" },\n"
" {\n"
" 'name' : 'First name',\n"
" 'value' : 'Alan',\n"
" 'accessControlProfiles' : [0, 1, ],\n"
" },\n"
" {\n"
" 'name' : 'Home address',\n"
" 'value' : 'Maida Vale, London, England',\n"
" 'accessControlProfiles' : [0, ],\n"
" },\n"
" ],\n"
" 'Image' : [\n"
" {\n"
" 'name' : 'Portrait image',\n"
" 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
" 'accessControlProfiles' : [0, 1, ],\n"
" },\n"
" ],\n"
" },\n"
" true,\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()));
writableCredential = nullptr;
// Extract doctype, storage key, and credentialPrivKey from credentialData... this works
// only because we asked for a test-credential meaning that the HBK is all zeroes.
auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey, exSha256Pop] =
extractFromTestCredentialData(credentialData);
ASSERT_TRUE(exSuccess);
ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
// ... check that the public key derived from the private key matches what was
// in the certificate.
optional<vector<uint8_t>> exCredentialKeyPair =
support::ecPrivateKeyToKeyPair(exCredentialPrivKey);
ASSERT_TRUE(exCredentialKeyPair);
optional<vector<uint8_t>> exCredentialPubKey =
support::ecKeyPairGetPublicKey(exCredentialKeyPair.value());
ASSERT_TRUE(exCredentialPubKey);
ASSERT_EQ(exCredentialPubKey.value(), credentialPubKey.value());
// Starting with API version 3 (feature version 202101) we require SHA-256(ProofOfProvisioning)
// to be in CredentialKeys (which is stored encrypted in CredentialData). Check
// that it's there with the expected value.
if (halApiVersion_ >= 3) {
ASSERT_EQ(exSha256Pop, support::sha256(proofOfProvisioning.value()));
}
// Now that the credential has been provisioned, read it back and check the
// correct data is returned.
sp<IIdentityCredential> credential;
ASSERT_TRUE(credentialStore_
->getCredential(
CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
credentialData, &credential)
.isOk());
ASSERT_NE(credential, nullptr);
optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
ASSERT_TRUE(readerEphemeralKeyPair);
optional<vector<uint8_t>> readerEphemeralPublicKey =
support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk());
vector<uint8_t> ephemeralKeyPair;
ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk());
optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
// Calculate requestData field and sign it with the reader key.
auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.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> sessionTranscriptEncoded = sessionTranscript.encode();
vector<uint8_t> itemsRequestBytes =
cppbor::Map("nameSpaces",
cppbor::Map()
.add("PersonalData", cppbor::Map()
.add("Last name", false)
.add("Birth date", false)
.add("First name", false)
.add("Home address", true))
.add("Image", cppbor::Map().add("Portrait image", false)))
.encode();
cborPretty = cppbor::prettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
EXPECT_EQ(
"{\n"
" 'nameSpaces' : {\n"
" 'PersonalData' : {\n"
" 'Last name' : false,\n"
" 'Birth date' : false,\n"
" 'First name' : false,\n"
" 'Home address' : true,\n"
" },\n"
" 'Image' : {\n"
" 'Portrait image' : false,\n"
" },\n"
" },\n"
"}",
cborPretty);
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(readerKey, {}, // content
encodedReaderAuthenticationBytes, // detached content
readerCertificate.value());
ASSERT_TRUE(readerSignature);
// Generate the key that will be used to sign AuthenticatedData.
vector<uint8_t> signingKeyBlob;
Certificate signingKeyCertificate;
ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
optional<vector<uint8_t>> signingPubKey =
support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
EXPECT_TRUE(signingPubKey);
identity: Fix attestation and documentation problems. - The docs said that IdentityCredential.createEphemeralKey() returned data encoded PKCS#8 which is wrong. It's supposed to be in DER format which is also what the VTS tests and credstore expects. - Clarify that createEphemeralKeyPair(), setReaderEphemeralPublicKey(), and createAuthChallenge() are all optional. - Avoid passing an invalid profile ID in the IdentityCredentialTests. verifyOneProfileAndEntryPass test. - Update requirements for which tags must be present in the attestation for CredentialKey as well as the requirements on expiration date and the issuer name. Update default implementation to satisfy these requirements. Update VTS tests to carefully verify these requrements are met. - Clarify requirements for X.509 cert for AuthenticationKey. Add VTS test to verify. - Mandate that TAG_IDENTITY_CREDENTIAL_KEY must not be set for test credentials. Add VTS test to verify this. - Make default implementation pretend to be implemented in a trusted environment and streamline VTS tests to not special-case for the default implementation. - Switch to using the attestation extension parser from the KM 4.1 support library instead of the one from system/keymaster. The latter one did not support the latest attestation extension and thus would fail for pretty much anything that wasn't the default HAL impl. - Fix a couple of bugs in keymaster::V4_1::parse_attestation_record(): - Report root_of_trust.security_level - Add support for Tag::IDENTITY_CREDENTIAL_KEY - Fix how EMacKey is calculated. - Add test vectors to verify how EMacKey and DeviceMac is calculated. Test: atest VtsHalIdentityTargetTest Test: atest android.security.identity.cts Bug: 171745570 Change-Id: I2f8bd772de078556733f769cec2021918d1d7de6
2020-10-26 20:26:36 -04:00
test_utils::verifyAuthKeyCertificate(signingKeyCertificate.encodedCertificate);
// Since we're using a test-credential we know storageKey meaning we can get the
// private key. Do this, derive the public key from it, and check this matches what
// is in the certificate...
const vector<uint8_t> exDocTypeVec(exDocType.begin(), exDocType.end());
optional<vector<uint8_t>> exSigningPrivKey =
support::decryptAes128Gcm(exStorageKey, signingKeyBlob, exDocTypeVec);
ASSERT_TRUE(exSigningPrivKey);
optional<vector<uint8_t>> exSigningKeyPair =
support::ecPrivateKeyToKeyPair(exSigningPrivKey.value());
ASSERT_TRUE(exSigningKeyPair);
optional<vector<uint8_t>> exSigningPubKey =
support::ecKeyPairGetPublicKey(exSigningKeyPair.value());
ASSERT_TRUE(exSigningPubKey);
ASSERT_EQ(exSigningPubKey.value(), signingPubKey.value());
vector<RequestNamespace> requestedNamespaces = test_utils::buildRequestNamespaces(testEntries);
// OK to fail, not available in v1 HAL
credential->setRequestedNamespaces(requestedNamespaces);
// OK to fail, not available in v1 HAL
credential->setVerificationToken(verificationToken);
ASSERT_TRUE(credential
->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes,
signingKeyBlob, sessionTranscriptEncoded,
readerSignature.value(), testEntriesEntryCounts)
.isOk());
for (const auto& entry : testEntries) {
ASSERT_TRUE(credential
->startRetrieveEntryValue(entry.nameSpace, entry.name,
entry.valueCbor.size(), entry.profileIds)
.isOk());
auto it = encryptedBlobs.find(&entry);
ASSERT_NE(it, encryptedBlobs.end());
const vector<vector<uint8_t>>& encryptedChunks = it->second;
vector<uint8_t> content;
for (const auto& encryptedChunk : encryptedChunks) {
vector<uint8_t> chunk;
ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
content.insert(content.end(), chunk.begin(), chunk.end());
}
EXPECT_EQ(content, entry.valueCbor);
// TODO: also use |exStorageKey| to decrypt data and check it's the same as whatt
// the HAL returns...
}
vector<uint8_t> mac;
vector<uint8_t> ecdsaSignature;
identity: Fix attestation and documentation problems. - The docs said that IdentityCredential.createEphemeralKey() returned data encoded PKCS#8 which is wrong. It's supposed to be in DER format which is also what the VTS tests and credstore expects. - Clarify that createEphemeralKeyPair(), setReaderEphemeralPublicKey(), and createAuthChallenge() are all optional. - Avoid passing an invalid profile ID in the IdentityCredentialTests. verifyOneProfileAndEntryPass test. - Update requirements for which tags must be present in the attestation for CredentialKey as well as the requirements on expiration date and the issuer name. Update default implementation to satisfy these requirements. Update VTS tests to carefully verify these requrements are met. - Clarify requirements for X.509 cert for AuthenticationKey. Add VTS test to verify. - Mandate that TAG_IDENTITY_CREDENTIAL_KEY must not be set for test credentials. Add VTS test to verify this. - Make default implementation pretend to be implemented in a trusted environment and streamline VTS tests to not special-case for the default implementation. - Switch to using the attestation extension parser from the KM 4.1 support library instead of the one from system/keymaster. The latter one did not support the latest attestation extension and thus would fail for pretty much anything that wasn't the default HAL impl. - Fix a couple of bugs in keymaster::V4_1::parse_attestation_record(): - Report root_of_trust.security_level - Add support for Tag::IDENTITY_CREDENTIAL_KEY - Fix how EMacKey is calculated. - Add test vectors to verify how EMacKey and DeviceMac is calculated. Test: atest VtsHalIdentityTargetTest Test: atest android.security.identity.cts Bug: 171745570 Change-Id: I2f8bd772de078556733f769cec2021918d1d7de6
2020-10-26 20:26:36 -04:00
vector<uint8_t> deviceNameSpacesEncoded;
// API version 5 (feature version 202301) returns both MAC and ECDSA signature.
if (halApiVersion_ >= 5) {
ASSERT_TRUE(credential
->finishRetrievalWithSignature(&mac, &deviceNameSpacesEncoded,
&ecdsaSignature)
.isOk());
ASSERT_GT(ecdsaSignature.size(), 0);
} else {
ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
}
cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ(
"{\n"
" 'PersonalData' : {\n"
" 'Last name' : 'Turing',\n"
" 'Birth date' : '19120623',\n"
" 'First name' : 'Alan',\n"
" 'Home address' : 'Maida Vale, London, England',\n"
" },\n"
" 'Image' : {\n"
" 'Portrait image' : <bstr size=262134 "
"sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
" },\n"
"}",
cborPretty);
string docType = "org.iso.18013-5.2019.mdl";
optional<vector<uint8_t>> readerEphemeralPrivateKey =
support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
optional<vector<uint8_t>> eMacKey =
support::calcEMacKey(readerEphemeralPrivateKey.value(), // Private Key
signingPubKey.value(), // Public Key
cppbor::SemanticTag(24, sessionTranscript.encode())
.encode()); // SessionTranscriptBytes
optional<vector<uint8_t>> calculatedMac =
identity: Fix attestation and documentation problems. - The docs said that IdentityCredential.createEphemeralKey() returned data encoded PKCS#8 which is wrong. It's supposed to be in DER format which is also what the VTS tests and credstore expects. - Clarify that createEphemeralKeyPair(), setReaderEphemeralPublicKey(), and createAuthChallenge() are all optional. - Avoid passing an invalid profile ID in the IdentityCredentialTests. verifyOneProfileAndEntryPass test. - Update requirements for which tags must be present in the attestation for CredentialKey as well as the requirements on expiration date and the issuer name. Update default implementation to satisfy these requirements. Update VTS tests to carefully verify these requrements are met. - Clarify requirements for X.509 cert for AuthenticationKey. Add VTS test to verify. - Mandate that TAG_IDENTITY_CREDENTIAL_KEY must not be set for test credentials. Add VTS test to verify this. - Make default implementation pretend to be implemented in a trusted environment and streamline VTS tests to not special-case for the default implementation. - Switch to using the attestation extension parser from the KM 4.1 support library instead of the one from system/keymaster. The latter one did not support the latest attestation extension and thus would fail for pretty much anything that wasn't the default HAL impl. - Fix a couple of bugs in keymaster::V4_1::parse_attestation_record(): - Report root_of_trust.security_level - Add support for Tag::IDENTITY_CREDENTIAL_KEY - Fix how EMacKey is calculated. - Add test vectors to verify how EMacKey and DeviceMac is calculated. Test: atest VtsHalIdentityTargetTest Test: atest android.security.identity.cts Bug: 171745570 Change-Id: I2f8bd772de078556733f769cec2021918d1d7de6
2020-10-26 20:26:36 -04:00
support::calcMac(sessionTranscript.encode(), // SessionTranscript
docType, // DocType
deviceNameSpacesEncoded, // DeviceNamespaces
eMacKey.value()); // EMacKey
ASSERT_TRUE(calculatedMac);
EXPECT_EQ(mac, calculatedMac);
if (ecdsaSignature.size() > 0) {
vector<uint8_t> encodedDeviceAuthentication =
cppbor::Array()
.add("DeviceAuthentication")
.add(sessionTranscript.clone())
.add(docType)
.add(cppbor::SemanticTag(24, deviceNameSpacesEncoded))
.encode();
vector<uint8_t> deviceAuthenticationBytes =
cppbor::SemanticTag(24, encodedDeviceAuthentication).encode();
EXPECT_TRUE(support::coseCheckEcDsaSignature(ecdsaSignature,
deviceAuthenticationBytes, // Detached content
signingPubKey.value()));
}
// Also perform an additional empty request. This is what mDL applications
// are envisioned to do - one call to get the data elements, another to get
// an empty DeviceSignedItems and corresponding MAC.
//
credential->setRequestedNamespaces({}); // OK to fail, not available in v1 HAL
ASSERT_TRUE(credential
->startRetrieval(
secureProfiles.value(), authToken, {}, // itemsRequestBytes
signingKeyBlob, sessionTranscriptEncoded, {}, // readerSignature,
testEntriesEntryCounts)
.isOk());
// API version 5 (feature version 202301) returns both MAC and ECDSA signature.
if (halApiVersion_ >= 5) {
ASSERT_TRUE(credential
->finishRetrievalWithSignature(&mac, &deviceNameSpacesEncoded,
&ecdsaSignature)
.isOk());
ASSERT_GT(ecdsaSignature.size(), 0);
} else {
ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
}
cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ("{}", cborPretty);
// Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
identity: Fix attestation and documentation problems. - The docs said that IdentityCredential.createEphemeralKey() returned data encoded PKCS#8 which is wrong. It's supposed to be in DER format which is also what the VTS tests and credstore expects. - Clarify that createEphemeralKeyPair(), setReaderEphemeralPublicKey(), and createAuthChallenge() are all optional. - Avoid passing an invalid profile ID in the IdentityCredentialTests. verifyOneProfileAndEntryPass test. - Update requirements for which tags must be present in the attestation for CredentialKey as well as the requirements on expiration date and the issuer name. Update default implementation to satisfy these requirements. Update VTS tests to carefully verify these requrements are met. - Clarify requirements for X.509 cert for AuthenticationKey. Add VTS test to verify. - Mandate that TAG_IDENTITY_CREDENTIAL_KEY must not be set for test credentials. Add VTS test to verify this. - Make default implementation pretend to be implemented in a trusted environment and streamline VTS tests to not special-case for the default implementation. - Switch to using the attestation extension parser from the KM 4.1 support library instead of the one from system/keymaster. The latter one did not support the latest attestation extension and thus would fail for pretty much anything that wasn't the default HAL impl. - Fix a couple of bugs in keymaster::V4_1::parse_attestation_record(): - Report root_of_trust.security_level - Add support for Tag::IDENTITY_CREDENTIAL_KEY - Fix how EMacKey is calculated. - Add test vectors to verify how EMacKey and DeviceMac is calculated. Test: atest VtsHalIdentityTargetTest Test: atest android.security.identity.cts Bug: 171745570 Change-Id: I2f8bd772de078556733f769cec2021918d1d7de6
2020-10-26 20:26:36 -04:00
calculatedMac = support::calcMac(sessionTranscript.encode(), // SessionTranscript
docType, // DocType
deviceNameSpacesEncoded, // DeviceNamespaces
eMacKey.value()); // EMacKey
ASSERT_TRUE(calculatedMac);
EXPECT_EQ(mac, calculatedMac);
if (ecdsaSignature.size() > 0) {
vector<uint8_t> encodedDeviceAuthentication =
cppbor::Array()
.add("DeviceAuthentication")
.add(sessionTranscript.clone())
.add(docType)
.add(cppbor::SemanticTag(24, deviceNameSpacesEncoded))
.encode();
vector<uint8_t> deviceAuthenticationBytes =
cppbor::SemanticTag(24, encodedDeviceAuthentication).encode();
EXPECT_TRUE(support::coseCheckEcDsaSignature(ecdsaSignature,
deviceAuthenticationBytes, // Detached content
signingPubKey.value()));
}
// Some mDL apps might send a request but with a single empty
// namespace. Check that too.
RequestNamespace emptyRequestNS;
emptyRequestNS.namespaceName = "PersonalData";
credential->setRequestedNamespaces({emptyRequestNS}); // OK to fail, not available in v1 HAL
ASSERT_TRUE(credential
->startRetrieval(
secureProfiles.value(), authToken, {}, // itemsRequestBytes
signingKeyBlob, sessionTranscriptEncoded, {}, // readerSignature,
testEntriesEntryCounts)
.isOk());
// API version 5 (feature version 202301) returns both MAC and ECDSA signature.
if (halApiVersion_ >= 5) {
ASSERT_TRUE(credential
->finishRetrievalWithSignature(&mac, &deviceNameSpacesEncoded,
&ecdsaSignature)
.isOk());
ASSERT_GT(ecdsaSignature.size(), 0);
} else {
ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
}
cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ("{}", cborPretty);
// Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
identity: Fix attestation and documentation problems. - The docs said that IdentityCredential.createEphemeralKey() returned data encoded PKCS#8 which is wrong. It's supposed to be in DER format which is also what the VTS tests and credstore expects. - Clarify that createEphemeralKeyPair(), setReaderEphemeralPublicKey(), and createAuthChallenge() are all optional. - Avoid passing an invalid profile ID in the IdentityCredentialTests. verifyOneProfileAndEntryPass test. - Update requirements for which tags must be present in the attestation for CredentialKey as well as the requirements on expiration date and the issuer name. Update default implementation to satisfy these requirements. Update VTS tests to carefully verify these requrements are met. - Clarify requirements for X.509 cert for AuthenticationKey. Add VTS test to verify. - Mandate that TAG_IDENTITY_CREDENTIAL_KEY must not be set for test credentials. Add VTS test to verify this. - Make default implementation pretend to be implemented in a trusted environment and streamline VTS tests to not special-case for the default implementation. - Switch to using the attestation extension parser from the KM 4.1 support library instead of the one from system/keymaster. The latter one did not support the latest attestation extension and thus would fail for pretty much anything that wasn't the default HAL impl. - Fix a couple of bugs in keymaster::V4_1::parse_attestation_record(): - Report root_of_trust.security_level - Add support for Tag::IDENTITY_CREDENTIAL_KEY - Fix how EMacKey is calculated. - Add test vectors to verify how EMacKey and DeviceMac is calculated. Test: atest VtsHalIdentityTargetTest Test: atest android.security.identity.cts Bug: 171745570 Change-Id: I2f8bd772de078556733f769cec2021918d1d7de6
2020-10-26 20:26:36 -04:00
calculatedMac = support::calcMac(sessionTranscript.encode(), // SessionTranscript
docType, // DocType
deviceNameSpacesEncoded, // DeviceNamespaces
eMacKey.value()); // EMacKey
ASSERT_TRUE(calculatedMac);
EXPECT_EQ(mac, calculatedMac);
if (ecdsaSignature.size() > 0) {
vector<uint8_t> encodedDeviceAuthentication =
cppbor::Array()
.add("DeviceAuthentication")
.add(sessionTranscript.clone())
.add(docType)
.add(cppbor::SemanticTag(24, deviceNameSpacesEncoded))
.encode();
vector<uint8_t> deviceAuthenticationBytes =
cppbor::SemanticTag(24, encodedDeviceAuthentication).encode();
EXPECT_TRUE(support::coseCheckEcDsaSignature(ecdsaSignature,
deviceAuthenticationBytes, // Detached content
signingPubKey.value()));
}
}
TEST_P(EndToEndTests, noSessionEncryption) {
if (halApiVersion_ < 5) {
GTEST_SKIP() << "Need HAL API version 5, have " << halApiVersion_;
}
const vector<test_utils::TestProfile> testProfiles = {// Profile 0 (no authentication)
{0, {}, false, 0}};
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();
// Here's the actual test data:
const vector<test_utils::TestEntryData> testEntries = {
{"PersonalData", "Last name", string("Turing"), vector<int32_t>{0}},
{"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0}},
{"PersonalData", "First name", string("Alan"), vector<int32_t>{0}},
};
const vector<int32_t> testEntriesEntryCounts = {3};
HardwareInformation hwInfo;
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
string cborPretty;
sp<IWritableIdentityCredential> writableCredential;
ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
true /* testCredential */));
string challenge = "attestationChallenge";
test_utils::AttestationData attData(writableCredential, challenge,
{1} /* atteestationApplicationId */);
ASSERT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
// This is kinda of a hack but we need to give the size of
// ProofOfProvisioning that we'll expect to receive.
const int32_t expectedProofOfProvisioningSize = 230;
// OK to fail, not available in v1 HAL
writableCredential->setExpectedProofOfProvisioningSize(expectedProofOfProvisioningSize);
ASSERT_TRUE(
writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
.isOk());
optional<vector<SecureAccessControlProfile>> secureProfiles =
test_utils::addAccessControlProfiles(writableCredential, testProfiles);
ASSERT_TRUE(secureProfiles);
// Uses TestEntryData* pointer as key and values are the encrypted blobs. This
// is a little hacky but it works well enough.
map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
for (const auto& entry : testEntries) {
ASSERT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize,
encryptedBlobs, true));
}
vector<uint8_t> credentialData;
vector<uint8_t> proofOfProvisioningSignature;
ASSERT_TRUE(
writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
.isOk());
// Validate the proofOfProvisioning which was returned
optional<vector<uint8_t>> proofOfProvisioning =
support::coseSignGetPayload(proofOfProvisioningSignature);
ASSERT_TRUE(proofOfProvisioning);
cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
EXPECT_EQ(
"[\n"
" 'ProofOfProvisioning',\n"
" 'org.iso.18013-5.2019.mdl',\n"
" [\n"
" {\n"
" 'id' : 0,\n"
" },\n"
" ],\n"
" {\n"
" 'PersonalData' : [\n"
" {\n"
" 'name' : 'Last name',\n"
" 'value' : 'Turing',\n"
" 'accessControlProfiles' : [0, ],\n"
" },\n"
" {\n"
" 'name' : 'Birth date',\n"
" 'value' : '19120623',\n"
" 'accessControlProfiles' : [0, ],\n"
" },\n"
" {\n"
" 'name' : 'First name',\n"
" 'value' : 'Alan',\n"
" 'accessControlProfiles' : [0, ],\n"
" },\n"
" ],\n"
" },\n"
" true,\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()));
writableCredential = nullptr;
// Extract doctype, storage key, and credentialPrivKey from credentialData... this works
// only because we asked for a test-credential meaning that the HBK is all zeroes.
auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey, exSha256Pop] =
extractFromTestCredentialData(credentialData);
ASSERT_TRUE(exSuccess);
ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
// ... check that the public key derived from the private key matches what was
// in the certificate.
optional<vector<uint8_t>> exCredentialKeyPair =
support::ecPrivateKeyToKeyPair(exCredentialPrivKey);
ASSERT_TRUE(exCredentialKeyPair);
optional<vector<uint8_t>> exCredentialPubKey =
support::ecKeyPairGetPublicKey(exCredentialKeyPair.value());
ASSERT_TRUE(exCredentialPubKey);
ASSERT_EQ(exCredentialPubKey.value(), credentialPubKey.value());
sp<IIdentityCredential> credential;
ASSERT_TRUE(credentialStore_
->getCredential(
CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
credentialData, &credential)
.isOk());
ASSERT_NE(credential, nullptr);
// Calculate sessionTranscript, make something that resembles what you'd use for
// an over-the-Internet presentation not using mdoc session encryption.
cppbor::Array sessionTranscript =
cppbor::Array()
.add(cppbor::Null()) // DeviceEngagementBytes isn't used.
.add(cppbor::Null()) // EReaderKeyBytes isn't used.
.add(cppbor::Array() // Proprietary handover structure follows.
.add(cppbor::Tstr("TestHandover"))
.add(cppbor::Bstr(vector<uint8_t>{1, 2, 3}))
.add(cppbor::Bstr(vector<uint8_t>{9, 8, 7, 6})));
vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
// Generate the key that will be used to sign AuthenticatedData.
vector<uint8_t> signingKeyBlob;
Certificate signingKeyCertificate;
ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
optional<vector<uint8_t>> signingPubKey =
support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
EXPECT_TRUE(signingPubKey);
test_utils::verifyAuthKeyCertificate(signingKeyCertificate.encodedCertificate);
vector<RequestNamespace> requestedNamespaces = test_utils::buildRequestNamespaces(testEntries);
ASSERT_TRUE(credential->setRequestedNamespaces(requestedNamespaces).isOk());
ASSERT_TRUE(credential->setVerificationToken(verificationToken).isOk());
Status status = credential->startRetrieval(
secureProfiles.value(), authToken, {} /* itemsRequestBytes*/, signingKeyBlob,
sessionTranscriptEncoded, {} /* readerSignature */, testEntriesEntryCounts);
ASSERT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
for (const auto& entry : testEntries) {
ASSERT_TRUE(credential
->startRetrieveEntryValue(entry.nameSpace, entry.name,
entry.valueCbor.size(), entry.profileIds)
.isOk());
auto it = encryptedBlobs.find(&entry);
ASSERT_NE(it, encryptedBlobs.end());
const vector<vector<uint8_t>>& encryptedChunks = it->second;
vector<uint8_t> content;
for (const auto& encryptedChunk : encryptedChunks) {
vector<uint8_t> chunk;
ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
content.insert(content.end(), chunk.begin(), chunk.end());
}
EXPECT_EQ(content, entry.valueCbor);
}
vector<uint8_t> mac;
vector<uint8_t> ecdsaSignature;
vector<uint8_t> deviceNameSpacesEncoded;
status = credential->finishRetrievalWithSignature(&mac, &deviceNameSpacesEncoded,
&ecdsaSignature);
ASSERT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
// MACing should NOT work since we're not using session encryption
ASSERT_EQ(0, mac.size());
// ECDSA signatures should work, however. Check this.
ASSERT_GT(ecdsaSignature.size(), 0);
cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ(
"{\n"
" 'PersonalData' : {\n"
" 'Last name' : 'Turing',\n"
" 'Birth date' : '19120623',\n"
" 'First name' : 'Alan',\n"
" },\n"
"}",
cborPretty);
string docType = "org.iso.18013-5.2019.mdl";
vector<uint8_t> encodedDeviceAuthentication =
cppbor::Array()
.add("DeviceAuthentication")
.add(sessionTranscript.clone())
.add(docType)
.add(cppbor::SemanticTag(24, deviceNameSpacesEncoded))
.encode();
vector<uint8_t> deviceAuthenticationBytes =
cppbor::SemanticTag(24, encodedDeviceAuthentication).encode();
EXPECT_TRUE(support::coseCheckEcDsaSignature(ecdsaSignature,
deviceAuthenticationBytes, // Detached content
signingPubKey.value()));
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EndToEndTests);
INSTANTIATE_TEST_SUITE_P(
Identity, EndToEndTests,
testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
android::PrintInstanceNameToString);
} // namespace android::hardware::identity
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
::android::ProcessState::self()->startThreadPool();
return RUN_ALL_TESTS();
}