Merge "LockSettingsService: migrate to its own keystore namespace"
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ea1c68d..07ac14f 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_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.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.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 @@
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 @@
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 @@
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 @@
mStrongAuthTracker.register(mStrongAuth);
mSpManager = injector.getSyntheticPasswordManager(mStorage);
- mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache();
+ mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore);
mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
mStorage);
@@ -959,6 +971,21 @@
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 @@
}
}
+ 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 fa477c8..672c3f7 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.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 @@
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 35e6489..3386408 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 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 @@
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 @@
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 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 @@
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 6b5295f..0c182a0 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 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 @@
} 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 @@
}
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 @@
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 @@
}
}
- 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 @@
}
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 41562bb..a8b10f6 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 @@
}
@Override
- public ManagedProfilePasswordCache getManagedProfilePasswordCache() {
+ public ManagedProfilePasswordCache getManagedProfilePasswordCache(
+ java.security.KeyStore ks) {
return mock(ManagedProfilePasswordCache.class);
}