Loading core/java/com/android/internal/widget/LockPatternUtils.java +1 −1 Original line number Diff line number Diff line Loading @@ -170,7 +170,7 @@ public class LockPatternUtils { 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 SYNTHETIC_PASSWORD_HANDLE_KEY = "sp-handle"; public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle"; public static final String PASSWORD_HISTORY_DELIMITER = ","; @UnsupportedAppUsage Loading core/java/com/android/internal/widget/LockscreenCredential.java +6 −6 Original line number Diff line number Diff line Loading @@ -40,8 +40,8 @@ import java.util.List; import java.util.Objects; /** * A class representing a lockscreen credential. It can be either an empty password, a pattern * or a password (or PIN). * A class representing a lockscreen credential, also called a Lock Screen Knowledge Factor (LSKF). * It can be a PIN, pattern, password, or none (a.k.a. empty). * * <p> As required by some security certification, the framework tries its best to * remove copies of the lockscreen credential bytes from memory. In this regard, this class Loading @@ -52,10 +52,10 @@ import java.util.Objects; * // Process the credential in some way * } * </pre> * With this construct, we can guarantee that there will be no copies of the password left in * memory when the credential goes out of scope. This should help mitigate certain class of * attacks where the attcker gains read-only access to full device memory (cold boot attack, * unsecured software/hardware memory dumping interfaces such as JTAG). * With this construct, we can guarantee that there will be no copies of the credential left in * memory when the object goes out of scope. This should help mitigate certain class of attacks * where the attacker gains read-only access to full device memory (cold boot attack, unsecured * software/hardware memory dumping interfaces such as JTAG). */ public class LockscreenCredential implements Parcelable, AutoCloseable { Loading services/core/java/com/android/server/locksettings/LockSettingsService.java +149 −174 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/locksettings/LockSettingsStorage.java +13 −12 Original line number Diff line number Diff line Loading @@ -380,29 +380,30 @@ class LockSettingsStorage { } } public void writeSyntheticPasswordState(int userId, long handle, String name, byte[] data) { public void writeSyntheticPasswordState(int userId, long protectorId, String name, byte[] data) { ensureSyntheticPasswordDirectoryForUser(userId); writeFile(getSyntheticPasswordStateFileForUser(userId, handle, name), data); writeFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name), data); } public byte[] readSyntheticPasswordState(int userId, long handle, String name) { return readFile(getSyntheticPasswordStateFileForUser(userId, handle, name)); public byte[] readSyntheticPasswordState(int userId, long protectorId, String name) { return readFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name)); } public void deleteSyntheticPasswordState(int userId, long handle, String name) { deleteFile(getSyntheticPasswordStateFileForUser(userId, handle, name)); public void deleteSyntheticPasswordState(int userId, long protectorId, String name) { deleteFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name)); } public Map<Integer, List<Long>> listSyntheticPasswordHandlesForAllUsers(String stateName) { public Map<Integer, List<Long>> listSyntheticPasswordProtectorsForAllUsers(String stateName) { Map<Integer, List<Long>> result = new ArrayMap<>(); final UserManager um = UserManager.get(mContext); for (UserInfo user : um.getUsers()) { result.put(user.id, listSyntheticPasswordHandlesForUser(stateName, user.id)); result.put(user.id, listSyntheticPasswordProtectorsForUser(stateName, user.id)); } return result; } public List<Long> listSyntheticPasswordHandlesForUser(String stateName, int userId) { public List<Long> listSyntheticPasswordProtectorsForUser(String stateName, int userId) { File baseDir = getSyntheticPasswordDirectoryForUser(userId); List<Long> result = new ArrayList<>(); File[] files = baseDir.listFiles(); Loading @@ -415,7 +416,7 @@ class LockSettingsStorage { try { result.add(Long.parseUnsignedLong(parts[0], 16)); } catch (NumberFormatException e) { Slog.e(TAG, "Failed to parse handle " + parts[0]); Slog.e(TAG, "Failed to parse protector ID " + parts[0]); } } } Loading @@ -435,8 +436,8 @@ class LockSettingsStorage { } } private File getSyntheticPasswordStateFileForUser(int userId, long handle, String name) { String fileName = formatSimple("%016x.%s", handle, name); private File getSyntheticPasswordStateFileForUser(int userId, long protectorId, String name) { String fileName = formatSimple("%016x.%s", protectorId, name); return new File(getSyntheticPasswordDirectoryForUser(userId), fileName); } Loading services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java +34 −16 Original line number Diff line number Diff line Loading @@ -52,7 +52,7 @@ public class SyntheticPasswordCrypto { private static final int PROFILE_KEY_IV_SIZE = 12; private static final int DEFAULT_TAG_LENGTH_BITS = 128; private static final int AES_KEY_LENGTH = 32; // 256-bit AES key private static final byte[] APPLICATION_ID_PERSONALIZATION = "application-id".getBytes(); private static final byte[] PROTECTOR_SECRET_PERSONALIZATION = "application-id".getBytes(); // Time between the user credential is verified with GK and the decryption of synthetic password // under the auth-bound key. This should always happen one after the other, but give it 15 // seconds just to be sure. Loading Loading @@ -127,15 +127,19 @@ public class SyntheticPasswordCrypto { } } public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] applicationId) { /** * Decrypt 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) { try { KeyStore keyStore = getKeyStore(); SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null); if (decryptionKey == null) { SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null); if (keyStoreKey == null) { throw new IllegalStateException("SP key is missing: " + keyAlias); } byte[] intermediate = decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, blob); return decrypt(decryptionKey, intermediate); byte[] intermediate = decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, blob); return decrypt(keyStoreKey, intermediate); } catch (Exception e) { Slog.e(TAG, "Failed to decrypt V1 blob", e); throw new IllegalStateException("Failed to decrypt blob", e); Loading @@ -157,16 +161,19 @@ public class SyntheticPasswordCrypto { return keyStore; } public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) { /** * Decrypts an SP blob that was created by {@link #createBlob}. */ public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] protectorSecret) { try { final KeyStore keyStore = getKeyStore(); SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null); if (decryptionKey == null) { SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null); if (keyStoreKey == null) { throw new IllegalStateException("SP key is missing: " + keyAlias); } byte[] intermediate = decrypt(decryptionKey, blob); return decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, intermediate); byte[] intermediate = decrypt(keyStoreKey, blob); return decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, intermediate); } catch (CertificateException | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException Loading @@ -177,11 +184,22 @@ public class SyntheticPasswordCrypto { } } public static byte[] createBlob(String keyAlias, byte[] data, byte[] applicationId, long sid) { /** * 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. * * 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, long sid) { try { KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES); keyGenerator.init(AES_KEY_LENGTH * 8, new SecureRandom()); SecretKey secretKey = keyGenerator.generateKey(); SecretKey keyStoreKey = keyGenerator.generateKey(); final KeyStore keyStore = getKeyStore(); KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) Loading @@ -194,10 +212,10 @@ public class SyntheticPasswordCrypto { } keyStore.setEntry(keyAlias, new KeyStore.SecretKeyEntry(secretKey), new KeyStore.SecretKeyEntry(keyStoreKey), builder.build()); byte[] intermediate = encrypt(applicationId, APPLICATION_ID_PERSONALIZATION, data); return encrypt(secretKey, intermediate); byte[] intermediate = encrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, data); return encrypt(keyStoreKey, intermediate); } catch (CertificateException | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException Loading Loading
core/java/com/android/internal/widget/LockPatternUtils.java +1 −1 Original line number Diff line number Diff line Loading @@ -170,7 +170,7 @@ public class LockPatternUtils { 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 SYNTHETIC_PASSWORD_HANDLE_KEY = "sp-handle"; public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle"; public static final String PASSWORD_HISTORY_DELIMITER = ","; @UnsupportedAppUsage Loading
core/java/com/android/internal/widget/LockscreenCredential.java +6 −6 Original line number Diff line number Diff line Loading @@ -40,8 +40,8 @@ import java.util.List; import java.util.Objects; /** * A class representing a lockscreen credential. It can be either an empty password, a pattern * or a password (or PIN). * A class representing a lockscreen credential, also called a Lock Screen Knowledge Factor (LSKF). * It can be a PIN, pattern, password, or none (a.k.a. empty). * * <p> As required by some security certification, the framework tries its best to * remove copies of the lockscreen credential bytes from memory. In this regard, this class Loading @@ -52,10 +52,10 @@ import java.util.Objects; * // Process the credential in some way * } * </pre> * With this construct, we can guarantee that there will be no copies of the password left in * memory when the credential goes out of scope. This should help mitigate certain class of * attacks where the attcker gains read-only access to full device memory (cold boot attack, * unsecured software/hardware memory dumping interfaces such as JTAG). * With this construct, we can guarantee that there will be no copies of the credential left in * memory when the object goes out of scope. This should help mitigate certain class of attacks * where the attacker gains read-only access to full device memory (cold boot attack, unsecured * software/hardware memory dumping interfaces such as JTAG). */ public class LockscreenCredential implements Parcelable, AutoCloseable { Loading
services/core/java/com/android/server/locksettings/LockSettingsService.java +149 −174 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/locksettings/LockSettingsStorage.java +13 −12 Original line number Diff line number Diff line Loading @@ -380,29 +380,30 @@ class LockSettingsStorage { } } public void writeSyntheticPasswordState(int userId, long handle, String name, byte[] data) { public void writeSyntheticPasswordState(int userId, long protectorId, String name, byte[] data) { ensureSyntheticPasswordDirectoryForUser(userId); writeFile(getSyntheticPasswordStateFileForUser(userId, handle, name), data); writeFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name), data); } public byte[] readSyntheticPasswordState(int userId, long handle, String name) { return readFile(getSyntheticPasswordStateFileForUser(userId, handle, name)); public byte[] readSyntheticPasswordState(int userId, long protectorId, String name) { return readFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name)); } public void deleteSyntheticPasswordState(int userId, long handle, String name) { deleteFile(getSyntheticPasswordStateFileForUser(userId, handle, name)); public void deleteSyntheticPasswordState(int userId, long protectorId, String name) { deleteFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name)); } public Map<Integer, List<Long>> listSyntheticPasswordHandlesForAllUsers(String stateName) { public Map<Integer, List<Long>> listSyntheticPasswordProtectorsForAllUsers(String stateName) { Map<Integer, List<Long>> result = new ArrayMap<>(); final UserManager um = UserManager.get(mContext); for (UserInfo user : um.getUsers()) { result.put(user.id, listSyntheticPasswordHandlesForUser(stateName, user.id)); result.put(user.id, listSyntheticPasswordProtectorsForUser(stateName, user.id)); } return result; } public List<Long> listSyntheticPasswordHandlesForUser(String stateName, int userId) { public List<Long> listSyntheticPasswordProtectorsForUser(String stateName, int userId) { File baseDir = getSyntheticPasswordDirectoryForUser(userId); List<Long> result = new ArrayList<>(); File[] files = baseDir.listFiles(); Loading @@ -415,7 +416,7 @@ class LockSettingsStorage { try { result.add(Long.parseUnsignedLong(parts[0], 16)); } catch (NumberFormatException e) { Slog.e(TAG, "Failed to parse handle " + parts[0]); Slog.e(TAG, "Failed to parse protector ID " + parts[0]); } } } Loading @@ -435,8 +436,8 @@ class LockSettingsStorage { } } private File getSyntheticPasswordStateFileForUser(int userId, long handle, String name) { String fileName = formatSimple("%016x.%s", handle, name); private File getSyntheticPasswordStateFileForUser(int userId, long protectorId, String name) { String fileName = formatSimple("%016x.%s", protectorId, name); return new File(getSyntheticPasswordDirectoryForUser(userId), fileName); } Loading
services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java +34 −16 Original line number Diff line number Diff line Loading @@ -52,7 +52,7 @@ public class SyntheticPasswordCrypto { private static final int PROFILE_KEY_IV_SIZE = 12; private static final int DEFAULT_TAG_LENGTH_BITS = 128; private static final int AES_KEY_LENGTH = 32; // 256-bit AES key private static final byte[] APPLICATION_ID_PERSONALIZATION = "application-id".getBytes(); private static final byte[] PROTECTOR_SECRET_PERSONALIZATION = "application-id".getBytes(); // Time between the user credential is verified with GK and the decryption of synthetic password // under the auth-bound key. This should always happen one after the other, but give it 15 // seconds just to be sure. Loading Loading @@ -127,15 +127,19 @@ public class SyntheticPasswordCrypto { } } public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] applicationId) { /** * Decrypt 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) { try { KeyStore keyStore = getKeyStore(); SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null); if (decryptionKey == null) { SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null); if (keyStoreKey == null) { throw new IllegalStateException("SP key is missing: " + keyAlias); } byte[] intermediate = decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, blob); return decrypt(decryptionKey, intermediate); byte[] intermediate = decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, blob); return decrypt(keyStoreKey, intermediate); } catch (Exception e) { Slog.e(TAG, "Failed to decrypt V1 blob", e); throw new IllegalStateException("Failed to decrypt blob", e); Loading @@ -157,16 +161,19 @@ public class SyntheticPasswordCrypto { return keyStore; } public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) { /** * Decrypts an SP blob that was created by {@link #createBlob}. */ public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] protectorSecret) { try { final KeyStore keyStore = getKeyStore(); SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null); if (decryptionKey == null) { SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null); if (keyStoreKey == null) { throw new IllegalStateException("SP key is missing: " + keyAlias); } byte[] intermediate = decrypt(decryptionKey, blob); return decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, intermediate); byte[] intermediate = decrypt(keyStoreKey, blob); return decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, intermediate); } catch (CertificateException | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException Loading @@ -177,11 +184,22 @@ public class SyntheticPasswordCrypto { } } public static byte[] createBlob(String keyAlias, byte[] data, byte[] applicationId, long sid) { /** * 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. * * 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, long sid) { try { KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES); keyGenerator.init(AES_KEY_LENGTH * 8, new SecureRandom()); SecretKey secretKey = keyGenerator.generateKey(); SecretKey keyStoreKey = keyGenerator.generateKey(); final KeyStore keyStore = getKeyStore(); KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) Loading @@ -194,10 +212,10 @@ public class SyntheticPasswordCrypto { } keyStore.setEntry(keyAlias, new KeyStore.SecretKeyEntry(secretKey), new KeyStore.SecretKeyEntry(keyStoreKey), builder.build()); byte[] intermediate = encrypt(applicationId, APPLICATION_ID_PERSONALIZATION, data); return encrypt(secretKey, intermediate); byte[] intermediate = encrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, data); return encrypt(keyStoreKey, intermediate); } catch (CertificateException | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException Loading