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

Commit 0e80b5d1 authored by Shawn Willden's avatar Shawn Willden
Browse files

Add basic testing for KeyMint certs.

This is by no means complete, but it validates basic functionality.
More is coming.

Test: VtsAidlKeyMintTargetTest
Change-Id: I0727a9f5b137b58b9a2f0aaf9935bfdc6525df8f
parent 1c15423d
Loading
Loading
Loading
Loading
+55 −1
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ using std::optional;
        os << "(Empty)" << ::std::endl;
    else {
        os << "\n";
        for (size_t i = 0; i < set.size(); ++i) os << set[i] << ::std::endl;
        for (auto& entry : set) os << entry << ::std::endl;
    }
    return os;
}
@@ -131,6 +131,17 @@ ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc,
        *key_blob = std::move(creationResult.keyBlob);
        *key_characteristics = std::move(creationResult.keyCharacteristics);
        cert_chain_ = std::move(creationResult.certificateChain);

        auto algorithm = key_desc.GetTagValue(TAG_ALGORITHM);
        EXPECT_TRUE(algorithm);
        if (algorithm &&
            (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC)) {
            EXPECT_GE(cert_chain_.size(), 1);
            if (key_desc.Contains(TAG_ATTESTATION_CHALLENGE)) EXPECT_GT(cert_chain_.size(), 1);
        } else {
            // For symmetric keys there should be no certificates.
            EXPECT_EQ(cert_chain_.size(), 0);
        }
    }

    return GetReturnErrorCode(result);
@@ -162,6 +173,17 @@ ErrorCode KeyMintAidlTestBase::ImportKey(const AuthorizationSet& key_desc, KeyFo
        *key_blob = std::move(creationResult.keyBlob);
        *key_characteristics = std::move(creationResult.keyCharacteristics);
        cert_chain_ = std::move(creationResult.certificateChain);

        auto algorithm = key_desc.GetTagValue(TAG_ALGORITHM);
        EXPECT_TRUE(algorithm);
        if (algorithm &&
            (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC)) {
            EXPECT_GE(cert_chain_.size(), 1);
            if (key_desc.Contains(TAG_ATTESTATION_CHALLENGE)) EXPECT_GT(cert_chain_.size(), 1);
        } else {
            // For symmetric keys there should be no certificates.
            EXPECT_EQ(cert_chain_.size(), 0);
        }
    }

    return GetReturnErrorCode(result);
@@ -195,6 +217,20 @@ ErrorCode KeyMintAidlTestBase::ImportWrappedKey(string wrapped_key, string wrapp
        key_blob_ = std::move(creationResult.keyBlob);
        key_characteristics_ = std::move(creationResult.keyCharacteristics);
        cert_chain_ = std::move(creationResult.certificateChain);

        AuthorizationSet allAuths;
        for (auto& entry : key_characteristics_) {
            allAuths.push_back(AuthorizationSet(entry.authorizations));
        }
        auto algorithm = allAuths.GetTagValue(TAG_ALGORITHM);
        EXPECT_TRUE(algorithm);
        if (algorithm &&
            (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC)) {
            EXPECT_GE(cert_chain_.size(), 1);
        } else {
            // For symmetric keys there should be no certificates.
            EXPECT_EQ(cert_chain_.size(), 0);
        }
    }

    return GetReturnErrorCode(result);
@@ -788,6 +824,24 @@ const vector<KeyParameter>& KeyMintAidlTestBase::SecLevelAuthorizations(
    return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
}

const vector<KeyParameter>& KeyMintAidlTestBase::HwEnforcedAuthorizations(
        const vector<KeyCharacteristics>& key_characteristics) {
    auto found =
            std::find_if(key_characteristics.begin(), key_characteristics.end(), [](auto& entry) {
                return entry.securityLevel == SecurityLevel::STRONGBOX ||
                       entry.securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT;
            });
    return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
}

const vector<KeyParameter>& KeyMintAidlTestBase::SwEnforcedAuthorizations(
        const vector<KeyCharacteristics>& key_characteristics) {
    auto found = std::find_if(
            key_characteristics.begin(), key_characteristics.end(),
            [](auto& entry) { return entry.securityLevel == SecurityLevel::SOFTWARE; });
    return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
}

}  // namespace test

}  // namespace aidl::android::hardware::security::keymint
+12 −4
Original line number Diff line number Diff line
@@ -27,7 +27,11 @@

#include <keymint_support/authorization_set.h>

namespace aidl::android::hardware::security::keymint::test {
namespace aidl::android::hardware::security::keymint {

::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set);

namespace test {

using ::android::sp;
using Status = ::ndk::ScopedAStatus;
@@ -37,8 +41,6 @@ using ::std::vector;

constexpr uint64_t kOpHandleSentinel = 0xFFFFFFFFFFFFFFFF;

::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set);

class KeyMintAidlTestBase : public ::testing::TestWithParam<string> {
  public:
    void SetUp() override;
@@ -173,6 +175,10 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam<string> {
    inline const vector<KeyParameter>& SecLevelAuthorizations() {
        return SecLevelAuthorizations(key_characteristics_);
    }
    const vector<KeyParameter>& HwEnforcedAuthorizations(
            const vector<KeyCharacteristics>& key_characteristics);
    const vector<KeyParameter>& SwEnforcedAuthorizations(
            const vector<KeyCharacteristics>& key_characteristics);

  private:
    std::shared_ptr<IKeyMintDevice> keymint_;
@@ -190,4 +196,6 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam<string> {
                             testing::ValuesIn(KeyMintAidlTestBase::build_params()), \
                             ::android::PrintInstanceNameToString)

}  // namespace aidl::android::hardware::security::keymint::test
}  // namespace test

}  // namespace aidl::android::hardware::security::keymint
+319 −13
Original line number Diff line number Diff line
@@ -180,9 +180,280 @@ struct RSA_Delete {
    void operator()(RSA* p) { RSA_free(p); }
};

/* TODO(seleneh) add attestation verification codes like verify_chain() and
 * attestation tests after we decided on the keymint 1 attestation changes.
 */
char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
                       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

string bin2hex(const vector<uint8_t>& data) {
    string retval;
    retval.reserve(data.size() * 2 + 1);
    for (uint8_t byte : data) {
        retval.push_back(nibble2hex[0x0F & (byte >> 4)]);
        retval.push_back(nibble2hex[0x0F & byte]);
    }
    return retval;
}

X509* parse_cert_blob(const vector<uint8_t>& blob) {
    const uint8_t* p = blob.data();
    return d2i_X509(nullptr, &p, blob.size());
}

bool verify_chain(const vector<Certificate>& chain) {
    for (size_t i = 0; i < chain.size(); ++i) {
        X509_Ptr key_cert(parse_cert_blob(chain[i].encodedCertificate));
        X509_Ptr signing_cert;
        if (i < chain.size() - 1) {
            signing_cert.reset(parse_cert_blob(chain[i + 1].encodedCertificate));
        } else {
            signing_cert.reset(parse_cert_blob(chain[i].encodedCertificate));
        }
        EXPECT_TRUE(!!key_cert.get() && !!signing_cert.get());
        if (!key_cert.get() || !signing_cert.get()) return false;

        EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
        EXPECT_TRUE(!!signing_pubkey.get());
        if (!signing_pubkey.get()) return false;

        EXPECT_EQ(1, X509_verify(key_cert.get(), signing_pubkey.get()))
                << "Verification of certificate " << i << " failed "
                << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);

        char* cert_issuer =  //
                X509_NAME_oneline(X509_get_issuer_name(key_cert.get()), nullptr, 0);
        char* signer_subj =
                X509_NAME_oneline(X509_get_subject_name(signing_cert.get()), nullptr, 0);
        EXPECT_STREQ(cert_issuer, signer_subj) << "Cert " << i << " has wrong issuer.";
        if (i == 0) {
            char* cert_sub = X509_NAME_oneline(X509_get_subject_name(key_cert.get()), nullptr, 0);
            EXPECT_STREQ("/CN=Android Keystore Key", cert_sub)
                    << "Cert " << i << " has wrong subject.";
            OPENSSL_free(cert_sub);
        }

        OPENSSL_free(cert_issuer);
        OPENSSL_free(signer_subj);

        if (dump_Attestations) std::cout << bin2hex(chain[i].encodedCertificate) << std::endl;
    }

    return true;
}

// Extract attestation record from cert. Returned object is still part of cert; don't free it
// separately.
ASN1_OCTET_STRING* get_attestation_record(X509* certificate) {
    ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */));
    EXPECT_TRUE(!!oid.get());
    if (!oid.get()) return nullptr;

    int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */);
    EXPECT_NE(-1, location) << "Attestation extension not found in certificate";
    if (location == -1) return nullptr;

    X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
    EXPECT_TRUE(!!attest_rec_ext)
            << "Found attestation extension but couldn't retrieve it?  Probably a BoringSSL bug.";
    if (!attest_rec_ext) return nullptr;

    ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
    EXPECT_TRUE(!!attest_rec) << "Attestation extension contained no data";
    return attest_rec;
}

bool tag_in_list(const KeyParameter& entry) {
    // Attestations don't contain everything in key authorization lists, so we need to filter
    // the key lists to produce the lists that we expect to match the attestations.
    auto tag_list = {
            Tag::BLOB_USAGE_REQUIREMENTS,  //
            Tag::CREATION_DATETIME,        //
            Tag::EC_CURVE,
            Tag::HARDWARE_TYPE,
            Tag::INCLUDE_UNIQUE_ID,
    };
    return std::find(tag_list.begin(), tag_list.end(), entry.tag) != tag_list.end();
}

AuthorizationSet filtered_tags(const AuthorizationSet& set) {
    AuthorizationSet filtered;
    std::remove_copy_if(set.begin(), set.end(), std::back_inserter(filtered), tag_in_list);
    return filtered;
}

bool avb_verification_enabled() {
    char value[PROPERTY_VALUE_MAX];
    return property_get("ro.boot.vbmeta.device_state", value, "") != 0;
}

bool verify_attestation_record(const string& challenge,                //
                               const string& app_id,                   //
                               AuthorizationSet expected_sw_enforced,  //
                               AuthorizationSet expected_hw_enforced,  //
                               SecurityLevel security_level,
                               const vector<uint8_t>& attestation_cert) {
    X509_Ptr cert(parse_cert_blob(attestation_cert));
    EXPECT_TRUE(!!cert.get());
    if (!cert.get()) return false;

    ASN1_OCTET_STRING* attest_rec = get_attestation_record(cert.get());
    EXPECT_TRUE(!!attest_rec);
    if (!attest_rec) return false;

    AuthorizationSet att_sw_enforced;
    AuthorizationSet att_hw_enforced;
    uint32_t att_attestation_version;
    uint32_t att_keymaster_version;
    SecurityLevel att_attestation_security_level;
    SecurityLevel att_keymaster_security_level;
    vector<uint8_t> att_challenge;
    vector<uint8_t> att_unique_id;
    vector<uint8_t> att_app_id;

    auto error = parse_attestation_record(attest_rec->data,                 //
                                          attest_rec->length,               //
                                          &att_attestation_version,         //
                                          &att_attestation_security_level,  //
                                          &att_keymaster_version,           //
                                          &att_keymaster_security_level,    //
                                          &att_challenge,                   //
                                          &att_sw_enforced,                 //
                                          &att_hw_enforced,                 //
                                          &att_unique_id);
    EXPECT_EQ(ErrorCode::OK, error);
    if (error != ErrorCode::OK) return false;

    EXPECT_GE(att_attestation_version, 3U);

    expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID,
                                   vector<uint8_t>(app_id.begin(), app_id.end()));

    EXPECT_GE(att_keymaster_version, 4U);
    EXPECT_EQ(security_level, att_keymaster_security_level);
    EXPECT_EQ(security_level, att_attestation_security_level);

    EXPECT_EQ(challenge.length(), att_challenge.size());
    EXPECT_EQ(0, memcmp(challenge.data(), att_challenge.data(), challenge.length()));

    char property_value[PROPERTY_VALUE_MAX] = {};
    // TODO(b/136282179): When running under VTS-on-GSI the TEE-backed
    // keymaster implementation will report YYYYMM dates instead of YYYYMMDD
    // for the BOOT_PATCH_LEVEL.
    if (avb_verification_enabled()) {
        for (int i = 0; i < att_hw_enforced.size(); i++) {
            if (att_hw_enforced[i].tag == TAG_BOOT_PATCHLEVEL ||
                att_hw_enforced[i].tag == TAG_VENDOR_PATCHLEVEL) {
                std::string date =
                        std::to_string(att_hw_enforced[i].value.get<KeyParameterValue::dateTime>());
                // strptime seems to require delimiters, but the tag value will
                // be YYYYMMDD
                date.insert(6, "-");
                date.insert(4, "-");
                EXPECT_EQ(date.size(), 10);
                struct tm time;
                strptime(date.c_str(), "%Y-%m-%d", &time);

                // Day of the month (0-31)
                EXPECT_GE(time.tm_mday, 0);
                EXPECT_LT(time.tm_mday, 32);
                // Months since Jan (0-11)
                EXPECT_GE(time.tm_mon, 0);
                EXPECT_LT(time.tm_mon, 12);
                // Years since 1900
                EXPECT_GT(time.tm_year, 110);
                EXPECT_LT(time.tm_year, 200);
            }
        }
    }

    // Check to make sure boolean values are properly encoded. Presence of a boolean tag indicates
    // true. A provided boolean tag that can be pulled back out of the certificate indicates correct
    // encoding. No need to check if it's in both lists, since the AuthorizationSet compare below
    // will handle mismatches of tags.
    if (security_level == SecurityLevel::SOFTWARE) {
        EXPECT_TRUE(expected_sw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
    } else {
        EXPECT_TRUE(expected_hw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
    }

    // Alternatively this checks the opposite - a false boolean tag (one that isn't provided in
    // the authorization list during key generation) isn't being attested to in the certificate.
    EXPECT_FALSE(expected_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
    EXPECT_FALSE(att_sw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
    EXPECT_FALSE(expected_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));
    EXPECT_FALSE(att_hw_enforced.Contains(TAG_TRUSTED_USER_PRESENCE_REQUIRED));

    if (att_hw_enforced.Contains(TAG_ALGORITHM, Algorithm::EC)) {
        // For ECDSA keys, either an EC_CURVE or a KEY_SIZE can be specified, but one must be.
        EXPECT_TRUE(att_hw_enforced.Contains(TAG_EC_CURVE) ||
                    att_hw_enforced.Contains(TAG_KEY_SIZE));
    }

    // Test root of trust elements
    vector<uint8_t> verified_boot_key;
    VerifiedBoot verified_boot_state;
    bool device_locked;
    vector<uint8_t> verified_boot_hash;
    error = parse_root_of_trust(attest_rec->data, attest_rec->length, &verified_boot_key,
                                &verified_boot_state, &device_locked, &verified_boot_hash);
    EXPECT_EQ(ErrorCode::OK, error);

    if (avb_verification_enabled()) {
        EXPECT_NE(property_get("ro.boot.vbmeta.digest", property_value, ""), 0);
        string prop_string(property_value);
        EXPECT_EQ(prop_string.size(), 64);
        EXPECT_EQ(prop_string, bin2hex(verified_boot_hash));

        EXPECT_NE(property_get("ro.boot.vbmeta.device_state", property_value, ""), 0);
        if (!strcmp(property_value, "unlocked")) {
            EXPECT_FALSE(device_locked);
        } else {
            EXPECT_TRUE(device_locked);
        }

        // Check that the device is locked if not debuggable, e.g., user build
        // images in CTS. For VTS, debuggable images are used to allow adb root
        // and the device is unlocked.
        if (!property_get_bool("ro.debuggable", false)) {
            EXPECT_TRUE(device_locked);
        } else {
            EXPECT_FALSE(device_locked);
        }
    }

    // Verified boot key should be all 0's if the boot state is not verified or self signed
    std::string empty_boot_key(32, '\0');
    std::string verified_boot_key_str((const char*)verified_boot_key.data(),
                                      verified_boot_key.size());
    EXPECT_NE(property_get("ro.boot.verifiedbootstate", property_value, ""), 0);
    if (!strcmp(property_value, "green")) {
        EXPECT_EQ(verified_boot_state, VerifiedBoot::VERIFIED);
        EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
                            verified_boot_key.size()));
    } else if (!strcmp(property_value, "yellow")) {
        EXPECT_EQ(verified_boot_state, VerifiedBoot::SELF_SIGNED);
        EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
                            verified_boot_key.size()));
    } else if (!strcmp(property_value, "orange")) {
        EXPECT_EQ(verified_boot_state, VerifiedBoot::UNVERIFIED);
        EXPECT_EQ(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
                            verified_boot_key.size()));
    } else if (!strcmp(property_value, "red")) {
        EXPECT_EQ(verified_boot_state, VerifiedBoot::FAILED);
    } else {
        EXPECT_EQ(verified_boot_state, VerifiedBoot::UNVERIFIED);
        EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
                            verified_boot_key.size()));
    }

    att_sw_enforced.Sort();
    expected_sw_enforced.Sort();
    EXPECT_EQ(filtered_tags(expected_sw_enforced), filtered_tags(att_sw_enforced));

    att_hw_enforced.Sort();
    expected_hw_enforced.Sort();
    EXPECT_EQ(filtered_tags(expected_hw_enforced), filtered_tags(att_hw_enforced));

    return true;
}

std::string make_string(const uint8_t* data, size_t length) {
    return std::string(reinterpret_cast<const char*>(data), length);
@@ -288,6 +559,51 @@ TEST_P(NewKeyGenerationTest, Rsa) {
    }
}

/*
 * NewKeyGenerationTest.Rsa
 *
 * Verifies that keymint can generate all required RSA key sizes, and that the resulting keys
 * have correct characteristics.
 */
TEST_P(NewKeyGenerationTest, RsaWithAttestation) {
    for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
        auto challenge = "hello";
        auto app_id = "foo";

        vector<uint8_t> key_blob;
        vector<KeyCharacteristics> key_characteristics;
        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                     .RsaSigningKey(key_size, 65537)
                                                     .Digest(Digest::NONE)
                                                     .Padding(PaddingMode::NONE)
                                                     .AttestationChallenge(challenge)
                                                     .AttestationApplicationId(app_id)
                                                     .Authorization(TAG_NO_AUTH_REQUIRED),
                                             &key_blob, &key_characteristics));

        ASSERT_GT(key_blob.size(), 0U);
        CheckBaseParams(key_characteristics);

        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);

        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
                << "Key size " << key_size << "missing";
        EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));

        EXPECT_TRUE(verify_chain(cert_chain_));
        ASSERT_GT(cert_chain_.size(), 0);

        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
        EXPECT_TRUE(verify_attestation_record(challenge, app_id,  //
                                              sw_enforced, hw_enforced, SecLevel(),
                                              cert_chain_[0].encodedCertificate));

        CheckedDeleteKey(&key_blob);
    }
}

/*
 * NewKeyGenerationTest.NoInvalidRsaSizes
 *
@@ -3895,16 +4211,6 @@ TEST_P(AddEntropyTest, AddLargeEntropy) {

INSTANTIATE_KEYMINT_AIDL_TEST(AddEntropyTest);

typedef KeyMintAidlTestBase AttestationTest;

/*
 * AttestationTest.RsaAttestation
 *
 * Verifies that attesting to RSA keys works and generates the expected output.
 */
// TODO(seleneh) add attestation tests back after decided on the new attestation
// behavior under generateKey and importKey

typedef KeyMintAidlTestBase KeyDeletionTest;

/**
+20 −0
Original line number Diff line number Diff line
@@ -259,6 +259,12 @@ class AuthorizationSetBuilder : public AuthorizationSet {
                             size - 1);  // drop the terminating '\0'
    }

    template <Tag tag>
    AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag,
                                           const std::string& data) {
        return Authorization(ttag, reinterpret_cast<const uint8_t*>(data.data()), data.size());
    }

    AuthorizationSetBuilder& Authorizations(const AuthorizationSet& set) {
        for (const auto& entry : set) {
            push_back(entry);
@@ -294,6 +300,20 @@ class AuthorizationSetBuilder : public AuthorizationSet {
    AuthorizationSetBuilder& Digest(std::vector<Digest> digests);
    AuthorizationSetBuilder& Padding(std::initializer_list<PaddingMode> paddings);

    AuthorizationSetBuilder& AttestationChallenge(const std::string& challenge) {
        return Authorization(TAG_ATTESTATION_CHALLENGE, challenge);
    }
    AuthorizationSetBuilder& AttestationChallenge(std::vector<uint8_t> challenge) {
        return Authorization(TAG_ATTESTATION_CHALLENGE, challenge);
    }

    AuthorizationSetBuilder& AttestationApplicationId(const std::string& id) {
        return Authorization(TAG_ATTESTATION_APPLICATION_ID, id);
    }
    AuthorizationSetBuilder& AttestationApplicationId(std::vector<uint8_t> id) {
        return Authorization(TAG_ATTESTATION_APPLICATION_ID, id);
    }

    template <typename... T>
    AuthorizationSetBuilder& BlockMode(T&&... a) {
        return BlockMode({std::forward<T>(a)...});