diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl index 132135bf06..9f4e509552 100644 --- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl +++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl @@ -43,5 +43,7 @@ interface IKeyMintDevice { void deleteAllKeys(); void destroyAttestationIds(); android.hardware.security.keymint.BeginResult begin(in android.hardware.security.keymint.KeyPurpose inPurpose, in byte[] inKeyBlob, in android.hardware.security.keymint.KeyParameter[] inParams, in android.hardware.security.keymint.HardwareAuthToken inAuthToken); + void deviceLocked(in boolean passwordOnly, in @nullable android.hardware.security.secureclock.TimeStampToken timestampToken); + void earlyBootEnded(); const int AUTH_TOKEN_MAC_LENGTH = 32; } diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl index 93966ea2b0..d06312a290 100644 --- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl +++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl @@ -37,4 +37,5 @@ parcelable KeyMintHardwareInfo { android.hardware.security.keymint.SecurityLevel securityLevel; @utf8InCpp String keyMintName; @utf8InCpp String keyMintAuthorName; + boolean timestampTokenRequired; } diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl index 0120a30dd5..71abedd7a0 100644 --- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl +++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl @@ -22,10 +22,11 @@ import android.hardware.security.keymint.HardwareAuthToken; import android.hardware.security.keymint.IKeyMintOperation; import android.hardware.security.keymint.KeyCreationResult; import android.hardware.security.keymint.KeyFormat; -import android.hardware.security.keymint.KeyParameter; import android.hardware.security.keymint.KeyMintHardwareInfo; +import android.hardware.security.keymint.KeyParameter; import android.hardware.security.keymint.KeyPurpose; import android.hardware.security.keymint.SecurityLevel; +import android.hardware.security.secureclock.TimeStampToken; /** * KeyMint device definition. @@ -346,8 +347,8 @@ interface IKeyMintDevice { * * @return The result of key creation. See KeyCreationResult.aidl. */ - KeyCreationResult importKey(in KeyParameter[] keyParams, in KeyFormat keyFormat, - in byte[] keyData); + KeyCreationResult importKey( + in KeyParameter[] keyParams, in KeyFormat keyFormat, in byte[] keyData); /** * Securely imports a key, or key pair, returning a key blob and a description of the imported @@ -429,12 +430,9 @@ interface IKeyMintDevice { * * @return The result of key creation. See KeyCreationResult.aidl. */ - KeyCreationResult importWrappedKey(in byte[] wrappedKeyData, - in byte[] wrappingKeyBlob, - in byte[] maskingKey, - in KeyParameter[] unwrappingParams, - in long passwordSid, - in long biometricSid); + KeyCreationResult importWrappedKey(in byte[] wrappedKeyData, in byte[] wrappingKeyBlob, + in byte[] maskingKey, in KeyParameter[] unwrappingParams, in long passwordSid, + in long biometricSid); /** * Upgrades an old key blob. Keys can become "old" in two ways: IKeyMintDevice can be @@ -705,8 +703,44 @@ interface IKeyMintDevice { * from operations that generate an IV or nonce, and IKeyMintOperation object pointer * which is used to perform update(), finish() or abort() operations. */ - BeginResult begin(in KeyPurpose inPurpose, - in byte[] inKeyBlob, - in KeyParameter[] inParams, - in HardwareAuthToken inAuthToken); + BeginResult begin(in KeyPurpose inPurpose, in byte[] inKeyBlob, in KeyParameter[] inParams, + in HardwareAuthToken inAuthToken); + + /** + * Called by client to notify the IKeyMintDevice that the device is now locked, and keys with + * the UNLOCKED_DEVICE_REQUIRED tag should no longer be usable. When this function is called, + * the IKeyMintDevice should note the current timestamp, and attempts to use + * UNLOCKED_DEVICE_REQUIRED keys must be rejected with Error::DEVICE_LOCKED until an + * authentication token with a later timestamp is presented. If the `passwordOnly' argument is + * set to true the sufficiently-recent authentication token must indicate that the user + * authenticated with a password, not a biometric. + * + * Note that the IKeyMintDevice UNLOCKED_DEVICE_REQUIRED semantics are slightly different from + * the UNLOCKED_DEVICE_REQUIRED semantics enforced by keystore. Keystore handles device locking + * on a per-user basis. Because auth tokens do not contain an Android user ID, it's not + * possible to replicate the keystore enformcement logic in IKeyMintDevice. So from the + * IKeyMintDevice perspective, any user unlock unlocks all UNLOCKED_DEVICE_REQUIRED keys. + * Keystore will continue enforcing the per-user device locking. + * + * @param passwordOnly specifies whether the device must be unlocked with a password, rather + * than a biometric, before UNLOCKED_DEVICE_REQUIRED keys can be used. + * + * @param timestampToken is used by StrongBox implementations of IKeyMintDevice. It + * provides the StrongBox IKeyMintDevice with a fresh, MACed timestamp which it can use as the + * device-lock time, for future comparison against auth tokens when operations using + * UNLOCKED_DEVICE_REQUIRED keys are attempted. Unless the auth token timestamp is newer than + * the timestamp in the timestampToken, the device is still considered to be locked. + * Crucially, if a StrongBox IKeyMintDevice receives a deviceLocked() call with a timestampToken + * timestamp that is less than the timestamp in the last deviceLocked() call, it must ignore the + * new timestamp. TEE IKeyMintDevice implementations will receive an empty timestampToken (zero + * values and empty vectors) and should use their own clock as the device-lock time. + */ + void deviceLocked(in boolean passwordOnly, in @nullable TimeStampToken timestampToken); + + /** + * Called by client to notify the IKeyMintDevice that the device has left the early boot + * state, and that keys with the EARLY_BOOT_ONLY tag may no longer be used. All attempts to use + * an EARLY_BOOT_ONLY key after this method is called must fail with Error::INVALID_KEY_BLOB. + */ + void earlyBootEnded(); } diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl index 1a107ba728..2fcaf4cf3b 100644 --- a/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl +++ b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl @@ -45,4 +45,11 @@ parcelable KeyMintHardwareInfo { * same author. */ @utf8InCpp String keyMintAuthorName; + + /* The timestampTokenRequired is a boolean flag, which when true reflects that IKeyMintDevice + * instance will expect a valid TimeStampToken with various operations. This will typically + * required by the StrongBox implementations that generally don't have secure clock hardware to + * generate timestamp tokens. + */ + boolean timestampTokenRequired; } diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp index 6555157e5c..eb66aca58a 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp @@ -45,7 +45,7 @@ using std::optional; namespace test { namespace { - +typedef KeyMintAidlTestBase::KeyData KeyData; // Predicate for testing basic characteristics validity in generation or import. bool KeyCharacteristicsBasicallyValid(SecurityLevel secLevel, const vector& key_characteristics) { @@ -461,6 +461,34 @@ void KeyMintAidlTestBase::AbortIfNeeded() { } } +auto KeyMintAidlTestBase::ProcessMessage(const vector& key_blob, KeyPurpose operation, + const string& message, const AuthorizationSet& in_params) + -> std::tuple { + AuthorizationSet begin_out_params; + ErrorCode result = Begin(operation, key_blob, in_params, &begin_out_params); + AuthorizationSet out_params(std::move(begin_out_params)); + if (result != ErrorCode::OK) { + return {result, {}, out_params}; + } + + string output; + int32_t consumed = 0; + AuthorizationSet update_params; + AuthorizationSet update_out_params; + result = Update(update_params, message, &update_out_params, &output, &consumed); + out_params.push_back(update_out_params); + if (result != ErrorCode::OK) { + return {result, output, out_params}; + } + + string unused; + AuthorizationSet finish_params; + AuthorizationSet finish_out_params; + result = Finish(finish_params, message.substr(consumed), unused, &finish_out_params, &output); + out_params.push_back(finish_out_params); + return {result, output, out_params}; +} + string KeyMintAidlTestBase::ProcessMessage(const vector& key_blob, KeyPurpose operation, const string& message, const AuthorizationSet& in_params, AuthorizationSet* out_params) { @@ -859,6 +887,35 @@ AuthorizationSet KeyMintAidlTestBase::SwEnforcedAuthorizations( return authList; } +ErrorCode KeyMintAidlTestBase::UseAesKey(const vector& aesKeyBlob) { + auto [result, ciphertext, out_params] = ProcessMessage( + aesKeyBlob, KeyPurpose::ENCRYPT, "1234567890123456", + AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE)); + return result; +} + +ErrorCode KeyMintAidlTestBase::UseHmacKey(const vector& hmacKeyBlob) { + auto [result, mac, out_params] = ProcessMessage( + hmacKeyBlob, KeyPurpose::SIGN, "1234567890123456", + AuthorizationSetBuilder().Authorization(TAG_MAC_LENGTH, 128).Digest(Digest::SHA_2_256)); + return result; +} + +ErrorCode KeyMintAidlTestBase::UseRsaKey(const vector& rsaKeyBlob) { + std::string message(2048 / 8, 'a'); + auto [result, signature, out_params] = ProcessMessage( + rsaKeyBlob, KeyPurpose::SIGN, message, + AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)); + return result; +} + +ErrorCode KeyMintAidlTestBase::UseEcdsaKey(const vector& ecdsaKeyBlob) { + auto [result, signature, out_params] = + ProcessMessage(ecdsaKeyBlob, KeyPurpose::SIGN, "a", + AuthorizationSetBuilder().Digest(Digest::SHA_2_256)); + return result; +} + } // namespace test } // namespace aidl::android::hardware::security::keymint diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h index 780971dc8c..4e546ed553 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h @@ -43,6 +43,11 @@ constexpr uint64_t kOpHandleSentinel = 0xFFFFFFFFFFFFFFFF; class KeyMintAidlTestBase : public ::testing::TestWithParam { public: + struct KeyData { + vector blob; + vector characteristics; + }; + void SetUp() override; void TearDown() override { if (key_blob_.size()) { @@ -61,7 +66,6 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { vector* key_characteristics); ErrorCode GenerateKey(const AuthorizationSet& key_desc); - ErrorCode ImportKey(const AuthorizationSet& key_desc, KeyFormat format, const string& key_material, vector* key_blob, vector* key_characteristics); @@ -106,7 +110,9 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { string ProcessMessage(const vector& key_blob, KeyPurpose operation, const string& message, const AuthorizationSet& in_params, AuthorizationSet* out_params); - + std::tuple + ProcessMessage(const vector& key_blob, KeyPurpose operation, + const std::string& message, const AuthorizationSet& in_params); string SignMessage(const vector& key_blob, const string& message, const AuthorizationSet& params); string SignMessage(const string& message, const AuthorizationSet& params); @@ -149,6 +155,56 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { std::pair> UpgradeKey(const vector& key_blob); + template + std::tuple + CreateTestKeys(TagType tagToTest, ErrorCode expectedReturn) { + /* AES */ + KeyData aesKeyData; + ErrorCode errorCode = GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(tagToTest) + .BlockMode(BlockMode::ECB) + .Padding(PaddingMode::NONE) + .Authorization(TAG_NO_AUTH_REQUIRED), + &aesKeyData.blob, &aesKeyData.characteristics); + EXPECT_EQ(expectedReturn, errorCode); + + /* HMAC */ + KeyData hmacKeyData; + errorCode = GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Authorization(tagToTest) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 128) + .Authorization(TAG_NO_AUTH_REQUIRED), + &hmacKeyData.blob, &hmacKeyData.characteristics); + EXPECT_EQ(expectedReturn, errorCode); + + /* RSA */ + KeyData rsaKeyData; + errorCode = GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(2048, 65537) + .Authorization(tagToTest) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + &rsaKeyData.blob, &rsaKeyData.characteristics); + EXPECT_EQ(expectedReturn, errorCode); + + /* ECDSA */ + KeyData ecdsaKeyData; + errorCode = GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(256) + .Authorization(tagToTest) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + &ecdsaKeyData.blob, &ecdsaKeyData.characteristics); + EXPECT_EQ(expectedReturn, errorCode); + return {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}; + } bool IsSecure() const { return securityLevel_ != SecurityLevel::SOFTWARE; } SecurityLevel SecLevel() const { return securityLevel_; } @@ -182,6 +238,10 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { const vector& key_characteristics); AuthorizationSet SwEnforcedAuthorizations( const vector& key_characteristics); + ErrorCode UseAesKey(const vector& aesKeyBlob); + ErrorCode UseHmacKey(const vector& hmacKeyBlob); + ErrorCode UseRsaKey(const vector& rsaKeyBlob); + ErrorCode UseEcdsaKey(const vector& ecdsaKeyBlob); private: std::shared_ptr keymint_; diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp index 88122ce7a2..16c07554d2 100644 --- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp @@ -78,7 +78,8 @@ namespace aidl::android::hardware::security::keymint::test { namespace { template -bool contains(vector& set, TypedTag ttag, ValueT expected_value) { +bool contains(const vector& set, TypedTag ttag, + ValueT expected_value) { auto it = std::find_if(set.begin(), set.end(), [&](const KeyParameter& param) { if (auto p = authorizationValue(ttag, param)) { return *p == expected_value; @@ -89,7 +90,7 @@ bool contains(vector& set, TypedTag ttag, ValueT ex } template -bool contains(vector& set, TypedTag) { +bool contains(const vector& set, TypedTag) { auto it = std::find_if(set.begin(), set.end(), [&](const KeyParameter& param) { return param.tag == tag; }); return (it != set.end()); @@ -4961,6 +4962,99 @@ TEST_P(KeyAgreementTest, Ecdh) { INSTANTIATE_KEYMINT_AIDL_TEST(KeyAgreementTest); +typedef KeyMintAidlTestBase EarlyBootKeyTest; + +TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) { + auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] = + CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK); + + CheckedDeleteKey(&aesKeyData.blob); + CheckedDeleteKey(&hmacKeyData.blob); + CheckedDeleteKey(&rsaKeyData.blob); + CheckedDeleteKey(&ecdsaKeyData.blob); +} + +// This is a more comprenhensive test, but it can only be run on a machine which is still in early +// boot stage, which no proper Android device is by the time we can run VTS. To use this, +// un-disable it and modify vold to remove the call to earlyBootEnded(). Running the test will end +// early boot, so you'll have to reboot between runs. +TEST_P(EarlyBootKeyTest, DISABLED_FullTest) { + auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] = + CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK); + // TAG_EARLY_BOOT_ONLY should be in hw-enforced. + EXPECT_TRUE(HwEnforcedAuthorizations(aesKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY)); + EXPECT_TRUE( + HwEnforcedAuthorizations(hmacKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY)); + EXPECT_TRUE(HwEnforcedAuthorizations(rsaKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY)); + EXPECT_TRUE( + HwEnforcedAuthorizations(ecdsaKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY)); + + // Should be able to use keys, since early boot has not ended + EXPECT_EQ(ErrorCode::OK, UseAesKey(aesKeyData.blob)); + EXPECT_EQ(ErrorCode::OK, UseHmacKey(hmacKeyData.blob)); + EXPECT_EQ(ErrorCode::OK, UseRsaKey(rsaKeyData.blob)); + EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob)); + + // End early boot + ErrorCode earlyBootResult = GetReturnErrorCode(keyMint().earlyBootEnded()); + EXPECT_EQ(earlyBootResult, ErrorCode::OK); + + // Should not be able to use already-created keys. + EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseAesKey(aesKeyData.blob)); + EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseHmacKey(hmacKeyData.blob)); + EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseRsaKey(rsaKeyData.blob)); + EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseEcdsaKey(ecdsaKeyData.blob)); + + CheckedDeleteKey(&aesKeyData.blob); + CheckedDeleteKey(&hmacKeyData.blob); + CheckedDeleteKey(&rsaKeyData.blob); + CheckedDeleteKey(&ecdsaKeyData.blob); + + // Should not be able to create new keys + std::tie(aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData) = + CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::EARLY_BOOT_ENDED); + + CheckedDeleteKey(&aesKeyData.blob); + CheckedDeleteKey(&hmacKeyData.blob); + CheckedDeleteKey(&rsaKeyData.blob); + CheckedDeleteKey(&ecdsaKeyData.blob); +} +INSTANTIATE_KEYMINT_AIDL_TEST(EarlyBootKeyTest); + +typedef KeyMintAidlTestBase UnlockedDeviceRequiredTest; + +// This may be a problematic test. It can't be run repeatedly without unlocking the device in +// between runs... and on most test devices there are no enrolled credentials so it can't be +// unlocked at all, meaning the only way to get the test to pass again on a properly-functioning +// device is to reboot it. For that reason, this is disabled by default. It can be used as part of +// a manual test process, which includes unlocking between runs, which is why it's included here. +// Well, that and the fact that it's the only test we can do without also making calls into the +// Gatekeeper HAL. We haven't written any cross-HAL tests, and don't know what all of the +// implications might be, so that may or may not be a solution. +TEST_P(UnlockedDeviceRequiredTest, DISABLED_KeysBecomeUnusable) { + auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] = + CreateTestKeys(TAG_UNLOCKED_DEVICE_REQUIRED, ErrorCode::OK); + + EXPECT_EQ(ErrorCode::OK, UseAesKey(aesKeyData.blob)); + EXPECT_EQ(ErrorCode::OK, UseHmacKey(hmacKeyData.blob)); + EXPECT_EQ(ErrorCode::OK, UseRsaKey(rsaKeyData.blob)); + EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob)); + + ErrorCode rc = GetReturnErrorCode( + keyMint().deviceLocked(false /* passwordOnly */, {} /* verificationToken */)); + ASSERT_EQ(ErrorCode::OK, rc); + EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseAesKey(aesKeyData.blob)); + EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseHmacKey(hmacKeyData.blob)); + EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseRsaKey(rsaKeyData.blob)); + EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseEcdsaKey(ecdsaKeyData.blob)); + + CheckedDeleteKey(&aesKeyData.blob); + CheckedDeleteKey(&hmacKeyData.blob); + CheckedDeleteKey(&rsaKeyData.blob); + CheckedDeleteKey(&ecdsaKeyData.blob); +} +INSTANTIATE_KEYMINT_AIDL_TEST(UnlockedDeviceRequiredTest); + } // namespace aidl::android::hardware::security::keymint::test int main(int argc, char** argv) {