Loading services/core/java/com/android/server/locksettings/LockSettingsService.java +79 −29 Original line number Diff line number Diff line Loading @@ -30,6 +30,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; Loading Loading @@ -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; Loading Loading @@ -155,6 +158,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; Loading Loading @@ -226,6 +230,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; Loading Loading @@ -535,16 +540,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) { Loading @@ -556,6 +567,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(); Loading @@ -580,7 +592,7 @@ public class LockSettingsService extends ILockSettings.Stub { mRandom = new SecureRandom(); mSpManager = injector.getSyntheticPasswordManager(mStorage); mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(); mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore); mBiometricDeferredQueue = new BiometricDeferredQueue(mContext, mSpManager, mHandler); mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(), Loading Loading @@ -955,6 +967,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() { Loading Loading @@ -995,6 +1022,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. * Loading Loading @@ -1286,11 +1329,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); Loading Loading @@ -1880,30 +1920,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); Loading @@ -1912,10 +1948,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); } Loading Loading @@ -2460,13 +2496,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); } Loading Loading @@ -3420,6 +3452,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); Loading @@ -3441,6 +3479,18 @@ public class LockSettingsService extends ILockSettings.Stub { pw.println("PasswordHandleCount: " + mGatekeeperPasswords.size()); } 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 Loading services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java +2 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) Loading services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java +47 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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) { Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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; } } } services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +21 −6 Original line number Diff line number Diff line Loading @@ -522,7 +522,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)); } } Loading Loading @@ -958,7 +958,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 Loading Loading @@ -1141,10 +1141,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) { Loading Loading @@ -1247,7 +1247,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); } Loading Loading @@ -1363,7 +1363,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); } Loading Loading @@ -1424,4 +1424,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; } } services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +2 −1 Original line number Diff line number Diff line Loading @@ -155,7 +155,8 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @Override public ManagedProfilePasswordCache getManagedProfilePasswordCache() { public ManagedProfilePasswordCache getManagedProfilePasswordCache( java.security.KeyStore ks) { return mock(ManagedProfilePasswordCache.class); } Loading Loading
services/core/java/com/android/server/locksettings/LockSettingsService.java +79 −29 Original line number Diff line number Diff line Loading @@ -30,6 +30,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; Loading Loading @@ -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; Loading Loading @@ -155,6 +158,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; Loading Loading @@ -226,6 +230,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; Loading Loading @@ -535,16 +540,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) { Loading @@ -556,6 +567,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(); Loading @@ -580,7 +592,7 @@ public class LockSettingsService extends ILockSettings.Stub { mRandom = new SecureRandom(); mSpManager = injector.getSyntheticPasswordManager(mStorage); mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(); mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore); mBiometricDeferredQueue = new BiometricDeferredQueue(mContext, mSpManager, mHandler); mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(), Loading Loading @@ -955,6 +967,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() { Loading Loading @@ -995,6 +1022,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. * Loading Loading @@ -1286,11 +1329,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); Loading Loading @@ -1880,30 +1920,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); Loading @@ -1912,10 +1948,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); } Loading Loading @@ -2460,13 +2496,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); } Loading Loading @@ -3420,6 +3452,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); Loading @@ -3441,6 +3479,18 @@ public class LockSettingsService extends ILockSettings.Stub { pw.println("PasswordHandleCount: " + mGatekeeperPasswords.size()); } 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 Loading
services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java +2 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) Loading
services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java +47 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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) { Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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; } } }
services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +21 −6 Original line number Diff line number Diff line Loading @@ -522,7 +522,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)); } } Loading Loading @@ -958,7 +958,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 Loading Loading @@ -1141,10 +1141,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) { Loading Loading @@ -1247,7 +1247,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); } Loading Loading @@ -1363,7 +1363,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); } Loading Loading @@ -1424,4 +1424,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; } }
services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +2 −1 Original line number Diff line number Diff line Loading @@ -155,7 +155,8 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @Override public ManagedProfilePasswordCache getManagedProfilePasswordCache() { public ManagedProfilePasswordCache getManagedProfilePasswordCache( java.security.KeyStore ks) { return mock(ManagedProfilePasswordCache.class); } Loading