From 34945b1fd2d8f9201c7db1b7ff915eaea78d33ac Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 23 Jul 2022 00:28:01 +0000 Subject: [PATCH 1/2] AndroidKeyStore: support platform use of rollback-resistant keys The keystore2 binder API supports rollback resistance when KeyMint supports it, but until now this wasn't exposed to Java code that uses AndroidKeyStore. Add support for rollback-resistant keys to KeyProtection and AndroidKeyStoreSpi.setSecretKeyEntry() so that LockSettingsService can request it for SP protector keys. This CL does *not* do any of the following: - Add any non-hidden APIs. KeyMint implementations only support a limited number of rollback-resistant keys; currently the available space is reserved for platform use only. Note that other examples of "hidden", platform-only key properties are isCriticalToDeviceEncryption() and getBoundToSpecificSecureUserId(). - Support rollback resistance with keys directly generated by Keystore. This isn't currently needed. Note that this would require changes KeyGenParameterSpec and AndroidKeyStoreKeyGeneratorSpi. - Allow querying the rollback resistance property of keys. This isn't currently needed. Note that this would require changes to KeyInfo and AndroidKeyStoreSecretKeyFactorySpi. Bug: 239632930 Test: see I05f3b7e5c139471febe5c266a39e3dc3bca4831f Change-Id: Ifcfd0b8f1bf440ef1ac80a9ac2b0e9c7f62106dd --- .../security/keystore/KeyProtection.java | 35 +++++++++++++++++-- .../keystore2/AndroidKeyStoreSpi.java | 6 ++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index c14c3c534cf42..5ab21bc5f4897 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -237,6 +237,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private final boolean mUnlockedDeviceRequired; private final boolean mIsStrongBoxBacked; private final int mMaxUsageCount; + private final boolean mRollbackResistant; private KeyProtection( Date keyValidityStart, @@ -259,7 +260,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { boolean userConfirmationRequired, boolean unlockedDeviceRequired, boolean isStrongBoxBacked, - int maxUsageCount) { + int maxUsageCount, + boolean rollbackResistant) { mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart); mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd); mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); @@ -283,6 +285,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mUnlockedDeviceRequired = unlockedDeviceRequired; mIsStrongBoxBacked = isStrongBoxBacked; mMaxUsageCount = maxUsageCount; + mRollbackResistant = rollbackResistant; } /** @@ -562,6 +565,17 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { return mMaxUsageCount; } + /** + * Returns {@code true} if the key is rollback-resistant, meaning that when deleted it is + * guaranteed to be permanently deleted and unusable. + * + * @see Builder#setRollbackResistant(boolean) + * @hide + */ + public boolean isRollbackResistant() { + return mRollbackResistant; + } + /** * Builder of {@link KeyProtection} instances. */ @@ -591,6 +605,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private boolean mIsStrongBoxBacked = false; private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT; private String mAttestKeyAlias = null; + private boolean mRollbackResistant = false; /** * Creates a new instance of the {@code Builder}. @@ -1096,6 +1111,21 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { throw new IllegalArgumentException("maxUsageCount is not valid"); } + /** + * Sets whether the key should be rollback-resistant, meaning that when deleted it is + * guaranteed to be permanently deleted and unusable. Not all implementations support + * rollback-resistant keys. This method is hidden because implementations only support a + * limited number of rollback-resistant keys; currently the available space is reserved for + * critical system keys. + * + * @hide + */ + @NonNull + public Builder setRollbackResistant(boolean rollbackResistant) { + mRollbackResistant = rollbackResistant; + return this; + } + /** * Builds an instance of {@link KeyProtection}. * @@ -1124,7 +1154,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mUserConfirmationRequired, mUnlockedDeviceRequired, mIsStrongBoxBacked, - mMaxUsageCount); + mMaxUsageCount, + mRollbackResistant); } } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 33411e1ec5b97..dfda356ec35bc 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -783,6 +783,12 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { params.getMaxUsageCount() )); } + + if (params.isRollbackResistant()) { + importArgs.add(KeyStore2ParameterUtils.makeBool( + KeymasterDefs.KM_TAG_ROLLBACK_RESISTANT + )); + } } catch (IllegalArgumentException | IllegalStateException e) { throw new KeyStoreException(e); } From 0f7dea9083a8ce11bf876ce4f8610bb286af6a2e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 23 Jul 2022 00:28:02 +0000 Subject: [PATCH 2/2] Request rollback resistance for SP protector keys When supported, use rollback resistance for synthetic password (SP) protector keys, i.e. the Keystore keys that encrypt the SP for a user. This allows SP protectors to be securely deleted on devices that don't support Weaver, but do support rollback-resistant Keystore keys. Secure deletion of SP protectors is necessary to guarantee that user data cannot be accessed using old LSKFs or deactivated escrow tokens. This also aligns LockSettingsService with vold, which already uses rollback-resistant Keystore keys (when supported) to encrypt all storage keys that aren't bound to the SP. Bug: 239632930 Test: atest com.android.server.locksettings (on Cuttlefish) Test: On Cuttlefish, which doesn't support rollback-resistant keys, set an LSKF and checked for the expected log message from SyntheticPasswordCrypto ("Rollback-resistant keys unavailable.."). Test: On Raven, which supports rollback-resistant keys, set an LSKF and checked for the expected log message from SyntheticPasswordCrypto ("Using rollback-resistant key"). Also made some temporary changes to log the rollback resistance property of the key, and verified that it really got set. Change-Id: I05f3b7e5c139471febe5c266a39e3dc3bca4831f --- .../locksettings/SyntheticPasswordCrypto.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java index 371ef76b1ba6c..b06af8e5a385e 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java @@ -17,6 +17,7 @@ package com.android.server.locksettings; import android.security.AndroidKeyStoreMaintenance; +import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; @@ -210,10 +211,26 @@ public class SyntheticPasswordCrypto { .setBoundToSpecificSecureUserId(sid) .setUserAuthenticationValidityDurationSeconds(USER_AUTHENTICATION_VALIDITY); } + final KeyProtection protNonRollbackResistant = builder.build(); + builder.setRollbackResistant(true); + final KeyProtection protRollbackResistant = builder.build(); + final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(keyStoreKey); + try { + keyStore.setEntry(keyAlias, entry, protRollbackResistant); + Slog.i(TAG, "Using rollback-resistant key"); + } catch (KeyStoreException e) { + if (!(e.getCause() instanceof android.security.KeyStoreException)) { + throw e; + } + int errorCode = ((android.security.KeyStoreException) e.getCause()).getErrorCode(); + if (errorCode != KeymasterDefs.KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE) { + throw e; + } + Slog.w(TAG, "Rollback-resistant keys unavailable. Falling back to " + + "non-rollback-resistant key"); + keyStore.setEntry(keyAlias, entry, protNonRollbackResistant); + } - keyStore.setEntry(keyAlias, - new KeyStore.SecretKeyEntry(keyStoreKey), - builder.build()); byte[] intermediate = encrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, data); return encrypt(keyStoreKey, intermediate); } catch (CertificateException | IOException | BadPaddingException