LockSettingsService: migrate to its own keystore namespace
Moves keystore keys used by LockSettingsService to a separate namespace, migrating existing key during boot if needed. Affected keys are encryption keys for synthetic password blob, unified profile challenge encryption key and unified profile challenge cache key. Bug: 184664830 Test: atest com.android.server.locksettings Change-Id: Iac339e64812232875afa9dc6109b848b50b0ef2f
This commit is contained in:
@@ -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_PATTERN;
|
||||||
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
|
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.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.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_AFTER_LOCKOUT;
|
||||||
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
|
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.KeyChainSnapshot;
|
||||||
import android.security.keystore.recovery.RecoveryCertPath;
|
import android.security.keystore.recovery.RecoveryCertPath;
|
||||||
import android.security.keystore.recovery.WrappedApplicationKey;
|
import android.security.keystore.recovery.WrappedApplicationKey;
|
||||||
|
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
|
||||||
import android.security.keystore2.AndroidKeyStoreProvider;
|
import android.security.keystore2.AndroidKeyStoreProvider;
|
||||||
import android.service.gatekeeper.GateKeeperResponse;
|
import android.service.gatekeeper.GateKeeperResponse;
|
||||||
import android.service.gatekeeper.IGateKeeperService;
|
import android.service.gatekeeper.IGateKeeperService;
|
||||||
@@ -155,6 +158,7 @@ import java.text.SimpleDateFormat;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
@@ -226,6 +230,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
private final SyntheticPasswordManager mSpManager;
|
private final SyntheticPasswordManager mSpManager;
|
||||||
|
|
||||||
private final KeyStore mKeyStore;
|
private final KeyStore mKeyStore;
|
||||||
|
private final java.security.KeyStore mJavaKeyStore;
|
||||||
private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
|
private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
|
||||||
private ManagedProfilePasswordCache mManagedProfilePasswordCache;
|
private ManagedProfilePasswordCache mManagedProfilePasswordCache;
|
||||||
|
|
||||||
@@ -535,16 +540,22 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
return Settings.Secure.getIntForUser(contentResolver, keyName, defaultValue, userId);
|
return Settings.Secure.getIntForUser(contentResolver, keyName, defaultValue, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache() {
|
public java.security.KeyStore getJavaKeyStore() {
|
||||||
try {
|
try {
|
||||||
java.security.KeyStore ks = java.security.KeyStore.getInstance(
|
java.security.KeyStore ks = java.security.KeyStore.getInstance(
|
||||||
SyntheticPasswordCrypto.androidKeystoreProviderName());
|
SyntheticPasswordCrypto.androidKeystoreProviderName());
|
||||||
ks.load(null);
|
ks.load(new AndroidKeyStoreLoadStoreParameter(
|
||||||
return new ManagedProfilePasswordCache(ks, getUserManager());
|
SyntheticPasswordCrypto.keyNamespace()));
|
||||||
|
return ks;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IllegalStateException("Cannot load keystore", 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) {
|
public LockSettingsService(Context context) {
|
||||||
@@ -556,6 +567,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
mInjector = injector;
|
mInjector = injector;
|
||||||
mContext = injector.getContext();
|
mContext = injector.getContext();
|
||||||
mKeyStore = injector.getKeyStore();
|
mKeyStore = injector.getKeyStore();
|
||||||
|
mJavaKeyStore = injector.getJavaKeyStore();
|
||||||
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
|
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
|
||||||
mHandler = injector.getHandler(injector.getServiceThread());
|
mHandler = injector.getHandler(injector.getServiceThread());
|
||||||
mStrongAuth = injector.getStrongAuth();
|
mStrongAuth = injector.getStrongAuth();
|
||||||
@@ -580,7 +592,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
mRandom = new SecureRandom();
|
mRandom = new SecureRandom();
|
||||||
|
|
||||||
mSpManager = injector.getSyntheticPasswordManager(mStorage);
|
mSpManager = injector.getSyntheticPasswordManager(mStorage);
|
||||||
mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache();
|
mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore);
|
||||||
mBiometricDeferredQueue = new BiometricDeferredQueue(mContext, mSpManager, mHandler);
|
mBiometricDeferredQueue = new BiometricDeferredQueue(mContext, mSpManager, mHandler);
|
||||||
|
|
||||||
mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
|
mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
|
||||||
@@ -955,6 +967,21 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
setString("migrated_wear_lockscreen_disabled", "true", 0);
|
setString("migrated_wear_lockscreen_disabled", "true", 0);
|
||||||
Slog.i(TAG, "Migrated lockscreen_disabled for Wear devices");
|
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() {
|
private void migrateOldDataAfterSystemReady() {
|
||||||
@@ -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.
|
* Returns the lowest password quality that still presents the same UI for entering it.
|
||||||
*
|
*
|
||||||
@@ -1286,11 +1329,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE,
|
byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE,
|
||||||
storedData.length);
|
storedData.length);
|
||||||
byte[] decryptionResult;
|
byte[] decryptionResult;
|
||||||
java.security.KeyStore keyStore = java.security.KeyStore.getInstance(
|
SecretKey decryptionKey = (SecretKey) mJavaKeyStore.getKey(
|
||||||
SyntheticPasswordCrypto.androidKeystoreProviderName());
|
PROFILE_KEY_NAME_DECRYPT + userId, null);
|
||||||
keyStore.load(null);
|
|
||||||
SecretKey decryptionKey = (SecretKey) keyStore.getKey(
|
|
||||||
LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId, null);
|
|
||||||
|
|
||||||
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
|
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
|
||||||
+ KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
|
+ KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
|
||||||
@@ -1880,30 +1920,26 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
|
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
|
||||||
keyGenerator.init(new SecureRandom());
|
keyGenerator.init(new SecureRandom());
|
||||||
SecretKey secretKey = keyGenerator.generateKey();
|
SecretKey secretKey = keyGenerator.generateKey();
|
||||||
java.security.KeyStore keyStore = java.security.KeyStore.getInstance(
|
|
||||||
SyntheticPasswordCrypto.androidKeystoreProviderName());
|
|
||||||
keyStore.load(null);
|
|
||||||
try {
|
try {
|
||||||
keyStore.setEntry(
|
mJavaKeyStore.setEntry(
|
||||||
LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId,
|
PROFILE_KEY_NAME_ENCRYPT + userId,
|
||||||
new java.security.KeyStore.SecretKeyEntry(secretKey),
|
new java.security.KeyStore.SecretKeyEntry(secretKey),
|
||||||
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
|
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
|
||||||
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
||||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
||||||
.build());
|
.build());
|
||||||
keyStore.setEntry(
|
mJavaKeyStore.setEntry(
|
||||||
LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + userId,
|
PROFILE_KEY_NAME_DECRYPT + userId,
|
||||||
new java.security.KeyStore.SecretKeyEntry(secretKey),
|
new java.security.KeyStore.SecretKeyEntry(secretKey),
|
||||||
new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
|
new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
|
||||||
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
||||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
||||||
.setUserAuthenticationRequired(true)
|
.setUserAuthenticationRequired(true)
|
||||||
.setUserAuthenticationValidityDurationSeconds(30)
|
.setUserAuthenticationValidityDurationSeconds(30)
|
||||||
.setCriticalToDeviceEncryption(true)
|
|
||||||
.build());
|
.build());
|
||||||
// Key imported, obtain a reference to it.
|
// Key imported, obtain a reference to it.
|
||||||
SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey(
|
SecretKey keyStoreEncryptionKey = (SecretKey) mJavaKeyStore.getKey(
|
||||||
LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userId, null);
|
PROFILE_KEY_NAME_ENCRYPT + userId, null);
|
||||||
Cipher cipher = Cipher.getInstance(
|
Cipher cipher = Cipher.getInstance(
|
||||||
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
|
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
|
||||||
+ KeyProperties.ENCRYPTION_PADDING_NONE);
|
+ KeyProperties.ENCRYPTION_PADDING_NONE);
|
||||||
@@ -1912,10 +1948,10 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
iv = cipher.getIV();
|
iv = cipher.getIV();
|
||||||
} finally {
|
} finally {
|
||||||
// The original key can now be discarded.
|
// 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
|
} catch (UnrecoverableKeyException
|
||||||
| IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException
|
| BadPaddingException | IllegalBlockSizeException | KeyStoreException
|
||||||
| NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
|
| NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
|
||||||
throw new IllegalStateException("Failed to encrypt key", e);
|
throw new IllegalStateException("Failed to encrypt key", e);
|
||||||
}
|
}
|
||||||
@@ -2460,13 +2496,9 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
private void removeKeystoreProfileKey(int targetUserId) {
|
private void removeKeystoreProfileKey(int targetUserId) {
|
||||||
Slog.i(TAG, "Remove keystore profile key for user: " + targetUserId);
|
Slog.i(TAG, "Remove keystore profile key for user: " + targetUserId);
|
||||||
try {
|
try {
|
||||||
java.security.KeyStore keyStore = java.security.KeyStore.getInstance(
|
mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + targetUserId);
|
||||||
SyntheticPasswordCrypto.androidKeystoreProviderName());
|
mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_DECRYPT + targetUserId);
|
||||||
keyStore.load(null);
|
} catch (KeyStoreException e) {
|
||||||
keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + targetUserId);
|
|
||||||
keyStore.deleteEntry(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT + targetUserId);
|
|
||||||
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
|
|
||||||
| IOException e) {
|
|
||||||
// We have tried our best to remove all keys
|
// We have tried our best to remove all keys
|
||||||
Slog.e(TAG, "Unable to remove keystore profile key for user:" + targetUserId, e);
|
Slog.e(TAG, "Unable to remove keystore profile key for user:" + targetUserId, e);
|
||||||
}
|
}
|
||||||
@@ -3420,6 +3452,12 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
pw.println();
|
pw.println();
|
||||||
pw.decreaseIndent();
|
pw.decreaseIndent();
|
||||||
|
|
||||||
|
pw.println("Keys in namespace:");
|
||||||
|
pw.increaseIndent();
|
||||||
|
dumpKeystoreKeys(pw);
|
||||||
|
pw.println();
|
||||||
|
pw.decreaseIndent();
|
||||||
|
|
||||||
pw.println("Storage:");
|
pw.println("Storage:");
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
mStorage.dump(pw);
|
mStorage.dump(pw);
|
||||||
@@ -3441,6 +3479,18 @@ public class LockSettingsService extends ILockSettings.Stub {
|
|||||||
pw.println("PasswordHandleCount: " + mGatekeeperPasswords.size());
|
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
|
* 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
|
* managed (either user has a profile owner, or if device is managed). Do not disable
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import android.os.UserManager;
|
|||||||
import android.security.keystore.KeyGenParameterSpec;
|
import android.security.keystore.KeyGenParameterSpec;
|
||||||
import android.security.keystore.KeyProperties;
|
import android.security.keystore.KeyProperties;
|
||||||
import android.security.keystore.UserNotAuthenticatedException;
|
import android.security.keystore.UserNotAuthenticatedException;
|
||||||
import android.security.keystore2.AndroidKeyStoreSpi;
|
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
@@ -95,11 +94,12 @@ public class ManagedProfilePasswordCache {
|
|||||||
SecretKey key;
|
SecretKey key;
|
||||||
try {
|
try {
|
||||||
generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
|
generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
|
||||||
AndroidKeyStoreSpi.NAME);
|
mKeyStore.getProvider());
|
||||||
generator.init(new KeyGenParameterSpec.Builder(
|
generator.init(new KeyGenParameterSpec.Builder(
|
||||||
keyName, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
|
keyName, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
|
||||||
.setKeySize(KEY_LENGTH)
|
.setKeySize(KEY_LENGTH)
|
||||||
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
||||||
|
.setNamespace(SyntheticPasswordCrypto.keyNamespace())
|
||||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
||||||
// Generate auth-bound key to user 0 (since we the caller is user 0)
|
// Generate auth-bound key to user 0 (since we the caller is user 0)
|
||||||
.setUserAuthenticationRequired(true)
|
.setUserAuthenticationRequired(true)
|
||||||
|
|||||||
@@ -16,8 +16,12 @@
|
|||||||
|
|
||||||
package com.android.server.locksettings;
|
package com.android.server.locksettings;
|
||||||
|
|
||||||
|
import android.security.AndroidKeyStoreMaintenance;
|
||||||
import android.security.keystore.KeyProperties;
|
import android.security.keystore.KeyProperties;
|
||||||
import android.security.keystore.KeyProtection;
|
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 android.util.Slog;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -125,9 +129,7 @@ public class SyntheticPasswordCrypto {
|
|||||||
|
|
||||||
public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] applicationId) {
|
public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] applicationId) {
|
||||||
try {
|
try {
|
||||||
KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName());
|
KeyStore keyStore = getKeyStore();
|
||||||
keyStore.load(null);
|
|
||||||
|
|
||||||
SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
|
SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
|
||||||
if (decryptionKey == null) {
|
if (decryptionKey == null) {
|
||||||
throw new IllegalStateException("SP key is missing: " + keyAlias);
|
throw new IllegalStateException("SP key is missing: " + keyAlias);
|
||||||
@@ -144,10 +146,20 @@ public class SyntheticPasswordCrypto {
|
|||||||
return "AndroidKeyStore";
|
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) {
|
public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) {
|
||||||
try {
|
try {
|
||||||
KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName());
|
final KeyStore keyStore = getKeyStore();
|
||||||
keyStore.load(null);
|
|
||||||
|
|
||||||
SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
|
SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
|
||||||
if (decryptionKey == null) {
|
if (decryptionKey == null) {
|
||||||
@@ -170,8 +182,7 @@ public class SyntheticPasswordCrypto {
|
|||||||
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
|
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
|
||||||
keyGenerator.init(AES_KEY_LENGTH * 8, new SecureRandom());
|
keyGenerator.init(AES_KEY_LENGTH * 8, new SecureRandom());
|
||||||
SecretKey secretKey = keyGenerator.generateKey();
|
SecretKey secretKey = keyGenerator.generateKey();
|
||||||
KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName());
|
final KeyStore keyStore = getKeyStore();
|
||||||
keyStore.load(null);
|
|
||||||
KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
|
KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
|
||||||
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
||||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
||||||
@@ -200,8 +211,7 @@ public class SyntheticPasswordCrypto {
|
|||||||
public static void destroyBlobKey(String keyAlias) {
|
public static void destroyBlobKey(String keyAlias) {
|
||||||
KeyStore keyStore;
|
KeyStore keyStore;
|
||||||
try {
|
try {
|
||||||
keyStore = KeyStore.getInstance(androidKeystoreProviderName());
|
keyStore = getKeyStore();
|
||||||
keyStore.load(null);
|
|
||||||
keyStore.deleteEntry(keyAlias);
|
keyStore.deleteEntry(keyAlias);
|
||||||
Slog.i(TAG, "SP key deleted: " + keyAlias);
|
Slog.i(TAG, "SP key deleted: " + keyAlias);
|
||||||
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
|
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
|
||||||
@@ -229,4 +239,32 @@ public class SyntheticPasswordCrypto {
|
|||||||
throw new IllegalStateException("NoSuchAlgorithmException for SHA-512", e);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -522,7 +522,7 @@ public class SyntheticPasswordManager {
|
|||||||
public void removeUser(int userId) {
|
public void removeUser(int userId) {
|
||||||
for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
|
for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
|
||||||
destroyWeaverSlot(handle, userId);
|
destroyWeaverSlot(handle, userId);
|
||||||
destroySPBlobKey(getHandleName(handle));
|
destroySPBlobKey(getKeyName(handle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,7 +958,7 @@ public class SyntheticPasswordManager {
|
|||||||
} else {
|
} else {
|
||||||
secret = authToken.getSyntheticPassword();
|
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];
|
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
|
* We can upgrade from v1 to v2 because that's just a change in the way that
|
||||||
@@ -1141,10 +1141,10 @@ public class SyntheticPasswordManager {
|
|||||||
}
|
}
|
||||||
final byte[] secret;
|
final byte[] secret;
|
||||||
if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
|
if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
|
||||||
secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
|
secret = SyntheticPasswordCrypto.decryptBlobV1(getKeyName(handle),
|
||||||
Arrays.copyOfRange(blob, 2, blob.length), applicationId);
|
Arrays.copyOfRange(blob, 2, blob.length), applicationId);
|
||||||
} else {
|
} else {
|
||||||
secret = decryptSPBlob(getHandleName(handle),
|
secret = decryptSPBlob(getKeyName(handle),
|
||||||
Arrays.copyOfRange(blob, 2, blob.length), applicationId);
|
Arrays.copyOfRange(blob, 2, blob.length), applicationId);
|
||||||
}
|
}
|
||||||
if (secret == null) {
|
if (secret == null) {
|
||||||
@@ -1247,7 +1247,7 @@ public class SyntheticPasswordManager {
|
|||||||
|
|
||||||
private void destroySyntheticPassword(long handle, int userId) {
|
private void destroySyntheticPassword(long handle, int userId) {
|
||||||
destroyState(SP_BLOB_NAME, handle, userId);
|
destroyState(SP_BLOB_NAME, handle, userId);
|
||||||
destroySPBlobKey(getHandleName(handle));
|
destroySPBlobKey(getKeyName(handle));
|
||||||
if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
|
if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
|
||||||
destroyWeaverSlot(handle, userId);
|
destroyWeaverSlot(handle, userId);
|
||||||
}
|
}
|
||||||
@@ -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);
|
return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1424,4 +1424,19 @@ public class SyntheticPasswordManager {
|
|||||||
}
|
}
|
||||||
return hexBytes;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,8 @@ public class LockSettingsServiceTestable extends LockSettingsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ManagedProfilePasswordCache getManagedProfilePasswordCache() {
|
public ManagedProfilePasswordCache getManagedProfilePasswordCache(
|
||||||
|
java.security.KeyStore ks) {
|
||||||
return mock(ManagedProfilePasswordCache.class);
|
return mock(ManagedProfilePasswordCache.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user