diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl index bc07235dc0..f52e32b96a 100644 --- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl +++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl @@ -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. diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp index 766c02dea9..6555157e5c 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp @@ -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& KeyMintAidlTestBase::SecLevelAuthorizations( return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations; } -const vector& KeyMintAidlTestBase::HwEnforcedAuthorizations( - const vector& 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& KeyMintAidlTestBase::SecLevelAuthorizations( + const vector& 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& KeyMintAidlTestBase::SwEnforcedAuthorizations( +AuthorizationSet KeyMintAidlTestBase::HwEnforcedAuthorizations( const vector& 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& 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 diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h index c1a1dd9034..780971dc8c 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h @@ -175,9 +175,12 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { inline const vector& SecLevelAuthorizations() { return SecLevelAuthorizations(key_characteristics_); } - const vector& HwEnforcedAuthorizations( + const vector& SecLevelAuthorizations( + const vector& key_characteristics, SecurityLevel securityLevel); + + AuthorizationSet HwEnforcedAuthorizations( const vector& key_characteristics); - const vector& SwEnforcedAuthorizations( + AuthorizationSet SwEnforcedAuthorizations( const vector& key_characteristics); private: diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp index c876440d4b..c849bade2e 100644 --- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp @@ -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 key_blob; + vector 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)); } } diff --git a/security/keymint/support/attestation_record.cpp b/security/keymint/support/attestation_record.cpp index 596b097bed..a48f770700 100644 --- a/security/keymint/support/attestation_record.cpp +++ b/security/keymint/support/attestation_record.cpp @@ -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; }