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

Commit beefae47 authored by Qi Wu's avatar Qi Wu
Browse files

Add more vts tests related to limited use key.

1. Fix test case for usage count limit tag = 1 case, when
  hardware cannot enforce it, the tag should by enforced by keystore.
2. Add test case for usage count limit tag > 1.
3. Add test case to verify the usage count limit tag appears
  correctly in the attestation certificate for asymmetic key.

Test: atest -c VtsAidlKeyMintTargetTest

Change-Id: I01df278b42a91a78c8888c13c4f81b7ec70cfa22
parent 06e5b50f
Loading
Loading
Loading
Loading
+20 −20
Original line number Diff line number Diff line
@@ -82,7 +82,6 @@ enum Tag {
     */
    BLOCK_MODE = (2 << 28) /* TagType:ENUM_REP */ | 4,


    /**
     * Tag::DIGEST specifies the digest algorithms that may be used with the key to perform signing
     * and verification operations.  This tag is relevant to RSA, ECDSA and HMAC keys.  Possible
@@ -346,14 +345,14 @@ enum Tag {
     * At this point, if the caller specifies count > 1, it is not expected that any TEE will be
     * able to enforce this feature in the hardware due to limited resources of secure
     * storage. In this case, the tag with the value of maximum usage must be added to the key
     * characteristics with SecurityLevel::SOFTWARE by the IKeyMintDevice.
     * characteristics with SecurityLevel::KEYSTORE by the IKeyMintDevice.
     *
     * On the other hand, if the caller specifies count = 1, some TEEs may have the ability
     * to enforce this feature in the hardware with its secure storage. If the IKeyMintDevice
     * implementation can enforce this feature, the tag with value = 1 must be added to the key
     * characteristics with the SecurityLevel of the IKeyMintDevice. If the IKeyMintDevice can't
     * enforce this feature even when the count = 1, the tag must be added to the key
     * characteristics with the SecurityLevel::SOFTWARE.
     * characteristics with the SecurityLevel::KEYSTORE.
     *
     * When the key is attested, this tag with the same value must also be added to the attestation
     * record. This tag must have the same SecurityLevel as the tag that is added to the key
@@ -497,7 +496,8 @@ enum Tag {
     */
    TRUSTED_USER_PRESENCE_REQUIRED = (7 << 28) /* TagType:BOOL */ | 507,

    /** Tag::TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and
    /**
     * Tag::TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and
     *  specifies that this key must not be usable unless the user provides confirmation of the data
     *  to be signed.  Confirmation is proven to keyMint via an approval token.  See
     *  CONFIRMATION_TOKEN, as well as the ConfirmatinUI HAL.
+29 −12
Original line number Diff line number Diff line
@@ -55,6 +55,9 @@ bool KeyCharacteristicsBasicallyValid(SecurityLevel secLevel,
    for (auto& entry : key_characteristics) {
        if (entry.authorizations.empty()) return false;

        // Just ignore the SecurityLevel::KEYSTORE as the KM won't do any enforcement on this.
        if (entry.securityLevel == SecurityLevel::KEYSTORE) continue;

        if (levels_seen.find(entry.securityLevel) != levels_seen.end()) return false;
        levels_seen.insert(entry.securityLevel);

@@ -824,22 +827,36 @@ 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;
            });
const vector<KeyParameter>& KeyMintAidlTestBase::SecLevelAuthorizations(
        const vector<KeyCharacteristics>& key_characteristics, SecurityLevel securityLevel) {
    auto found = std::find_if(
            key_characteristics.begin(), key_characteristics.end(),
            [securityLevel](auto& entry) { return entry.securityLevel == securityLevel; });
    return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
}

const vector<KeyParameter>& KeyMintAidlTestBase::SwEnforcedAuthorizations(
AuthorizationSet KeyMintAidlTestBase::HwEnforcedAuthorizations(
        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;
    AuthorizationSet authList;
    for (auto& entry : key_characteristics) {
        if (entry.securityLevel == SecurityLevel::STRONGBOX ||
            entry.securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT) {
            authList.push_back(AuthorizationSet(entry.authorizations));
        }
    }
    return authList;
}

AuthorizationSet KeyMintAidlTestBase::SwEnforcedAuthorizations(
        const vector<KeyCharacteristics>& key_characteristics) {
    AuthorizationSet authList;
    for (auto& entry : key_characteristics) {
        if (entry.securityLevel == SecurityLevel::SOFTWARE ||
            entry.securityLevel == SecurityLevel::KEYSTORE) {
            authList.push_back(AuthorizationSet(entry.authorizations));
        }
    }
    return authList;
}

}  // namespace test
+5 −2
Original line number Diff line number Diff line
@@ -175,9 +175,12 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam<string> {
    inline const vector<KeyParameter>& SecLevelAuthorizations() {
        return SecLevelAuthorizations(key_characteristics_);
    }
    const vector<KeyParameter>& HwEnforcedAuthorizations(
    const vector<KeyParameter>& SecLevelAuthorizations(
            const vector<KeyCharacteristics>& key_characteristics, SecurityLevel securityLevel);

    AuthorizationSet HwEnforcedAuthorizations(
            const vector<KeyCharacteristics>& key_characteristics);
    const vector<KeyParameter>& SwEnforcedAuthorizations(
    AuthorizationSet SwEnforcedAuthorizations(
            const vector<KeyCharacteristics>& key_characteristics);

  private:
+159 −18
Original line number Diff line number Diff line
@@ -645,6 +645,61 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsa) {
    }
}

/*
 * NewKeyGenerationTest.LimitedUsageRsaWithAttestation
 *
 * Verifies that KeyMint can generate all required RSA key sizes with limited usage, and that the
 * resulting keys have correct characteristics and attestation.
 */
TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) {
    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)
                                                     .Authorization(TAG_USAGE_COUNT_LIMIT, 1),
                                             &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));

        // Check the usage count limit tag appears in the authorizations.
        AuthorizationSet auths;
        for (auto& entry : key_characteristics) {
            auths.push_back(AuthorizationSet(entry.authorizations));
        }
        EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
                << "key usage count limit " << 1U << " missing";

        // Check the usage count limit tag also appears in the attestation.
        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
 *
@@ -4297,11 +4352,11 @@ INSTANTIATE_KEYMINT_AIDL_TEST(MaxOperationsTest);
typedef KeyMintAidlTestBase UsageCountLimitTest;

/*
 * UsageCountLimitTest.TestLimitAes
 * UsageCountLimitTest.TestSingleUseAes
 *
 * Verifies that the usage count limit tag works correctly with AES keys.
 * Verifies that the usage count limit tag = 1 works correctly with AES keys.
 */
TEST_P(UsageCountLimitTest, TestLimitAes) {
TEST_P(UsageCountLimitTest, TestSingleUseAes) {
    if (SecLevel() == SecurityLevel::STRONGBOX) return;

    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -4322,31 +4377,75 @@ TEST_P(UsageCountLimitTest, TestLimitAes) {
    string message = "1234567890123456";
    auto params = AuthorizationSetBuilder().EcbMode().Padding(PaddingMode::NONE);

    AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
    AuthorizationSet keystore_auths =
            SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);

    // First usage of AES key should work.
    EncryptMessage(message, params);

    AuthorizationSet hardware_auths;
    if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U)) {
        // Usage count limit tag is enforced by hardware. After using the key, the key blob
        // must be invalidated from secure storage (such as RPMB partition).
        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::ENCRYPT, params));
    } else {
        // Usage count limit tag is enforced by keystore, keymint does nothing.
        EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U));
        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
    }
}

/*
 * UsageCountLimitTest.TestLimitedUseAes
 *
 * Verifies that the usage count limit tag > 1 works correctly with AES keys.
 */
TEST_P(UsageCountLimitTest, TestLimitedUseAes) {
    if (SecLevel() == SecurityLevel::STRONGBOX) return;

    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
                                                 .AesEncryptionKey(128)
                                                 .EcbMode()
                                                 .Padding(PaddingMode::NONE)
                                                 .Authorization(TAG_USAGE_COUNT_LIMIT, 3)));

    // Check the usage count limit tag appears in the authorizations.
    AuthorizationSet auths;
    for (auto& entry : key_characteristics_) {
        if (entry.securityLevel != SecurityLevel::SOFTWARE) {
        auths.push_back(AuthorizationSet(entry.authorizations));
    }
    }
    if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U)) {
    EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U))
            << "key usage count limit " << 3U << " missing";

    string message = "1234567890123456";
    auto params = AuthorizationSetBuilder().EcbMode().Padding(PaddingMode::NONE);

    AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
    AuthorizationSet keystore_auths =
            SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);

    EncryptMessage(message, params);
    EncryptMessage(message, params);
    EncryptMessage(message, params);

    if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U)) {
        // Usage count limit tag is enforced by hardware. After using the key, the key blob
        // must be invalidated from secure storage (such as RPMB partition).
        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::ENCRYPT, params));
    } else {
        // Usage count limit tag is enforced by software, keymint does nothing.
        // Usage count limit tag is enforced by keystore, keymint does nothing.
        EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U));
        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
    }
}

/*
 * UsageCountLimitTest.TestLimitRsa
 * UsageCountLimitTest.TestSingleUseRsa
 *
 * Verifies that the usage count limit tag works correctly with RSA keys.
 * Verifies that the usage count limit tag = 1 works correctly with RSA keys.
 */
TEST_P(UsageCountLimitTest, TestLimitRsa) {
TEST_P(UsageCountLimitTest, TestSingleUseRsa) {
    if (SecLevel() == SecurityLevel::STRONGBOX) return;

    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
@@ -4366,22 +4465,64 @@ TEST_P(UsageCountLimitTest, TestLimitRsa) {
    string message = "1234567890123456";
    auto params = AuthorizationSetBuilder().NoDigestOrPadding();

    AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
    AuthorizationSet keystore_auths =
            SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);

    // First usage of RSA key should work.
    SignMessage(message, params);

    AuthorizationSet hardware_auths;
    if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U)) {
        // Usage count limit tag is enforced by hardware. After using the key, the key blob
        // must be invalidated from secure storage (such as RPMB partition).
        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::SIGN, params));
    } else {
        // Usage count limit tag is enforced by keystore, keymint does nothing.
        EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U));
        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, params));
    }
}

/*
 * UsageCountLimitTest.TestLimitUseRsa
 *
 * Verifies that the usage count limit tag > 1 works correctly with RSA keys.
 */
TEST_P(UsageCountLimitTest, TestLimitUseRsa) {
    if (SecLevel() == SecurityLevel::STRONGBOX) return;

    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
                                                 .RsaSigningKey(1024, 65537)
                                                 .NoDigestOrPadding()
                                                 .Authorization(TAG_USAGE_COUNT_LIMIT, 3)));

    // Check the usage count limit tag appears in the authorizations.
    AuthorizationSet auths;
    for (auto& entry : key_characteristics_) {
        if (entry.securityLevel != SecurityLevel::SOFTWARE) {
        auths.push_back(AuthorizationSet(entry.authorizations));
    }
    }
    EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U))
            << "key usage count limit " << 3U << " missing";

    if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U)) {
    string message = "1234567890123456";
    auto params = AuthorizationSetBuilder().NoDigestOrPadding();

    AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
    AuthorizationSet keystore_auths =
            SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);

    SignMessage(message, params);
    SignMessage(message, params);
    SignMessage(message, params);

    if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U)) {
        // Usage count limit tag is enforced by hardware. After using the key, the key blob
        // must be invalidated from secure storage (such as RPMB partition).
        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::SIGN, params));
    } else {
        // Usage count limit tag is enforced by software, keymint does nothing.
        // Usage count limit tag is enforced by keystore, keymint does nothing.
        EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U));
        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, params));
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -97,6 +97,7 @@ typedef struct km_auth_list {
    ASN1_NULL* device_unique_attestation;
    ASN1_NULL* storage_key;
    ASN1_NULL* identity_credential;
    ASN1_INTEGER* usage_count_limit;
} KM_AUTH_LIST;

ASN1_SEQUENCE(KM_AUTH_LIST) = {
@@ -143,7 +144,8 @@ ASN1_SEQUENCE(KM_AUTH_LIST) = {
        ASN1_EXP_OPT(KM_AUTH_LIST, storage_key, ASN1_NULL, TAG_STORAGE_KEY.maskedTag()),
        ASN1_EXP_OPT(KM_AUTH_LIST, identity_credential, ASN1_NULL,
                     TAG_IDENTITY_CREDENTIAL_KEY.maskedTag()),

        ASN1_EXP_OPT(KM_AUTH_LIST, usage_count_limit, ASN1_INTEGER,
                     TAG_USAGE_COUNT_LIMIT.maskedTag()),
} ASN1_SEQUENCE_END(KM_AUTH_LIST);
IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);

@@ -285,6 +287,7 @@ static ErrorCode extract_auth_list(const KM_AUTH_LIST* record, AuthorizationSet*
    copyAuthTag(record->device_unique_attestation, TAG_DEVICE_UNIQUE_ATTESTATION, auth_list);
    copyAuthTag(record->storage_key, TAG_STORAGE_KEY, auth_list);
    copyAuthTag(record->identity_credential, TAG_IDENTITY_CREDENTIAL_KEY, auth_list);
    copyAuthTag(record->usage_count_limit, TAG_USAGE_COUNT_LIMIT, auth_list);

    return ErrorCode::OK;
}