diff options
| author | 2021-04-29 22:43:19 +0000 | |
|---|---|---|
| committer | 2021-04-29 22:43:19 +0000 | |
| commit | fc7a26cd217115010231aae6a31a8f6efea904b1 (patch) | |
| tree | 0d86f690f12e40e494d4a2cd7b71dd62cfd4dc9e | |
| parent | bce6d733a88b404f42da3422e179033c87085d4a (diff) | |
| parent | b8e54262667b83634465d4f919bf0606e575976a (diff) | |
Merge "LockSettingsService: migrate to its own keystore namespace" am: b8e5426266
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1675900
Change-Id: Ibc8bdbcf832b90e3a5628829a6206374c9504374
5 files changed, 151 insertions, 47 deletions
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index ea1c68d1116e..07ac14f049c2 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -29,6 +29,8 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSW import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback; +import static com.android.internal.widget.LockPatternUtils.PROFILE_KEY_NAME_DECRYPT; +import static com.android.internal.widget.LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT; import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE; @@ -99,6 +101,7 @@ import android.security.keystore.recovery.KeyChainProtectionParams; import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.RecoveryCertPath; import android.security.keystore.recovery.WrappedApplicationKey; +import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; import android.security.keystore2.AndroidKeyStoreProvider; import android.service.gatekeeper.GateKeeperResponse; import android.service.gatekeeper.IGateKeeperService; @@ -153,6 +156,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -225,6 +229,7 @@ public class LockSettingsService extends ILockSettings.Stub { private final SyntheticPasswordManager mSpManager; private final KeyStore mKeyStore; + private final java.security.KeyStore mJavaKeyStore; private final RecoverableKeyStoreManager mRecoverableKeyStoreManager; private ManagedProfilePasswordCache mManagedProfilePasswordCache; @@ -543,16 +548,22 @@ public class LockSettingsService extends ILockSettings.Stub { return Settings.Secure.getIntForUser(contentResolver, keyName, defaultValue, userId); } - public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache() { + public java.security.KeyStore getJavaKeyStore() { try { java.security.KeyStore ks = java.security.KeyStore.getInstance( SyntheticPasswordCrypto.androidKeystoreProviderName()); - ks.load(null); - return new ManagedProfilePasswordCache(ks, getUserManager()); + ks.load(new AndroidKeyStoreLoadStoreParameter( + SyntheticPasswordCrypto.keyNamespace())); + return ks; } catch (Exception e) { throw new IllegalStateException("Cannot load keystore", e); } } + + public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache( + java.security.KeyStore ks) { + return new ManagedProfilePasswordCache(ks, getUserManager()); + } } public LockSettingsService(Context context) { @@ -564,6 +575,7 @@ public class LockSettingsService extends ILockSettings.Stub { mInjector = injector; mContext = injector.getContext(); mKeyStore = injector.getKeyStore(); + mJavaKeyStore = injector.getJavaKeyStore(); mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(); mHandler = injector.getHandler(injector.getServiceThread()); mStrongAuth = injector.getStrongAuth(); @@ -586,7 +598,7 @@ public class LockSettingsService extends ILockSettings.Stub { mStrongAuthTracker.register(mStrongAuth); mSpManager = injector.getSyntheticPasswordManager(mStorage); - mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(); + mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore); mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(), mStorage); @@ -959,6 +971,21 @@ public class LockSettingsService extends ILockSettings.Stub { setString("migrated_wear_lockscreen_disabled", "true", 0); Slog.i(TAG, "Migrated lockscreen_disabled for Wear devices"); } + + if (getString("migrated_keystore_namespace", null, 0) == null) { + boolean success = true; + synchronized (mSpManager) { + success &= mSpManager.migrateKeyNamespace(); + } + success &= migrateProfileLockKeys(); + if (success) { + setString("migrated_keystore_namespace", "true", 0); + Slog.i(TAG, "Migrated keys to LSS namespace"); + } else { + Slog.w(TAG, "Failed to migrate keys to LSS namespace"); + } + } + } private void migrateOldDataAfterSystemReady() { @@ -999,6 +1026,22 @@ public class LockSettingsService extends ILockSettings.Stub { } } + private boolean migrateProfileLockKeys() { + boolean success = true; + final List<UserInfo> users = mUserManager.getUsers(); + final int userCount = users.size(); + for (int i = 0; i < userCount; i++) { + UserInfo user = users.get(i); + if (user.isManagedProfile() && !getSeparateProfileChallengeEnabledInternal(user.id)) { + success &= SyntheticPasswordCrypto.migrateLockSettingsKey( + PROFILE_KEY_NAME_ENCRYPT + user.id); + success &= SyntheticPasswordCrypto.migrateLockSettingsKey( + PROFILE_KEY_NAME_DECRYPT + user.id); + } + } + return success; + } + /** * Returns the lowest password quality that still presents the same UI for entering it. * @@ -1284,11 +1327,8 @@ public class LockSettingsService extends ILockSettings.Stub { byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE, storedData.length); byte[] decryptionResult; - java.security.KeyStore keyStore = java.security.KeyStore.getInstance( - SyntheticPasswordCrypto.androidKeystoreProviderName()); - keyStore.load(null); - SecretKey decryptionKey = (SecretKey) keyStore.getKey( - LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId, null); + SecretKey decryptionKey = (SecretKey) mJavaKeyStore.getKey( + PROFILE_KEY_NAME_DECRYPT + userId, null); Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE); @@ -1744,30 +1784,26 @@ public class LockSettingsService extends ILockSettings.Stub { KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES); keyGenerator.init(new SecureRandom()); SecretKey secretKey = keyGenerator.generateKey(); - java.security.KeyStore keyStore = java.security.KeyStore.getInstance( - SyntheticPasswordCrypto.androidKeystoreProviderName()); - keyStore.load(null); try { - keyStore.setEntry( - LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId, + mJavaKeyStore.setEntry( + PROFILE_KEY_NAME_ENCRYPT + userId, new java.security.KeyStore.SecretKeyEntry(secretKey), new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .build()); - keyStore.setEntry( - LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId, + mJavaKeyStore.setEntry( + PROFILE_KEY_NAME_DECRYPT + userId, new java.security.KeyStore.SecretKeyEntry(secretKey), new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setUserAuthenticationRequired(true) .setUserAuthenticationValidityDurationSeconds(30) - .setCriticalToDeviceEncryption(true) .build()); // Key imported, obtain a reference to it. - SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey( - LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId, null); + SecretKey keyStoreEncryptionKey = (SecretKey) mJavaKeyStore.getKey( + PROFILE_KEY_NAME_ENCRYPT + userId, null); Cipher cipher = Cipher.getInstance( KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE); @@ -1776,10 +1812,10 @@ public class LockSettingsService extends ILockSettings.Stub { iv = cipher.getIV(); } finally { // The original key can now be discarded. - keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId); + mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + userId); } - } catch (CertificateException | UnrecoverableKeyException - | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException + } catch (UnrecoverableKeyException + | BadPaddingException | IllegalBlockSizeException | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) { throw new IllegalStateException("Failed to encrypt key", e); } @@ -2300,13 +2336,9 @@ public class LockSettingsService extends ILockSettings.Stub { private void removeKeystoreProfileKey(int targetUserId) { Slog.i(TAG, "Remove keystore profile key for user: " + targetUserId); try { - java.security.KeyStore keyStore = java.security.KeyStore.getInstance( - SyntheticPasswordCrypto.androidKeystoreProviderName()); - keyStore.load(null); - keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + targetUserId); - keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + targetUserId); - } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException - | IOException e) { + mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + targetUserId); + mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_DECRYPT + targetUserId); + } catch (KeyStoreException e) { // We have tried our best to remove all keys Slog.e(TAG, "Unable to remove keystore profile key for user:" + targetUserId, e); } @@ -3257,6 +3289,12 @@ public class LockSettingsService extends ILockSettings.Stub { pw.println(); pw.decreaseIndent(); + pw.println("Keys in namespace:"); + pw.increaseIndent(); + dumpKeystoreKeys(pw); + pw.println(); + pw.decreaseIndent(); + pw.println("Storage:"); pw.increaseIndent(); mStorage.dump(pw); @@ -3276,6 +3314,18 @@ public class LockSettingsService extends ILockSettings.Stub { pw.decreaseIndent(); } + private void dumpKeystoreKeys(IndentingPrintWriter pw) { + try { + final Enumeration<String> aliases = mJavaKeyStore.aliases(); + while (aliases.hasMoreElements()) { + pw.println(aliases.nextElement()); + } + } catch (KeyStoreException e) { + pw.println("Unable to get keys: " + e.toString()); + Slog.d(TAG, "Dump error", e); + } + } + /** * Cryptographically disable escrow token support for the current user, if the user is not * managed (either user has a profile owner, or if device is managed). Do not disable diff --git a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java b/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java index fa477c8fd35a..672c3f739413 100644 --- a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java +++ b/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java @@ -23,7 +23,6 @@ import android.os.UserManager; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.security.keystore.UserNotAuthenticatedException; -import android.security.keystore2.AndroidKeyStoreSpi; import android.util.Slog; import android.util.SparseArray; @@ -95,11 +94,12 @@ public class ManagedProfilePasswordCache { SecretKey key; try { generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, - AndroidKeyStoreSpi.NAME); + mKeyStore.getProvider()); generator.init(new KeyGenParameterSpec.Builder( keyName, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setKeySize(KEY_LENGTH) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setNamespace(SyntheticPasswordCrypto.keyNamespace()) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) // Generate auth-bound key to user 0 (since we the caller is user 0) .setUserAuthenticationRequired(true) diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java index 35e6489debcf..3386408f32cc 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java @@ -16,8 +16,12 @@ package com.android.server.locksettings; +import android.security.AndroidKeyStoreMaintenance; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; +import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; +import android.system.keystore2.Domain; +import android.system.keystore2.KeyDescriptor; import android.util.Slog; import java.io.ByteArrayOutputStream; @@ -125,9 +129,7 @@ public class SyntheticPasswordCrypto { public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] applicationId) { try { - KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName()); - keyStore.load(null); - + KeyStore keyStore = getKeyStore(); SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null); if (decryptionKey == null) { throw new IllegalStateException("SP key is missing: " + keyAlias); @@ -144,10 +146,20 @@ public class SyntheticPasswordCrypto { return "AndroidKeyStore"; } + static int keyNamespace() { + return KeyProperties.NAMESPACE_LOCKSETTINGS; + } + + private static KeyStore getKeyStore() + throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { + KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName()); + keyStore.load(new AndroidKeyStoreLoadStoreParameter(keyNamespace())); + return keyStore; + } + public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) { try { - KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName()); - keyStore.load(null); + final KeyStore keyStore = getKeyStore(); SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null); if (decryptionKey == null) { @@ -170,8 +182,7 @@ public class SyntheticPasswordCrypto { KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES); keyGenerator.init(AES_KEY_LENGTH * 8, new SecureRandom()); SecretKey secretKey = keyGenerator.generateKey(); - KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName()); - keyStore.load(null); + final KeyStore keyStore = getKeyStore(); KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) @@ -200,8 +211,7 @@ public class SyntheticPasswordCrypto { public static void destroyBlobKey(String keyAlias) { KeyStore keyStore; try { - keyStore = KeyStore.getInstance(androidKeystoreProviderName()); - keyStore.load(null); + keyStore = getKeyStore(); keyStore.deleteEntry(keyAlias); Slog.i(TAG, "SP key deleted: " + keyAlias); } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException @@ -229,4 +239,32 @@ public class SyntheticPasswordCrypto { throw new IllegalStateException("NoSuchAlgorithmException for SHA-512", e); } } + + static boolean migrateLockSettingsKey(String alias) { + final KeyDescriptor legacyKey = new KeyDescriptor(); + legacyKey.domain = Domain.APP; + legacyKey.nspace = KeyProperties.NAMESPACE_APPLICATION; + legacyKey.alias = alias; + + final KeyDescriptor newKey = new KeyDescriptor(); + newKey.domain = Domain.SELINUX; + newKey.nspace = SyntheticPasswordCrypto.keyNamespace(); + newKey.alias = alias; + Slog.i(TAG, "Migrating key " + alias); + int err = AndroidKeyStoreMaintenance.migrateKeyNamespace(legacyKey, newKey); + if (err == 0) { + return true; + } else if (err == AndroidKeyStoreMaintenance.KEY_NOT_FOUND) { + Slog.i(TAG, "Key does not exist"); + // Treat this as a success so we don't migrate again. + return true; + } else if (err == AndroidKeyStoreMaintenance.INVALID_ARGUMENT) { + Slog.i(TAG, "Key already exists"); + // Treat this as a success so we don't migrate again. + return true; + } else { + Slog.e(TAG, String.format("Failed to migrate key: %d", err)); + return false; + } + } } diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index 6b5295f70598..0c182a01585d 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -519,7 +519,7 @@ public class SyntheticPasswordManager { public void removeUser(int userId) { for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) { destroyWeaverSlot(handle, userId); - destroySPBlobKey(getHandleName(handle)); + destroySPBlobKey(getKeyName(handle)); } } @@ -955,7 +955,7 @@ public class SyntheticPasswordManager { } else { secret = authToken.getSyntheticPassword(); } - byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid); + byte[] content = createSPBlob(getKeyName(handle), secret, applicationId, sid); byte[] blob = new byte[content.length + 1 + 1]; /* * We can upgrade from v1 to v2 because that's just a change in the way that @@ -1137,10 +1137,10 @@ public class SyntheticPasswordManager { } final byte[] secret; if (version == SYNTHETIC_PASSWORD_VERSION_V1) { - secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle), + secret = SyntheticPasswordCrypto.decryptBlobV1(getKeyName(handle), Arrays.copyOfRange(blob, 2, blob.length), applicationId); } else { - secret = decryptSPBlob(getHandleName(handle), + secret = decryptSPBlob(getKeyName(handle), Arrays.copyOfRange(blob, 2, blob.length), applicationId); } if (secret == null) { @@ -1235,7 +1235,7 @@ public class SyntheticPasswordManager { private void destroySyntheticPassword(long handle, int userId) { destroyState(SP_BLOB_NAME, handle, userId); - destroySPBlobKey(getHandleName(handle)); + destroySPBlobKey(getKeyName(handle)); if (hasState(WEAVER_SLOT_NAME, handle, userId)) { destroyWeaverSlot(handle, userId); } @@ -1351,7 +1351,7 @@ public class SyntheticPasswordManager { } } - private String getHandleName(long handle) { + private String getKeyName(long handle) { return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle); } @@ -1412,4 +1412,19 @@ public class SyntheticPasswordManager { } return hexBytes; } + + /** + * Migrate all existing SP keystore keys from uid 1000 app domain to LSS selinux domain + */ + public boolean migrateKeyNamespace() { + boolean success = true; + final Map<Integer, List<Long>> allHandles = + mStorage.listSyntheticPasswordHandlesForAllUsers(SP_BLOB_NAME); + for (List<Long> userHandles : allHandles.values()) { + for (long handle : userHandles) { + success &= SyntheticPasswordCrypto.migrateLockSettingsKey(getKeyName(handle)); + } + } + return success; + } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java index 41562bb52a8d..a8b10f62d89a 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -155,7 +155,8 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @Override - public ManagedProfilePasswordCache getManagedProfilePasswordCache() { + public ManagedProfilePasswordCache getManagedProfilePasswordCache( + java.security.KeyStore ks) { return mock(ManagedProfilePasswordCache.class); } |