KeyMint VTS: improve attestation tests

Check that the various ATTESTATION_ID_* tags are included if they
have the correct value, and that keygen fails if they have an invalid
value.

Also update attestation tags to include vendor/boot patchlevel if
they're available. (They always should be, but fixing that is a
separate task.)

Bug: 190757200
Test: VtsAidlKeyMintTargetTest
Change-Id: Ibaed7364c6d08c0982e2a9fb6cb864ae42cf39fe
This commit is contained in:
David Drysdale 2021-06-14 14:46:02 +01:00
parent 79309cf1a2
commit 03346e175e
3 changed files with 288 additions and 27 deletions

View file

@ -556,7 +556,7 @@ TEST_P(AttestKeyTest, AllEcCurves) {
.EcdsaSigningKey(curve)
.AttestKey()
.SetDefaultValidity(),
{} /* attestation siging key */, &attest_key.keyBlob,
{} /* attestation signing key */, &attest_key.keyBlob,
&attest_key_characteristics, &attest_key_cert_chain));
ASSERT_GT(attest_key_cert_chain.size(), 0);
@ -640,7 +640,7 @@ TEST_P(AttestKeyTest, AttestWithNonAttestKey) {
ErrorCode::OK,
GenerateKey(
AuthorizationSetBuilder().EcdsaSigningKey(EcCurve::P_256).SetDefaultValidity(),
{} /* attestation siging key */, &non_attest_key.keyBlob,
{} /* attestation signing key */, &non_attest_key.keyBlob,
&non_attest_key_characteristics, &non_attest_key_cert_chain));
ASSERT_GT(non_attest_key_cert_chain.size(), 0);
@ -662,6 +662,124 @@ TEST_P(AttestKeyTest, AttestWithNonAttestKey) {
&attested_key_cert_chain));
}
TEST_P(AttestKeyTest, EcdsaAttestationID) {
// Create attestation key.
AttestationKey attest_key;
vector<KeyCharacteristics> attest_key_characteristics;
vector<Certificate> attest_key_cert_chain;
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
.EcdsaSigningKey(EcCurve::P_256)
.AttestKey()
.SetDefaultValidity(),
{} /* attestation signing key */, &attest_key.keyBlob,
&attest_key_characteristics, &attest_key_cert_chain));
attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
ASSERT_GT(attest_key_cert_chain.size(), 0);
EXPECT_EQ(attest_key_cert_chain.size(), 1);
EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain));
// Collection of valid attestation ID tags.
auto attestation_id_tags = AuthorizationSetBuilder();
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
"ro.product.manufacturer");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
for (const KeyParameter& tag : attestation_id_tags) {
SCOPED_TRACE(testing::Message() << "+tag-" << tag);
// Use attestation key to sign an ECDSA key, but include an attestation ID field.
AuthorizationSetBuilder builder = AuthorizationSetBuilder()
.EcdsaSigningKey(EcCurve::P_256)
.Authorization(TAG_NO_AUTH_REQUIRED)
.AttestationChallenge("challenge")
.AttestationApplicationId("foo")
.SetDefaultValidity();
builder.push_back(tag);
vector<uint8_t> attested_key_blob;
vector<KeyCharacteristics> attested_key_characteristics;
vector<Certificate> attested_key_cert_chain;
auto result = GenerateKey(builder, attest_key, &attested_key_blob,
&attested_key_characteristics, &attested_key_cert_chain);
if (result == ErrorCode::CANNOT_ATTEST_IDS) {
continue;
}
ASSERT_EQ(result, ErrorCode::OK);
CheckedDeleteKey(&attested_key_blob);
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
// The attested key characteristics will not contain APPLICATION_ID_* fields (their
// spec definitions all have "Must never appear in KeyCharacteristics"), but the
// attestation extension should contain them, so make sure the extra tag is added.
hw_enforced.push_back(tag);
EXPECT_TRUE(verify_attestation_record("challenge", "foo", sw_enforced, hw_enforced,
SecLevel(),
attested_key_cert_chain[0].encodedCertificate));
}
CheckedDeleteKey(&attest_key.keyBlob);
}
TEST_P(AttestKeyTest, EcdsaAttestationMismatchID) {
// Create attestation key.
AttestationKey attest_key;
vector<KeyCharacteristics> attest_key_characteristics;
vector<Certificate> attest_key_cert_chain;
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
.EcdsaSigningKey(EcCurve::P_256)
.AttestKey()
.SetDefaultValidity(),
{} /* attestation signing key */, &attest_key.keyBlob,
&attest_key_characteristics, &attest_key_cert_chain));
attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
ASSERT_GT(attest_key_cert_chain.size(), 0);
EXPECT_EQ(attest_key_cert_chain.size(), 1);
EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain));
// Collection of invalid attestation ID tags.
auto attestation_id_tags =
AuthorizationSetBuilder()
.Authorization(TAG_ATTESTATION_ID_BRAND, "bogus-brand")
.Authorization(TAG_ATTESTATION_ID_DEVICE, "devious-device")
.Authorization(TAG_ATTESTATION_ID_PRODUCT, "punctured-product")
.Authorization(TAG_ATTESTATION_ID_SERIAL, "suspicious-serial")
.Authorization(TAG_ATTESTATION_ID_IMEI, "invalid-imei")
.Authorization(TAG_ATTESTATION_ID_MEID, "mismatching-meid")
.Authorization(TAG_ATTESTATION_ID_MANUFACTURER, "malformed-manufacturer")
.Authorization(TAG_ATTESTATION_ID_MODEL, "malicious-model");
vector<uint8_t> key_blob;
vector<KeyCharacteristics> key_characteristics;
for (const KeyParameter& invalid_tag : attestation_id_tags) {
SCOPED_TRACE(testing::Message() << "+tag-" << invalid_tag);
// Use attestation key to sign an ECDSA key, but include an invalid
// attestation ID field.
AuthorizationSetBuilder builder = AuthorizationSetBuilder()
.EcdsaSigningKey(EcCurve::P_256)
.Authorization(TAG_NO_AUTH_REQUIRED)
.AttestationChallenge("challenge")
.AttestationApplicationId("foo")
.SetDefaultValidity();
builder.push_back(invalid_tag);
vector<uint8_t> attested_key_blob;
vector<KeyCharacteristics> attested_key_characteristics;
vector<Certificate> attested_key_cert_chain;
auto result = GenerateKey(builder, attest_key, &attested_key_blob,
&attested_key_characteristics, &attested_key_cert_chain);
ASSERT_TRUE(result == ErrorCode::CANNOT_ATTEST_IDS || result == ErrorCode::INVALID_TAG)
<< "result = " << result;
}
CheckedDeleteKey(&attest_key.keyBlob);
}
INSTANTIATE_KEYMINT_AIDL_TEST(AttestKeyTest);
} // namespace aidl::android::hardware::security::keymint::test

View file

@ -29,7 +29,7 @@ class DeviceUniqueAttestationTest : public KeyMintAidlTestBase {
protected:
void CheckUniqueAttestationResults(const vector<uint8_t>& key_blob,
const vector<KeyCharacteristics>& key_characteristics,
const AuthorizationSet& hw_enforced, int key_size) {
const AuthorizationSet& hw_enforced) {
ASSERT_GT(cert_chain_.size(), 0);
if (KeyMintAidlTestBase::dump_Attestations) {
@ -40,8 +40,6 @@ class DeviceUniqueAttestationTest : public KeyMintAidlTestBase {
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size)) << "Key size missing";
// The device-unique attestation chain should contain exactly two certificates:
// * The leaf with the attestation extension.
// * A self-signed root, signed using the device-unique key.
@ -136,7 +134,8 @@ TEST_P(DeviceUniqueAttestationTest, RsaDeviceUniqueAttestation) {
ASSERT_EQ(ErrorCode::OK, result);
AuthorizationSet hw_enforced = AuthorizationSetBuilder()
AuthorizationSetBuilder hw_enforced =
AuthorizationSetBuilder()
.Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
.Authorization(TAG_NO_AUTH_REQUIRED)
.RsaSigningKey(2048, 65537)
@ -146,7 +145,21 @@ TEST_P(DeviceUniqueAttestationTest, RsaDeviceUniqueAttestation) {
.Authorization(TAG_OS_VERSION, os_version())
.Authorization(TAG_OS_PATCHLEVEL, os_patch_level());
CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced, key_size);
// Any patchlevels attached to the key should also be present in the attestation extension.
AuthorizationSet auths;
for (const auto& entry : key_characteristics) {
auths.push_back(AuthorizationSet(entry.authorizations));
}
auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL);
if (vendor_pl) {
hw_enforced.Authorization(TAG_VENDOR_PATCHLEVEL, *vendor_pl);
}
auto boot_pl = auths.GetTagValue(TAG_BOOT_PATCHLEVEL);
if (boot_pl) {
hw_enforced.Authorization(TAG_BOOT_PATCHLEVEL, *boot_pl);
}
CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced);
}
/*
@ -160,11 +173,10 @@ TEST_P(DeviceUniqueAttestationTest, EcdsaDeviceUniqueAttestation) {
vector<uint8_t> key_blob;
vector<KeyCharacteristics> key_characteristics;
int key_size = 256;
auto result = GenerateKey(AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.EcdsaSigningKey(256)
.EcdsaSigningKey(EcCurve::P_256)
.Digest(Digest::SHA_2_256)
.Authorization(TAG_INCLUDE_UNIQUE_ID)
.AttestationChallenge("challenge")
@ -176,17 +188,137 @@ TEST_P(DeviceUniqueAttestationTest, EcdsaDeviceUniqueAttestation) {
if (result == ErrorCode::CANNOT_ATTEST_IDS) return;
ASSERT_EQ(ErrorCode::OK, result);
AuthorizationSet hw_enforced = AuthorizationSetBuilder()
.Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
AuthorizationSetBuilder hw_enforced =
AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.EcdsaSigningKey(EcCurve::P_256)
.Digest(Digest::SHA_2_256)
.Authorization(TAG_EC_CURVE, EcCurve::P_256)
.Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
.Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
.Authorization(TAG_OS_VERSION, os_version())
.Authorization(TAG_OS_PATCHLEVEL, os_patch_level());
// Any patchlevels attached to the key should also be present in the attestation extension.
AuthorizationSet auths;
for (const auto& entry : key_characteristics) {
auths.push_back(AuthorizationSet(entry.authorizations));
}
auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL);
if (vendor_pl) {
hw_enforced.Authorization(TAG_VENDOR_PATCHLEVEL, *vendor_pl);
}
auto boot_pl = auths.GetTagValue(TAG_BOOT_PATCHLEVEL);
if (boot_pl) {
hw_enforced.Authorization(TAG_BOOT_PATCHLEVEL, *boot_pl);
}
CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced, key_size);
CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced);
}
/*
* DeviceUniqueAttestationTest.EcdsaDeviceUniqueAttestationID
*
* Verifies that device unique attestation can include IDs that do match the
* local device.
*/
TEST_P(DeviceUniqueAttestationTest, EcdsaDeviceUniqueAttestationID) {
if (SecLevel() != SecurityLevel::STRONGBOX) return;
// Collection of valid attestation ID tags.
auto attestation_id_tags = AuthorizationSetBuilder();
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
"ro.product.manufacturer");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
vector<uint8_t> key_blob;
vector<KeyCharacteristics> key_characteristics;
for (const KeyParameter& tag : attestation_id_tags) {
SCOPED_TRACE(testing::Message() << "+tag-" << tag);
AuthorizationSetBuilder builder = AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.EcdsaSigningKey(EcCurve::P_256)
.Digest(Digest::SHA_2_256)
.Authorization(TAG_INCLUDE_UNIQUE_ID)
.AttestationChallenge("challenge")
.AttestationApplicationId("foo")
.Authorization(TAG_DEVICE_UNIQUE_ATTESTATION);
builder.push_back(tag);
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
// It is optional for Strong box to support DeviceUniqueAttestation.
if (result == ErrorCode::CANNOT_ATTEST_IDS) return;
ASSERT_EQ(ErrorCode::OK, result);
AuthorizationSetBuilder hw_enforced =
AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.EcdsaSigningKey(EcCurve::P_256)
.Digest(Digest::SHA_2_256)
.Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
.Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
.Authorization(TAG_OS_VERSION, os_version())
.Authorization(TAG_OS_PATCHLEVEL, os_patch_level());
// Expect the specified tag to be present in the attestation extension.
hw_enforced.push_back(tag);
// Any patchlevels attached to the key should also be present in the attestation extension.
AuthorizationSet auths;
for (const auto& entry : key_characteristics) {
auths.push_back(AuthorizationSet(entry.authorizations));
}
auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL);
if (vendor_pl) {
hw_enforced.Authorization(TAG_VENDOR_PATCHLEVEL, *vendor_pl);
}
auto boot_pl = auths.GetTagValue(TAG_BOOT_PATCHLEVEL);
if (boot_pl) {
hw_enforced.Authorization(TAG_BOOT_PATCHLEVEL, *boot_pl);
}
CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced);
}
}
/*
* DeviceUniqueAttestationTest.EcdsaDeviceUniqueAttestationMismatchID
*
* Verifies that device unique attestation rejects attempts to attest to IDs that
* don't match the local device.
*/
TEST_P(DeviceUniqueAttestationTest, EcdsaDeviceUniqueAttestationMismatchID) {
if (SecLevel() != SecurityLevel::STRONGBOX) return;
// Collection of invalid attestation ID tags.
auto attestation_id_tags =
AuthorizationSetBuilder()
.Authorization(TAG_ATTESTATION_ID_BRAND, "bogus-brand")
.Authorization(TAG_ATTESTATION_ID_DEVICE, "devious-device")
.Authorization(TAG_ATTESTATION_ID_PRODUCT, "punctured-product")
.Authorization(TAG_ATTESTATION_ID_SERIAL, "suspicious-serial")
.Authorization(TAG_ATTESTATION_ID_IMEI, "invalid-imei")
.Authorization(TAG_ATTESTATION_ID_MEID, "mismatching-meid")
.Authorization(TAG_ATTESTATION_ID_MANUFACTURER, "malformed-manufacturer")
.Authorization(TAG_ATTESTATION_ID_MODEL, "malicious-model");
vector<uint8_t> key_blob;
vector<KeyCharacteristics> key_characteristics;
for (const KeyParameter& invalid_tag : attestation_id_tags) {
SCOPED_TRACE(testing::Message() << "+tag-" << invalid_tag);
AuthorizationSetBuilder builder = AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.EcdsaSigningKey(EcCurve::P_256)
.Digest(Digest::SHA_2_256)
.Authorization(TAG_INCLUDE_UNIQUE_ID)
.AttestationChallenge("challenge")
.AttestationApplicationId("foo")
.Authorization(TAG_DEVICE_UNIQUE_ATTESTATION);
// Add the tag that doesn't match the local device's real ID.
builder.push_back(invalid_tag);
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
ASSERT_TRUE(result == ErrorCode::CANNOT_ATTEST_IDS || result == ErrorCode::INVALID_TAG);
}
}
INSTANTIATE_KEYMINT_AIDL_TEST(DeviceUniqueAttestationTest);

View file

@ -21,6 +21,7 @@
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <android-base/properties.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <gtest/gtest.h>
@ -313,6 +314,16 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam<string> {
long challenge_;
};
// If the given property is available, add it to the tag set under the given tag ID.
template <Tag tag>
void add_tag_from_prop(AuthorizationSetBuilder* tags, TypedTag<TagType::BYTES, tag> ttag,
const char* prop) {
std::string prop_value = ::android::base::GetProperty(prop, /* default= */ "");
if (!prop_value.empty()) {
tags->Authorization(ttag, prop_value.data(), prop_value.size());
}
}
vector<uint8_t> build_serial_blob(const uint64_t serial_int);
void verify_subject(const X509* cert, const string& subject, bool self_signed);
void verify_serial(X509* cert, const uint64_t expected_serial);