Loading services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +1 −2 Original line number Diff line number Diff line Loading @@ -31,7 +31,6 @@ import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Context; import android.os.Binder; import android.os.Process; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; Loading Loading @@ -128,7 +127,7 @@ public class RecoverableKeyStoreManager { db, new RecoverySessionStorage(), Executors.newSingleThreadExecutor(), new RecoverySnapshotStorage(), RecoverySnapshotStorage.newInstance(), new RecoverySnapshotListenersStorage(), platformKeyManager, applicationKeyStorage); Loading services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java +8 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ import static com.android.server.locksettings.recoverablekeystore.serialization. import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS; import static com.android.server.locksettings.recoverablekeystore.serialization .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS; Loading Loading @@ -128,6 +131,11 @@ public class KeyChainSnapshotDeserializer { } break; case TAG_BACKEND_PUBLIC_KEY: builder.setTrustedHardwarePublicKey( readBlobTag(parser, TAG_BACKEND_PUBLIC_KEY)); break; case TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST: builder.setKeyChainProtectionParams(readKeyChainProtectionParamsList(parser)); break; Loading services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java +1 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ class KeyChainSnapshotSchema { static final String TAG_RECOVERY_KEY_MATERIAL = "recoveryKeyMaterial"; static final String TAG_SERVER_PARAMS = "serverParams"; static final String TAG_TRUSTED_HARDWARE_CERT_PATH = "thmCertPath"; static final String TAG_BACKEND_PUBLIC_KEY = "backendPublicKey"; static final String TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST = "keyChainProtectionParamsList"; Loading services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java +7 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ import static com.android.server.locksettings.recoverablekeystore.serialization. import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS; import static com.android.server.locksettings.recoverablekeystore.serialization .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS; Loading Loading @@ -159,6 +162,10 @@ public class KeyChainSnapshotSerializer { writePropertyTag(xmlSerializer, TAG_SERVER_PARAMS, keyChainSnapshot.getServerParams()); writePropertyTag(xmlSerializer, TAG_TRUSTED_HARDWARE_CERT_PATH, keyChainSnapshot.getTrustedHardwareCertPath()); if (keyChainSnapshot.getTrustedHardwarePublicKey() != null) { writePropertyTag(xmlSerializer, TAG_BACKEND_PUBLIC_KEY, keyChainSnapshot.getTrustedHardwarePublicKey()); } } private static void writePropertyTag( Loading services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java +120 −2 Original line number Diff line number Diff line Loading @@ -17,13 +17,28 @@ package com.android.server.locksettings.recoverablekeystore.storage; import android.annotation.Nullable; import android.os.Environment; import android.security.keystore.recovery.KeyChainSnapshot; import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.locksettings.recoverablekeystore.serialization .KeyChainSnapshotDeserializer; import com.android.server.locksettings.recoverablekeystore.serialization .KeyChainSnapshotParserException; import com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSerializer; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.cert.CertificateEncodingException; import java.util.Locale; /** * In-memory storage for recovery snapshots. * Storage for recovery snapshots. Stores snapshots in memory, backed by disk storage. * * <p>Recovery snapshots are generated after a successful screen unlock. They are only generated if * the recoverable keystore has been mutated since the previous snapshot. This class stores only the Loading @@ -33,14 +48,46 @@ import com.android.internal.annotations.GuardedBy; * {@link com.android.server.locksettings.recoverablekeystore.KeySyncTask} thread. */ public class RecoverySnapshotStorage { private static final String TAG = "RecoverySnapshotStorage"; private static final String ROOT_PATH = "system"; private static final String STORAGE_PATH = "recoverablekeystore/snapshots/"; @GuardedBy("this") private final SparseArray<KeyChainSnapshot> mSnapshotByUid = new SparseArray<>(); private final File rootDirectory; /** * A new instance, storing snapshots in /data/system/recoverablekeystore/snapshots. * * <p>NOTE: calling this multiple times DOES NOT return the same instance, so will NOT be backed * by the same in-memory store. */ public static RecoverySnapshotStorage newInstance() { return new RecoverySnapshotStorage( new File(Environment.getDataDirectory(), ROOT_PATH)); } @VisibleForTesting public RecoverySnapshotStorage(File rootDirectory) { this.rootDirectory = rootDirectory; } /** * Sets the latest {@code snapshot} for the recovery agent {@code uid}. */ public synchronized void put(int uid, KeyChainSnapshot snapshot) { mSnapshotByUid.put(uid, snapshot); try { writeToDisk(uid, snapshot); } catch (IOException | CertificateEncodingException e) { Log.e(TAG, String.format(Locale.US, "Error persisting snapshot for %d to disk", uid), e); } } /** Loading @@ -48,7 +95,17 @@ public class RecoverySnapshotStorage { */ @Nullable public synchronized KeyChainSnapshot get(int uid) { return mSnapshotByUid.get(uid); KeyChainSnapshot snapshot = mSnapshotByUid.get(uid); if (snapshot != null) { return snapshot; } try { return readFromDisk(uid); } catch (IOException | KeyChainSnapshotParserException e) { Log.e(TAG, String.format(Locale.US, "Error reading snapshot for %d from disk", uid), e); return null; } } /** Loading @@ -56,5 +113,66 @@ public class RecoverySnapshotStorage { */ public synchronized void remove(int uid) { mSnapshotByUid.remove(uid); getSnapshotFile(uid).delete(); } /** * Writes the snapshot for recovery agent {@code uid} to disk. * * @throws IOException if an IO error occurs writing to disk. */ private void writeToDisk(int uid, KeyChainSnapshot snapshot) throws IOException, CertificateEncodingException { File snapshotFile = getSnapshotFile(uid); try ( FileOutputStream fileOutputStream = new FileOutputStream(snapshotFile) ) { KeyChainSnapshotSerializer.serialize(snapshot, fileOutputStream); } catch (IOException | CertificateEncodingException e) { // If we fail to write the latest snapshot, we should delete any older snapshot that // happens to be around. Otherwise snapshot syncs might end up going 'back in time'. snapshotFile.delete(); throw e; } } /** * Reads the last snapshot for recovery agent {@code uid} from disk. * * @return The snapshot, or null if none existed. * @throws IOException if an IO error occurs reading from disk. */ @Nullable private KeyChainSnapshot readFromDisk(int uid) throws IOException, KeyChainSnapshotParserException { File snapshotFile = getSnapshotFile(uid); try ( FileInputStream fileInputStream = new FileInputStream(snapshotFile) ) { return KeyChainSnapshotDeserializer.deserialize(fileInputStream); } catch (IOException | KeyChainSnapshotParserException e) { // If we fail to read the latest snapshot, we should delete it in case it is in some way // corrupted. We can regenerate snapshots anyway. snapshotFile.delete(); throw e; } } private File getSnapshotFile(int uid) { File folder = getStorageFolder(); String fileName = getSnapshotFileName(uid); return new File(folder, fileName); } private String getSnapshotFileName(int uid) { return String.format(Locale.US, "%d.xml", uid); } private File getStorageFolder() { File folder = new File(rootDirectory, STORAGE_PATH); folder.mkdirs(); return folder; } } Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +1 −2 Original line number Diff line number Diff line Loading @@ -31,7 +31,6 @@ import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Context; import android.os.Binder; import android.os.Process; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; Loading Loading @@ -128,7 +127,7 @@ public class RecoverableKeyStoreManager { db, new RecoverySessionStorage(), Executors.newSingleThreadExecutor(), new RecoverySnapshotStorage(), RecoverySnapshotStorage.newInstance(), new RecoverySnapshotListenersStorage(), platformKeyManager, applicationKeyStorage); Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java +8 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ import static com.android.server.locksettings.recoverablekeystore.serialization. import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS; import static com.android.server.locksettings.recoverablekeystore.serialization .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS; Loading Loading @@ -128,6 +131,11 @@ public class KeyChainSnapshotDeserializer { } break; case TAG_BACKEND_PUBLIC_KEY: builder.setTrustedHardwarePublicKey( readBlobTag(parser, TAG_BACKEND_PUBLIC_KEY)); break; case TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST: builder.setKeyChainProtectionParams(readKeyChainProtectionParamsList(parser)); break; Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java +1 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ class KeyChainSnapshotSchema { static final String TAG_RECOVERY_KEY_MATERIAL = "recoveryKeyMaterial"; static final String TAG_SERVER_PARAMS = "serverParams"; static final String TAG_TRUSTED_HARDWARE_CERT_PATH = "thmCertPath"; static final String TAG_BACKEND_PUBLIC_KEY = "backendPublicKey"; static final String TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST = "keyChainProtectionParamsList"; Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java +7 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ import static com.android.server.locksettings.recoverablekeystore.serialization. import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS; import static com.android.server.locksettings.recoverablekeystore.serialization .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS; Loading Loading @@ -159,6 +162,10 @@ public class KeyChainSnapshotSerializer { writePropertyTag(xmlSerializer, TAG_SERVER_PARAMS, keyChainSnapshot.getServerParams()); writePropertyTag(xmlSerializer, TAG_TRUSTED_HARDWARE_CERT_PATH, keyChainSnapshot.getTrustedHardwareCertPath()); if (keyChainSnapshot.getTrustedHardwarePublicKey() != null) { writePropertyTag(xmlSerializer, TAG_BACKEND_PUBLIC_KEY, keyChainSnapshot.getTrustedHardwarePublicKey()); } } private static void writePropertyTag( Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java +120 −2 Original line number Diff line number Diff line Loading @@ -17,13 +17,28 @@ package com.android.server.locksettings.recoverablekeystore.storage; import android.annotation.Nullable; import android.os.Environment; import android.security.keystore.recovery.KeyChainSnapshot; import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.locksettings.recoverablekeystore.serialization .KeyChainSnapshotDeserializer; import com.android.server.locksettings.recoverablekeystore.serialization .KeyChainSnapshotParserException; import com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSerializer; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.cert.CertificateEncodingException; import java.util.Locale; /** * In-memory storage for recovery snapshots. * Storage for recovery snapshots. Stores snapshots in memory, backed by disk storage. * * <p>Recovery snapshots are generated after a successful screen unlock. They are only generated if * the recoverable keystore has been mutated since the previous snapshot. This class stores only the Loading @@ -33,14 +48,46 @@ import com.android.internal.annotations.GuardedBy; * {@link com.android.server.locksettings.recoverablekeystore.KeySyncTask} thread. */ public class RecoverySnapshotStorage { private static final String TAG = "RecoverySnapshotStorage"; private static final String ROOT_PATH = "system"; private static final String STORAGE_PATH = "recoverablekeystore/snapshots/"; @GuardedBy("this") private final SparseArray<KeyChainSnapshot> mSnapshotByUid = new SparseArray<>(); private final File rootDirectory; /** * A new instance, storing snapshots in /data/system/recoverablekeystore/snapshots. * * <p>NOTE: calling this multiple times DOES NOT return the same instance, so will NOT be backed * by the same in-memory store. */ public static RecoverySnapshotStorage newInstance() { return new RecoverySnapshotStorage( new File(Environment.getDataDirectory(), ROOT_PATH)); } @VisibleForTesting public RecoverySnapshotStorage(File rootDirectory) { this.rootDirectory = rootDirectory; } /** * Sets the latest {@code snapshot} for the recovery agent {@code uid}. */ public synchronized void put(int uid, KeyChainSnapshot snapshot) { mSnapshotByUid.put(uid, snapshot); try { writeToDisk(uid, snapshot); } catch (IOException | CertificateEncodingException e) { Log.e(TAG, String.format(Locale.US, "Error persisting snapshot for %d to disk", uid), e); } } /** Loading @@ -48,7 +95,17 @@ public class RecoverySnapshotStorage { */ @Nullable public synchronized KeyChainSnapshot get(int uid) { return mSnapshotByUid.get(uid); KeyChainSnapshot snapshot = mSnapshotByUid.get(uid); if (snapshot != null) { return snapshot; } try { return readFromDisk(uid); } catch (IOException | KeyChainSnapshotParserException e) { Log.e(TAG, String.format(Locale.US, "Error reading snapshot for %d from disk", uid), e); return null; } } /** Loading @@ -56,5 +113,66 @@ public class RecoverySnapshotStorage { */ public synchronized void remove(int uid) { mSnapshotByUid.remove(uid); getSnapshotFile(uid).delete(); } /** * Writes the snapshot for recovery agent {@code uid} to disk. * * @throws IOException if an IO error occurs writing to disk. */ private void writeToDisk(int uid, KeyChainSnapshot snapshot) throws IOException, CertificateEncodingException { File snapshotFile = getSnapshotFile(uid); try ( FileOutputStream fileOutputStream = new FileOutputStream(snapshotFile) ) { KeyChainSnapshotSerializer.serialize(snapshot, fileOutputStream); } catch (IOException | CertificateEncodingException e) { // If we fail to write the latest snapshot, we should delete any older snapshot that // happens to be around. Otherwise snapshot syncs might end up going 'back in time'. snapshotFile.delete(); throw e; } } /** * Reads the last snapshot for recovery agent {@code uid} from disk. * * @return The snapshot, or null if none existed. * @throws IOException if an IO error occurs reading from disk. */ @Nullable private KeyChainSnapshot readFromDisk(int uid) throws IOException, KeyChainSnapshotParserException { File snapshotFile = getSnapshotFile(uid); try ( FileInputStream fileInputStream = new FileInputStream(snapshotFile) ) { return KeyChainSnapshotDeserializer.deserialize(fileInputStream); } catch (IOException | KeyChainSnapshotParserException e) { // If we fail to read the latest snapshot, we should delete it in case it is in some way // corrupted. We can regenerate snapshots anyway. snapshotFile.delete(); throw e; } } private File getSnapshotFile(int uid) { File folder = getStorageFolder(); String fileName = getSnapshotFileName(uid); return new File(folder, fileName); } private String getSnapshotFileName(int uid) { return String.format(Locale.US, "%d.xml", uid); } private File getStorageFolder() { File folder = new File(rootDirectory, STORAGE_PATH); folder.mkdirs(); return folder; } }