Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a676c3b4 authored by David Drysdale's avatar David Drysdale
Browse files

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
Merged-In: Ibaed7364c6d08c0982e2a9fb6cb864ae42cf39fe
Change-Id: Ibaed7364c6d08c0982e2a9fb6cb864ae42cf39fe
parent 98949afb
Loading
Loading
Loading
Loading
+120 −2
Original line number Diff line number Diff line
@@ -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
+159 −27
Original line number Diff line number Diff line
@@ -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()
    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());
    // 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.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_EC_CURVE, EcCurve::P_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);
    }
}

    CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced, key_size);
/*
 * 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);
+11 −0
Original line number Diff line number Diff line
@@ -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);