Loading services/core/java/com/android/server/locksettings/LockSettingsStorage.java +27 −4 Original line number Original line Diff line number Diff line Loading @@ -309,6 +309,10 @@ class LockSettingsStorage { } } private void writeFile(File path, byte[] data) { private void writeFile(File path, byte[] data) { writeFile(path, data, /* syncParentDir= */ true); } private void writeFile(File path, byte[] data, boolean syncParentDir) { synchronized (mFileWriteLock) { synchronized (mFileWriteLock) { // Use AtomicFile to guarantee atomicity of the file write, including when an existing // Use AtomicFile to guarantee atomicity of the file write, including when an existing // file is replaced with a new one. This method is usually used to create new files, // file is replaced with a new one. This method is usually used to create new files, Loading @@ -326,9 +330,11 @@ class LockSettingsStorage { file.failWrite(out); file.failWrite(out); } } // For performance reasons, AtomicFile only syncs the file itself, not also the parent // For performance reasons, AtomicFile only syncs the file itself, not also the parent // directory. The latter must be done explicitly here, as some callers need a guarantee // directory. The latter must be done explicitly when requested here, as some callers // that the file really exists on-disk when this returns. // need a guarantee that the file really exists on-disk when this returns. if (syncParentDir) { fsyncDirectory(path.getParentFile()); fsyncDirectory(path.getParentFile()); } mCache.putFile(path, data); mCache.putFile(path, data); } } } } Loading Loading @@ -378,10 +384,20 @@ class LockSettingsStorage { } } } } /** * Writes the synthetic password state file for the given user ID, protector ID, and state name. * If the file already exists, then it is atomically replaced. * <p> * This doesn't sync the parent directory, and a result the new state file may be lost if the * system crashes. The caller must call {@link syncSyntheticPasswordState()} afterwards to sync * the parent directory if needed, preferably after batching up other state file creations for * the same user. We do it this way because directory syncs are expensive on some filesystems. */ public void writeSyntheticPasswordState(int userId, long protectorId, String name, public void writeSyntheticPasswordState(int userId, long protectorId, String name, byte[] data) { byte[] data) { ensureSyntheticPasswordDirectoryForUser(userId); ensureSyntheticPasswordDirectoryForUser(userId); writeFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name), data); writeFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name), data, /* syncParentDir= */ false); } } public byte[] readSyntheticPasswordState(int userId, long protectorId, String name) { public byte[] readSyntheticPasswordState(int userId, long protectorId, String name) { Loading @@ -392,6 +408,13 @@ class LockSettingsStorage { deleteFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name)); deleteFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name)); } } /** * Ensures that all synthetic password state files for the user have really been saved to disk. */ public void syncSyntheticPasswordState(int userId) { fsyncDirectory(getSyntheticPasswordDirectoryForUser(userId)); } public Map<Integer, List<Long>> listSyntheticPasswordProtectorsForAllUsers(String stateName) { public Map<Integer, List<Long>> listSyntheticPasswordProtectorsForAllUsers(String stateName) { Map<Integer, List<Long>> result = new ArrayMap<>(); Map<Integer, List<Long>> result = new ArrayMap<>(); final UserManager um = UserManager.get(mContext); final UserManager um = UserManager.get(mContext); Loading services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +51 −2 Original line number Original line Diff line number Diff line Loading @@ -619,12 +619,16 @@ public class SyntheticPasswordManager { /** /** * Creates a new synthetic password (SP) for the given user. * Creates a new synthetic password (SP) for the given user. * * <p> * Any existing SID for the user is cleared. * Any existing SID for the user is cleared. * * <p> * Also saves the escrow information necessary to re-generate the synthetic password under * Also saves the escrow information necessary to re-generate the synthetic password under * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if * password escrow should be disabled completely on the given user. * password escrow should be disabled completely on the given user. * <p> * {@link syncState()} is not called yet; the caller should create a protector afterwards, which * handles this. This makes it so that all the user's initial SP state files, including the * initial LSKF-based protector, are efficiently created with only a single {@link syncState()}. */ */ SyntheticPassword newSyntheticPassword(int userId) { SyntheticPassword newSyntheticPassword(int userId) { clearSidForUser(userId); clearSidForUser(userId); Loading Loading @@ -668,6 +672,7 @@ public class SyntheticPasswordManager { private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) { private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) { saveState(SP_HANDLE_NAME, spHandle, NULL_PROTECTOR_ID, userId); saveState(SP_HANDLE_NAME, spHandle, NULL_PROTECTOR_ID, userId); syncState(userId); } } private boolean loadEscrowData(SyntheticPassword sp, int userId) { private boolean loadEscrowData(SyntheticPassword sp, int userId) { Loading @@ -677,6 +682,11 @@ public class SyntheticPasswordManager { return e0 != null && p1 != null; return e0 != null && p1 != null; } } /** * Saves the escrow data for the synthetic password. The caller is responsible for calling * {@link syncState()} afterwards, once the user's other initial synthetic password state files * have been created. */ private void saveEscrowData(SyntheticPassword sp, int userId) { private void saveEscrowData(SyntheticPassword sp, int userId) { saveState(SP_E0_NAME, sp.mEncryptedEscrowSplit0, NULL_PROTECTOR_ID, userId); saveState(SP_E0_NAME, sp.mEncryptedEscrowSplit0, NULL_PROTECTOR_ID, userId); saveState(SP_P1_NAME, sp.mEscrowSplit1, NULL_PROTECTOR_ID, userId); saveState(SP_P1_NAME, sp.mEscrowSplit1, NULL_PROTECTOR_ID, userId); Loading Loading @@ -708,6 +718,10 @@ public class SyntheticPasswordManager { return buffer.getInt(); return buffer.getInt(); } } /** * Creates a file that stores the Weaver slot the protector is using. The caller is responsible * for calling {@link syncState()} afterwards, once all the protector's files have been created. */ private void saveWeaverSlot(int slot, long protectorId, int userId) { private void saveWeaverSlot(int slot, long protectorId, int userId) { ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES); ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES); buffer.put(WEAVER_VERSION); buffer.put(WEAVER_VERSION); Loading Loading @@ -837,6 +851,7 @@ public class SyntheticPasswordManager { } } createSyntheticPasswordBlob(protectorId, PROTECTOR_TYPE_LSKF_BASED, sp, protectorSecret, createSyntheticPasswordBlob(protectorId, PROTECTOR_TYPE_LSKF_BASED, sp, protectorSecret, sid, userId); sid, userId); syncState(userId); // ensure the new files are really saved to disk return protectorId; return protectorId; } } Loading Loading @@ -996,6 +1011,7 @@ public class SyntheticPasswordManager { saveSecdiscardable(tokenHandle, tokenData.secdiscardableOnDisk, userId); saveSecdiscardable(tokenHandle, tokenData.secdiscardableOnDisk, userId); createSyntheticPasswordBlob(tokenHandle, getTokenBasedProtectorType(tokenData.mType), sp, createSyntheticPasswordBlob(tokenHandle, getTokenBasedProtectorType(tokenData.mType), sp, tokenData.aggregatedSecret, 0L, userId); tokenData.aggregatedSecret, 0L, userId); syncState(userId); // ensure the new files are really saved to disk tokenMap.get(userId).remove(tokenHandle); tokenMap.get(userId).remove(tokenHandle); if (tokenData.mCallback != null) { if (tokenData.mCallback != null) { tokenData.mCallback.onEscrowTokenActivated(tokenHandle, userId); tokenData.mCallback.onEscrowTokenActivated(tokenHandle, userId); Loading @@ -1003,6 +1019,11 @@ public class SyntheticPasswordManager { return true; return true; } } /** * Creates a synthetic password blob, i.e. the file that stores the encrypted synthetic password * (or encrypted escrow secret) for a protector. The caller is responsible for calling * {@link syncState()} afterwards, once all the protector's files have been created. */ private void createSyntheticPasswordBlob(long protectorId, byte protectorType, private void createSyntheticPasswordBlob(long protectorId, byte protectorType, SyntheticPassword sp, byte[] protectorSecret, long sid, int userId) { SyntheticPassword sp, byte[] protectorSecret, long sid, int userId) { final byte[] spSecret; final byte[] spSecret; Loading Loading @@ -1118,6 +1139,7 @@ public class SyntheticPasswordManager { // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN) // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN) pwd.credentialType = credential.getType(); pwd.credentialType = credential.getType(); saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId); saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId); syncState(userId); synchronizeFrpPassword(pwd, 0, userId); synchronizeFrpPassword(pwd, 0, userId); } else { } else { Slog.w(TAG, "Fail to re-enroll user password for user " + userId); Slog.w(TAG, "Fail to re-enroll user password for user " + userId); Loading Loading @@ -1156,6 +1178,7 @@ public class SyntheticPasswordManager { if (result.syntheticPassword != null && !credential.isNone() && if (result.syntheticPassword != null && !credential.isNone() && !hasPasswordMetrics(protectorId, userId)) { !hasPasswordMetrics(protectorId, userId)) { savePasswordMetrics(credential, result.syntheticPassword, protectorId, userId); savePasswordMetrics(credential, result.syntheticPassword, protectorId, userId); syncState(userId); // Not strictly needed as the upgrade can be re-done, but be safe. } } return result; return result; } } Loading Loading @@ -1275,6 +1298,7 @@ public class SyntheticPasswordManager { + blob.mProtectorType); + blob.mProtectorType); createSyntheticPasswordBlob(protectorId, blob.mProtectorType, result, protectorSecret, createSyntheticPasswordBlob(protectorId, blob.mProtectorType, result, protectorSecret, sid, userId); sid, userId); syncState(userId); // Not strictly needed as the upgrade can be re-done, but be safe. } } return result; return result; } } Loading Loading @@ -1396,12 +1420,21 @@ public class SyntheticPasswordManager { return ArrayUtils.concat(data, secdiscardable); return ArrayUtils.concat(data, secdiscardable); } } /** * Generates and writes the secdiscardable file for the given protector. The caller is * responsible for calling {@link syncState()} afterwards, once all the protector's files have * been created. */ private byte[] createSecdiscardable(long protectorId, int userId) { private byte[] createSecdiscardable(long protectorId, int userId) { byte[] data = secureRandom(SECDISCARDABLE_LENGTH); byte[] data = secureRandom(SECDISCARDABLE_LENGTH); saveSecdiscardable(protectorId, data, userId); saveSecdiscardable(protectorId, data, userId); return data; return data; } } /** * Writes the secdiscardable file for the given protector. The caller is responsible for * calling {@link syncState()} afterwards, once all the protector's files have been created. */ private void saveSecdiscardable(long protectorId, byte[] secdiscardable, int userId) { private void saveSecdiscardable(long protectorId, byte[] secdiscardable, int userId) { saveState(SECDISCARDABLE_NAME, secdiscardable, protectorId, userId); saveState(SECDISCARDABLE_NAME, secdiscardable, protectorId, userId); } } Loading Loading @@ -1445,6 +1478,11 @@ public class SyntheticPasswordManager { return VersionedPasswordMetrics.deserialize(decrypted).getMetrics(); return VersionedPasswordMetrics.deserialize(decrypted).getMetrics(); } } /** * Creates the password metrics file: the file associated with the LSKF-based protector that * contains the encrypted metrics about the LSKF. The caller is responsible for calling * {@link syncState()} afterwards if needed. */ private void savePasswordMetrics(LockscreenCredential credential, SyntheticPassword sp, private void savePasswordMetrics(LockscreenCredential credential, SyntheticPassword sp, long protectorId, int userId) { long protectorId, int userId) { final byte[] encrypted = SyntheticPasswordCrypto.encrypt(sp.deriveMetricsKey(), final byte[] encrypted = SyntheticPasswordCrypto.encrypt(sp.deriveMetricsKey(), Loading @@ -1466,10 +1504,21 @@ public class SyntheticPasswordManager { return mStorage.readSyntheticPasswordState(userId, protectorId, stateName); return mStorage.readSyntheticPasswordState(userId, protectorId, stateName); } } /** * Persists the given synthetic password state for the given user ID and protector ID. * <p> * For performance reasons, this doesn't sync the user's synthetic password state directory. As * a result, it doesn't guarantee that the file will really be present after a crash. If that * is needed, call {@link syncState()} afterwards, preferably after batching up related updates. */ private void saveState(String stateName, byte[] data, long protectorId, int userId) { private void saveState(String stateName, byte[] data, long protectorId, int userId) { mStorage.writeSyntheticPasswordState(userId, protectorId, stateName, data); mStorage.writeSyntheticPasswordState(userId, protectorId, stateName, data); } } private void syncState(int userId) { mStorage.syncSyntheticPasswordState(userId); } private void destroyState(String stateName, long protectorId, int userId) { private void destroyState(String stateName, long protectorId, int userId) { mStorage.deleteSyntheticPasswordState(userId, protectorId, stateName); mStorage.deleteSyntheticPasswordState(userId, protectorId, stateName); } } Loading Loading
services/core/java/com/android/server/locksettings/LockSettingsStorage.java +27 −4 Original line number Original line Diff line number Diff line Loading @@ -309,6 +309,10 @@ class LockSettingsStorage { } } private void writeFile(File path, byte[] data) { private void writeFile(File path, byte[] data) { writeFile(path, data, /* syncParentDir= */ true); } private void writeFile(File path, byte[] data, boolean syncParentDir) { synchronized (mFileWriteLock) { synchronized (mFileWriteLock) { // Use AtomicFile to guarantee atomicity of the file write, including when an existing // Use AtomicFile to guarantee atomicity of the file write, including when an existing // file is replaced with a new one. This method is usually used to create new files, // file is replaced with a new one. This method is usually used to create new files, Loading @@ -326,9 +330,11 @@ class LockSettingsStorage { file.failWrite(out); file.failWrite(out); } } // For performance reasons, AtomicFile only syncs the file itself, not also the parent // For performance reasons, AtomicFile only syncs the file itself, not also the parent // directory. The latter must be done explicitly here, as some callers need a guarantee // directory. The latter must be done explicitly when requested here, as some callers // that the file really exists on-disk when this returns. // need a guarantee that the file really exists on-disk when this returns. if (syncParentDir) { fsyncDirectory(path.getParentFile()); fsyncDirectory(path.getParentFile()); } mCache.putFile(path, data); mCache.putFile(path, data); } } } } Loading Loading @@ -378,10 +384,20 @@ class LockSettingsStorage { } } } } /** * Writes the synthetic password state file for the given user ID, protector ID, and state name. * If the file already exists, then it is atomically replaced. * <p> * This doesn't sync the parent directory, and a result the new state file may be lost if the * system crashes. The caller must call {@link syncSyntheticPasswordState()} afterwards to sync * the parent directory if needed, preferably after batching up other state file creations for * the same user. We do it this way because directory syncs are expensive on some filesystems. */ public void writeSyntheticPasswordState(int userId, long protectorId, String name, public void writeSyntheticPasswordState(int userId, long protectorId, String name, byte[] data) { byte[] data) { ensureSyntheticPasswordDirectoryForUser(userId); ensureSyntheticPasswordDirectoryForUser(userId); writeFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name), data); writeFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name), data, /* syncParentDir= */ false); } } public byte[] readSyntheticPasswordState(int userId, long protectorId, String name) { public byte[] readSyntheticPasswordState(int userId, long protectorId, String name) { Loading @@ -392,6 +408,13 @@ class LockSettingsStorage { deleteFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name)); deleteFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name)); } } /** * Ensures that all synthetic password state files for the user have really been saved to disk. */ public void syncSyntheticPasswordState(int userId) { fsyncDirectory(getSyntheticPasswordDirectoryForUser(userId)); } public Map<Integer, List<Long>> listSyntheticPasswordProtectorsForAllUsers(String stateName) { public Map<Integer, List<Long>> listSyntheticPasswordProtectorsForAllUsers(String stateName) { Map<Integer, List<Long>> result = new ArrayMap<>(); Map<Integer, List<Long>> result = new ArrayMap<>(); final UserManager um = UserManager.get(mContext); final UserManager um = UserManager.get(mContext); Loading
services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +51 −2 Original line number Original line Diff line number Diff line Loading @@ -619,12 +619,16 @@ public class SyntheticPasswordManager { /** /** * Creates a new synthetic password (SP) for the given user. * Creates a new synthetic password (SP) for the given user. * * <p> * Any existing SID for the user is cleared. * Any existing SID for the user is cleared. * * <p> * Also saves the escrow information necessary to re-generate the synthetic password under * Also saves the escrow information necessary to re-generate the synthetic password under * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if * password escrow should be disabled completely on the given user. * password escrow should be disabled completely on the given user. * <p> * {@link syncState()} is not called yet; the caller should create a protector afterwards, which * handles this. This makes it so that all the user's initial SP state files, including the * initial LSKF-based protector, are efficiently created with only a single {@link syncState()}. */ */ SyntheticPassword newSyntheticPassword(int userId) { SyntheticPassword newSyntheticPassword(int userId) { clearSidForUser(userId); clearSidForUser(userId); Loading Loading @@ -668,6 +672,7 @@ public class SyntheticPasswordManager { private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) { private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) { saveState(SP_HANDLE_NAME, spHandle, NULL_PROTECTOR_ID, userId); saveState(SP_HANDLE_NAME, spHandle, NULL_PROTECTOR_ID, userId); syncState(userId); } } private boolean loadEscrowData(SyntheticPassword sp, int userId) { private boolean loadEscrowData(SyntheticPassword sp, int userId) { Loading @@ -677,6 +682,11 @@ public class SyntheticPasswordManager { return e0 != null && p1 != null; return e0 != null && p1 != null; } } /** * Saves the escrow data for the synthetic password. The caller is responsible for calling * {@link syncState()} afterwards, once the user's other initial synthetic password state files * have been created. */ private void saveEscrowData(SyntheticPassword sp, int userId) { private void saveEscrowData(SyntheticPassword sp, int userId) { saveState(SP_E0_NAME, sp.mEncryptedEscrowSplit0, NULL_PROTECTOR_ID, userId); saveState(SP_E0_NAME, sp.mEncryptedEscrowSplit0, NULL_PROTECTOR_ID, userId); saveState(SP_P1_NAME, sp.mEscrowSplit1, NULL_PROTECTOR_ID, userId); saveState(SP_P1_NAME, sp.mEscrowSplit1, NULL_PROTECTOR_ID, userId); Loading Loading @@ -708,6 +718,10 @@ public class SyntheticPasswordManager { return buffer.getInt(); return buffer.getInt(); } } /** * Creates a file that stores the Weaver slot the protector is using. The caller is responsible * for calling {@link syncState()} afterwards, once all the protector's files have been created. */ private void saveWeaverSlot(int slot, long protectorId, int userId) { private void saveWeaverSlot(int slot, long protectorId, int userId) { ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES); ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES); buffer.put(WEAVER_VERSION); buffer.put(WEAVER_VERSION); Loading Loading @@ -837,6 +851,7 @@ public class SyntheticPasswordManager { } } createSyntheticPasswordBlob(protectorId, PROTECTOR_TYPE_LSKF_BASED, sp, protectorSecret, createSyntheticPasswordBlob(protectorId, PROTECTOR_TYPE_LSKF_BASED, sp, protectorSecret, sid, userId); sid, userId); syncState(userId); // ensure the new files are really saved to disk return protectorId; return protectorId; } } Loading Loading @@ -996,6 +1011,7 @@ public class SyntheticPasswordManager { saveSecdiscardable(tokenHandle, tokenData.secdiscardableOnDisk, userId); saveSecdiscardable(tokenHandle, tokenData.secdiscardableOnDisk, userId); createSyntheticPasswordBlob(tokenHandle, getTokenBasedProtectorType(tokenData.mType), sp, createSyntheticPasswordBlob(tokenHandle, getTokenBasedProtectorType(tokenData.mType), sp, tokenData.aggregatedSecret, 0L, userId); tokenData.aggregatedSecret, 0L, userId); syncState(userId); // ensure the new files are really saved to disk tokenMap.get(userId).remove(tokenHandle); tokenMap.get(userId).remove(tokenHandle); if (tokenData.mCallback != null) { if (tokenData.mCallback != null) { tokenData.mCallback.onEscrowTokenActivated(tokenHandle, userId); tokenData.mCallback.onEscrowTokenActivated(tokenHandle, userId); Loading @@ -1003,6 +1019,11 @@ public class SyntheticPasswordManager { return true; return true; } } /** * Creates a synthetic password blob, i.e. the file that stores the encrypted synthetic password * (or encrypted escrow secret) for a protector. The caller is responsible for calling * {@link syncState()} afterwards, once all the protector's files have been created. */ private void createSyntheticPasswordBlob(long protectorId, byte protectorType, private void createSyntheticPasswordBlob(long protectorId, byte protectorType, SyntheticPassword sp, byte[] protectorSecret, long sid, int userId) { SyntheticPassword sp, byte[] protectorSecret, long sid, int userId) { final byte[] spSecret; final byte[] spSecret; Loading Loading @@ -1118,6 +1139,7 @@ public class SyntheticPasswordManager { // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN) // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN) pwd.credentialType = credential.getType(); pwd.credentialType = credential.getType(); saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId); saveState(PASSWORD_DATA_NAME, pwd.toBytes(), protectorId, userId); syncState(userId); synchronizeFrpPassword(pwd, 0, userId); synchronizeFrpPassword(pwd, 0, userId); } else { } else { Slog.w(TAG, "Fail to re-enroll user password for user " + userId); Slog.w(TAG, "Fail to re-enroll user password for user " + userId); Loading Loading @@ -1156,6 +1178,7 @@ public class SyntheticPasswordManager { if (result.syntheticPassword != null && !credential.isNone() && if (result.syntheticPassword != null && !credential.isNone() && !hasPasswordMetrics(protectorId, userId)) { !hasPasswordMetrics(protectorId, userId)) { savePasswordMetrics(credential, result.syntheticPassword, protectorId, userId); savePasswordMetrics(credential, result.syntheticPassword, protectorId, userId); syncState(userId); // Not strictly needed as the upgrade can be re-done, but be safe. } } return result; return result; } } Loading Loading @@ -1275,6 +1298,7 @@ public class SyntheticPasswordManager { + blob.mProtectorType); + blob.mProtectorType); createSyntheticPasswordBlob(protectorId, blob.mProtectorType, result, protectorSecret, createSyntheticPasswordBlob(protectorId, blob.mProtectorType, result, protectorSecret, sid, userId); sid, userId); syncState(userId); // Not strictly needed as the upgrade can be re-done, but be safe. } } return result; return result; } } Loading Loading @@ -1396,12 +1420,21 @@ public class SyntheticPasswordManager { return ArrayUtils.concat(data, secdiscardable); return ArrayUtils.concat(data, secdiscardable); } } /** * Generates and writes the secdiscardable file for the given protector. The caller is * responsible for calling {@link syncState()} afterwards, once all the protector's files have * been created. */ private byte[] createSecdiscardable(long protectorId, int userId) { private byte[] createSecdiscardable(long protectorId, int userId) { byte[] data = secureRandom(SECDISCARDABLE_LENGTH); byte[] data = secureRandom(SECDISCARDABLE_LENGTH); saveSecdiscardable(protectorId, data, userId); saveSecdiscardable(protectorId, data, userId); return data; return data; } } /** * Writes the secdiscardable file for the given protector. The caller is responsible for * calling {@link syncState()} afterwards, once all the protector's files have been created. */ private void saveSecdiscardable(long protectorId, byte[] secdiscardable, int userId) { private void saveSecdiscardable(long protectorId, byte[] secdiscardable, int userId) { saveState(SECDISCARDABLE_NAME, secdiscardable, protectorId, userId); saveState(SECDISCARDABLE_NAME, secdiscardable, protectorId, userId); } } Loading Loading @@ -1445,6 +1478,11 @@ public class SyntheticPasswordManager { return VersionedPasswordMetrics.deserialize(decrypted).getMetrics(); return VersionedPasswordMetrics.deserialize(decrypted).getMetrics(); } } /** * Creates the password metrics file: the file associated with the LSKF-based protector that * contains the encrypted metrics about the LSKF. The caller is responsible for calling * {@link syncState()} afterwards if needed. */ private void savePasswordMetrics(LockscreenCredential credential, SyntheticPassword sp, private void savePasswordMetrics(LockscreenCredential credential, SyntheticPassword sp, long protectorId, int userId) { long protectorId, int userId) { final byte[] encrypted = SyntheticPasswordCrypto.encrypt(sp.deriveMetricsKey(), final byte[] encrypted = SyntheticPasswordCrypto.encrypt(sp.deriveMetricsKey(), Loading @@ -1466,10 +1504,21 @@ public class SyntheticPasswordManager { return mStorage.readSyntheticPasswordState(userId, protectorId, stateName); return mStorage.readSyntheticPasswordState(userId, protectorId, stateName); } } /** * Persists the given synthetic password state for the given user ID and protector ID. * <p> * For performance reasons, this doesn't sync the user's synthetic password state directory. As * a result, it doesn't guarantee that the file will really be present after a crash. If that * is needed, call {@link syncState()} afterwards, preferably after batching up related updates. */ private void saveState(String stateName, byte[] data, long protectorId, int userId) { private void saveState(String stateName, byte[] data, long protectorId, int userId) { mStorage.writeSyntheticPasswordState(userId, protectorId, stateName, data); mStorage.writeSyntheticPasswordState(userId, protectorId, stateName, data); } } private void syncState(int userId) { mStorage.syncSyntheticPasswordState(userId); } private void destroyState(String stateName, long protectorId, int userId) { private void destroyState(String stateName, long protectorId, int userId) { mStorage.deleteSyntheticPasswordState(userId, protectorId, stateName); mStorage.deleteSyntheticPasswordState(userId, protectorId, stateName); } } Loading