From 995c75399eb63032ec79008746028dd3c974ce14 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 30 Jul 2022 01:49:02 +0000 Subject: [PATCH] Clean up protector key terminology Introduce the term "protector key" to refer to the Keystore key that encrypts the spblob file for a protector. Consistently use it everywhere. Previously these keys were variously refered to as synthetic password keys, blob keys, or simply Keystore keys. This aligns with updates I've made to http://go/android-locksettings-design. Reduce the visibility of PROFILE_KEY_NAME_* and SYNTHETIC_PASSWORD_KEY_PREFIX (the latter is also renamed to PROTECTOR_KEY_ALIAS_PREFIX), as they are no longer used elsewhere. This is a follow-up to change I564a738119a47a31b4822d26c6405249f8ce1c06 that addresses one of the code review comments. No change in behavior intended; this is just a cleanup. Change-Id: I69184f7b47b17f0ab25cfa939bf92feb727ba220 --- .../internal/widget/LockPatternUtils.java | 4 -- .../locksettings/LockSettingsService.java | 5 +- .../locksettings/SyntheticPasswordCrypto.java | 49 ++++++++++--------- .../SyntheticPasswordManager.java | 40 +++++++++------ .../MockSyntheticPasswordManager.java | 16 +++--- 5 files changed, 63 insertions(+), 51 deletions(-) diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index cc076ab95ebef..3e0a6cb1cae0f 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -166,10 +166,6 @@ public class LockPatternUtils { private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents"; private static final String IS_TRUST_USUALLY_MANAGED = "lockscreen.istrustusuallymanaged"; - public static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_"; - public static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_"; - public static final String SYNTHETIC_PASSWORD_KEY_PREFIX = "synthetic_password_"; - public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle"; public static final String PASSWORD_HISTORY_DELIMITER = ","; diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 9b6d0e7c09e49..7103b8fcd9a6d 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -35,8 +35,6 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTE import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; import static com.android.internal.widget.LockPatternUtils.CURRENT_LSKF_BASED_PROTECTOR_ID_KEY; 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.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.USER_FRP; @@ -211,6 +209,9 @@ public class LockSettingsService extends ILockSettings.Stub { // user's credential must be presented again, e.g. via ConfirmLockPattern/ConfirmLockPassword. private static final int GK_PW_HANDLE_STORE_DURATION_MS = 10 * 60 * 1000; // 10 minutes + private static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_"; + private static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_"; + // Order of holding lock: mSeparateChallengeLock -> mSpManager -> this // Do not call into ActivityManager while holding mSpManager lock. private final Object mSeparateChallengeLock = new Object(); diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java index 8ee2151649c01..30caefb5b7084 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java @@ -129,18 +129,20 @@ public class SyntheticPasswordCrypto { } /** - * Decrypt a legacy SP blob which did the Keystore and software encryption layers in the wrong + * Decrypts a legacy SP blob which did the Keystore and software encryption layers in the wrong * order. */ - public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] protectorSecret) { + public static byte[] decryptBlobV1(String protectorKeyAlias, byte[] blob, + byte[] protectorSecret) { try { KeyStore keyStore = getKeyStore(); - SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null); - if (keyStoreKey == null) { - throw new IllegalStateException("SP key is missing: " + keyAlias); + SecretKey protectorKey = (SecretKey) keyStore.getKey(protectorKeyAlias, null); + if (protectorKey == null) { + throw new IllegalStateException("SP protector key is missing: " + + protectorKeyAlias); } byte[] intermediate = decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, blob); - return decrypt(keyStoreKey, intermediate); + return decrypt(protectorKey, intermediate); } catch (Exception e) { Slog.e(TAG, "Failed to decrypt V1 blob", e); throw new IllegalStateException("Failed to decrypt blob", e); @@ -165,15 +167,17 @@ public class SyntheticPasswordCrypto { /** * Decrypts an SP blob that was created by {@link #createBlob}. */ - public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] protectorSecret) { + public static byte[] decryptBlob(String protectorKeyAlias, byte[] blob, + byte[] protectorSecret) { try { final KeyStore keyStore = getKeyStore(); - SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null); - if (keyStoreKey == null) { - throw new IllegalStateException("SP key is missing: " + keyAlias); + SecretKey protectorKey = (SecretKey) keyStore.getKey(protectorKeyAlias, null); + if (protectorKey == null) { + throw new IllegalStateException("SP protector key is missing: " + + protectorKeyAlias); } - byte[] intermediate = decrypt(keyStoreKey, blob); + byte[] intermediate = decrypt(protectorKey, blob); return decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, intermediate); } catch (CertificateException | IOException | BadPaddingException | IllegalBlockSizeException @@ -187,20 +191,21 @@ public class SyntheticPasswordCrypto { /** * Creates a new SP blob by encrypting the given data. Two encryption layers are applied: an - * inner layer using a hash of protectorSecret as the key, and an outer layer using a new - * Keystore key with the given alias and optionally bound to a SID. + * inner layer using a hash of protectorSecret as the key, and an outer layer using the + * protector key, which is a Keystore key that is optionally bound to a SID. This method + * creates the protector key and stores it under protectorKeyAlias. * * The reason we use a layer of software encryption, instead of using protectorSecret as the * applicationId of the Keystore key, is to work around buggy KeyMint implementations that don't * cryptographically bind the applicationId to the key. The Keystore layer has to be the outer * layer, so that LSKF verification is ratelimited by Gatekeeper when Weaver is unavailable. */ - public static byte[] createBlob(String keyAlias, byte[] data, byte[] protectorSecret, + public static byte[] createBlob(String protectorKeyAlias, byte[] data, byte[] protectorSecret, long sid) { try { KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES); keyGenerator.init(AES_GCM_KEY_SIZE * 8, new SecureRandom()); - SecretKey keyStoreKey = keyGenerator.generateKey(); + SecretKey protectorKey = keyGenerator.generateKey(); final KeyStore keyStore = getKeyStore(); KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) @@ -214,9 +219,9 @@ public class SyntheticPasswordCrypto { final KeyProtection protNonRollbackResistant = builder.build(); builder.setRollbackResistant(true); final KeyProtection protRollbackResistant = builder.build(); - final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(keyStoreKey); + final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(protectorKey); try { - keyStore.setEntry(keyAlias, entry, protRollbackResistant); + keyStore.setEntry(protectorKeyAlias, entry, protRollbackResistant); Slog.i(TAG, "Using rollback-resistant key"); } catch (KeyStoreException e) { if (!(e.getCause() instanceof android.security.KeyStoreException)) { @@ -228,11 +233,11 @@ public class SyntheticPasswordCrypto { } Slog.w(TAG, "Rollback-resistant keys unavailable. Falling back to " + "non-rollback-resistant key"); - keyStore.setEntry(keyAlias, entry, protNonRollbackResistant); + keyStore.setEntry(protectorKeyAlias, entry, protNonRollbackResistant); } byte[] intermediate = encrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, data); - return encrypt(keyStoreKey, intermediate); + return encrypt(protectorKey, intermediate); } catch (CertificateException | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException @@ -243,15 +248,15 @@ public class SyntheticPasswordCrypto { } } - public static void destroyBlobKey(String keyAlias) { + public static void destroyProtectorKey(String keyAlias) { KeyStore keyStore; try { keyStore = getKeyStore(); keyStore.deleteEntry(keyAlias); - Slog.i(TAG, "SP key deleted: " + keyAlias); + Slog.i(TAG, "Deleted SP protector key " + keyAlias); } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) { - Slog.e(TAG, "Failed to destroy blob", e); + Slog.e(TAG, "Failed to delete SP protector key " + keyAlias, e); } } diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index 1f807f9f69ca8..b65ec056b1088 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -135,6 +135,8 @@ public class SyntheticPasswordManager { private static final byte PROTECTOR_TYPE_STRONG_TOKEN_BASED = 1; private static final byte PROTECTOR_TYPE_WEAK_TOKEN_BASED = 2; + private static final String PROTECTOR_KEY_ALIAS_PREFIX = "synthetic_password_"; + // The security strength of the synthetic password, in bytes private static final int SYNTHETIC_PASSWORD_SECURITY_STRENGTH = 256 / 8; @@ -583,7 +585,7 @@ public class SyntheticPasswordManager { for (long protectorId : mStorage.listSyntheticPasswordProtectorsForUser(SP_BLOB_NAME, userId)) { destroyWeaverSlot(protectorId, userId); - destroySPBlobKey(getKeyName(protectorId)); + destroyProtectorKey(getProtectorKeyAlias(protectorId)); } // Remove potential persistent state (in RPMB), to prevent them from accumulating and // causing problems. @@ -999,7 +1001,8 @@ public class SyntheticPasswordManager { } else { spSecret = sp.getSyntheticPassword(); } - byte[] content = createSPBlob(getKeyName(protectorId), spSecret, protectorSecret, sid); + byte[] content = createSpBlob(getProtectorKeyAlias(protectorId), spSecret, protectorSecret, + sid); /* * We can upgrade from v1 to v2 because that's just a change in the way that * the SP is stored. However, we can't upgrade to v3 because that is a change @@ -1210,10 +1213,11 @@ public class SyntheticPasswordManager { } final byte[] spSecret; if (blob.mVersion == SYNTHETIC_PASSWORD_VERSION_V1) { - spSecret = SyntheticPasswordCrypto.decryptBlobV1(getKeyName(protectorId), blob.mContent, - protectorSecret); + spSecret = SyntheticPasswordCrypto.decryptBlobV1(getProtectorKeyAlias(protectorId), + blob.mContent, protectorSecret); } else { - spSecret = decryptSPBlob(getKeyName(protectorId), blob.mContent, protectorSecret); + spSecret = decryptSpBlob(getProtectorKeyAlias(protectorId), blob.mContent, + protectorSecret); } if (spSecret == null) { Slog.e(TAG, "Fail to decrypt SP for user " + userId); @@ -1337,7 +1341,7 @@ public class SyntheticPasswordManager { private void destroyProtectorCommon(long protectorId, int userId) { destroyState(SP_BLOB_NAME, protectorId, userId); - destroySPBlobKey(getKeyName(protectorId)); + destroyProtectorKey(getProtectorKeyAlias(protectorId)); destroyState(SECDISCARDABLE_NAME, protectorId, userId); if (hasState(WEAVER_SLOT_NAME, protectorId, userId)) { destroyWeaverSlot(protectorId, userId); @@ -1428,17 +1432,20 @@ public class SyntheticPasswordManager { mStorage.deleteSyntheticPasswordState(userId, protectorId, stateName); } - protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] protectorSecret) { - return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, protectorSecret); + @VisibleForTesting + protected byte[] decryptSpBlob(String protectorKeyAlias, byte[] blob, byte[] protectorSecret) { + return SyntheticPasswordCrypto.decryptBlob(protectorKeyAlias, blob, protectorSecret); } - protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] protectorSecret, + @VisibleForTesting + protected byte[] createSpBlob(String protectorKeyAlias, byte[] data, byte[] protectorSecret, long sid) { - return SyntheticPasswordCrypto.createBlob(blobKeyName, data, protectorSecret, sid); + return SyntheticPasswordCrypto.createBlob(protectorKeyAlias, data, protectorSecret, sid); } - protected void destroySPBlobKey(String keyAlias) { - SyntheticPasswordCrypto.destroyBlobKey(keyAlias); + @VisibleForTesting + protected void destroyProtectorKey(String keyAlias) { + SyntheticPasswordCrypto.destroyProtectorKey(keyAlias); } public static long generateProtectorId() { @@ -1463,8 +1470,8 @@ public class SyntheticPasswordManager { } } - private String getKeyName(long protectorId) { - return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, protectorId); + private String getProtectorKeyAlias(long protectorId) { + return String.format("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId); } private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) { @@ -1517,7 +1524,7 @@ public class SyntheticPasswordManager { } /** - * Migrate all existing SP keystore keys from uid 1000 app domain to LSS selinux domain + * Migrates all existing SP protector keys from uid 1000 app domain to LSS selinux domain. */ public boolean migrateKeyNamespace() { boolean success = true; @@ -1525,7 +1532,8 @@ public class SyntheticPasswordManager { mStorage.listSyntheticPasswordProtectorsForAllUsers(SP_BLOB_NAME); for (List userProtectors : allProtectors.values()) { for (long protectorId : userProtectors) { - success &= SyntheticPasswordCrypto.migrateLockSettingsKey(getKeyName(protectorId)); + success &= SyntheticPasswordCrypto.migrateLockSettingsKey( + getProtectorKeyAlias(protectorId)); } } return success; diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java index 0bb20215111b9..186a04f9f5466 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java @@ -44,12 +44,14 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager { mGateKeeper = gatekeeper; } - private ArrayMap mBlobs = new ArrayMap<>(); + private final ArrayMap mBlobs = new ArrayMap<>(); @Override - protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] protectorSecret) { - if (mBlobs.containsKey(blobKeyName) && !Arrays.equals(mBlobs.get(blobKeyName), blob)) { - throw new AssertionFailedError("blobKeyName content is overwritten: " + blobKeyName); + protected byte[] decryptSpBlob(String protectorKeyAlias, byte[] blob, byte[] protectorSecret) { + if (mBlobs.containsKey(protectorKeyAlias) && + !Arrays.equals(mBlobs.get(protectorKeyAlias), blob)) { + throw new AssertionFailedError("Blob was overwritten; protectorKeyAlias=" + + protectorKeyAlias); } ByteBuffer buffer = ByteBuffer.allocate(blob.length); buffer.put(blob, 0, blob.length); @@ -72,7 +74,7 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager { } @Override - protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] protectorSecret, + protected byte[] createSpBlob(String protectorKeyAlias, byte[] data, byte[] protectorSecret, long sid) { ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + data.length + Integer.BYTES + protectorSecret.length + Long.BYTES); @@ -82,12 +84,12 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager { buffer.put(protectorSecret); buffer.putLong(sid); byte[] result = buffer.array(); - mBlobs.put(blobKeyName, result); + mBlobs.put(protectorKeyAlias, result); return result; } @Override - protected void destroySPBlobKey(String keyAlias) { + protected void destroyProtectorKey(String keyAlias) { } @Override