Merge "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."

This commit is contained in:
Treehugger Robot 2021-02-03 17:38:05 +00:00 committed by Gerrit Code Review
commit 49ad4f04d8
5 changed files with 220 additions and 56 deletions

View file

@ -27,7 +27,7 @@ import android.hardware.security.keymint.TagType;
* data are stored in KeyParameter.
*/
@VintfStability
@Backing(type = "int")
@Backing(type="int")
enum Tag {
/**
* Tag::INVALID should never be set. It means you hit an error.
@ -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
@ -187,21 +186,21 @@ enum Tag {
*/
INCLUDE_UNIQUE_ID = (7 << 28) /* TagType:BOOL */ | 202,
/**
* Tag::RSA_OAEP_MGF_DIGEST specifies the MGF1 digest algorithms that may be used with
* RSA encryption/decryption with OAEP padding. If the key characteristics supports OAEP
* and this tag is absent then SHA1 digest is selected by default for MGF1.
*
* This tag is repeatable for key generation/import. If this tag is present in the key
* characteristics with one or more values from @4.0::Digest, then for RSA cipher
* operations with OAEP Padding, the caller must specify a digest in the additionalParams
* argument of begin operation. If this tag is missing or the specified digest is not in
* the digests associated with the key then begin operation must fail with
* ErrorCode::INCOMPATIBLE_MGF_DIGEST.
*
* Must be hardware-enforced.
*/
RSA_OAEP_MGF_DIGEST = (2 << 28) /* TagType:ENUM_REP */ | 203,
/**
* Tag::RSA_OAEP_MGF_DIGEST specifies the MGF1 digest algorithms that may be used with
* RSA encryption/decryption with OAEP padding. If the key characteristics supports OAEP
* and this tag is absent then SHA1 digest is selected by default for MGF1.
*
* This tag is repeatable for key generation/import. If this tag is present in the key
* characteristics with one or more values from @4.0::Digest, then for RSA cipher
* operations with OAEP Padding, the caller must specify a digest in the additionalParams
* argument of begin operation. If this tag is missing or the specified digest is not in
* the digests associated with the key then begin operation must fail with
* ErrorCode::INCOMPATIBLE_MGF_DIGEST.
*
* Must be hardware-enforced.
*/
RSA_OAEP_MGF_DIGEST = (2 << 28) /* TagType:ENUM_REP */ | 203,
/**
* TODO(seleneh) this tag needs to be deleted from all codes.
@ -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.

View file

@ -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

View file

@ -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:

View file

@ -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;
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)) {
// 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, 1U));
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
}
}
/*
* UsageCountLimitTest.TestLimitRsa
* UsageCountLimitTest.TestLimitedUseAes
*
* Verifies that the usage count limit tag works correctly with RSA keys.
* Verifies that the usage count limit tag > 1 works correctly with AES keys.
*/
TEST_P(UsageCountLimitTest, TestLimitRsa) {
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_) {
auths.push_back(AuthorizationSet(entry.authorizations));
}
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 keystore, keymint does nothing.
EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U));
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
}
}
/*
* UsageCountLimitTest.TestSingleUseRsa
*
* Verifies that the usage count limit tag = 1 works correctly with RSA keys.
*/
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;
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)) {
// 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, 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_) {
auths.push_back(AuthorizationSet(entry.authorizations));
}
EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U))
<< "key usage count limit " << 3U << " missing";
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 keystore, keymint does nothing.
EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U));
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, params));
}
}

View file

@ -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;
}