Loading services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java +92 −19 Original line number Diff line number Diff line Loading @@ -17,9 +17,17 @@ package com.android.server.locksettings.recoverablekeystore.storage; import android.annotation.Nullable; import android.os.SystemClock; import android.text.format.DateUtils; import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.server.locksettings.recoverablekeystore.SecureBox; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; /** * Memory based storage for keyPair used to send encrypted credentials from a remote device. Loading @@ -28,8 +36,12 @@ import java.security.KeyPair; */ public class RemoteLockscreenValidationSessionStorage { private final SparseArray<LockScreenVerificationSession> mSessionsByUserId = new SparseArray<>(); private static final long SESSION_TIMEOUT_MILLIS = 10L * DateUtils.MINUTE_IN_MILLIS; private static final String TAG = "RemoteLockscreenValidation"; @VisibleForTesting final SparseArray<LockscreenVerificationSession> mSessionsByUserId = new SparseArray<>(0); /** * Returns session for given user or null. Loading @@ -40,50 +52,111 @@ public class RemoteLockscreenValidationSessionStorage { * @hide */ @Nullable public LockScreenVerificationSession get(int userId) { public LockscreenVerificationSession get(int userId) { synchronized (mSessionsByUserId) { return mSessionsByUserId.get(userId); } } /** * Creates a new session to verify lockscreen credentials guess. * Creates a new session to verify credentials guess. * * Session will be automatically removed after 10 minutes of inactivity. * @param userId The user id * * @hide */ public LockScreenVerificationSession startSession(int userId) { public LockscreenVerificationSession startSession(int userId) { synchronized (mSessionsByUserId) { if (mSessionsByUserId.get(userId) != null) { mSessionsByUserId.remove(userId); mSessionsByUserId.delete(userId); } KeyPair newKeyPair; try { newKeyPair = SecureBox.genKeyPair(); } catch (NoSuchAlgorithmException e) { // impossible throw new RuntimeException(e); } LockScreenVerificationSession newSession = null; // TODO(b/254335492): Schedule a task to remove session. LockscreenVerificationSession newSession = new LockscreenVerificationSession(newKeyPair, SystemClock.elapsedRealtime()); mSessionsByUserId.put(userId, newSession); return newSession; } } /** * Deletes session for a user. */ public void remove(int userId) { mSessionsByUserId.remove(userId); public void finishSession(int userId) { synchronized (mSessionsByUserId) { mSessionsByUserId.delete(userId); } } /** * Holder for keypair used by remote lock screen validation. * Creates a task which deletes expired sessions. */ public Runnable getLockscreenValidationCleanupTask() { return new LockscreenValidationCleanupTask(); } /** * Holder for KeyPair used by remote lock screen validation. * * @hide */ public static class LockScreenVerificationSession { public class LockscreenVerificationSession { private final KeyPair mKeyPair; private final long mSessionStartTimeMillis; private final long mElapsedStartTime; /** * @hide */ public LockScreenVerificationSession(KeyPair keyPair, long sessionStartTimeMillis) { LockscreenVerificationSession(KeyPair keyPair, long elapsedStartTime) { mKeyPair = keyPair; mSessionStartTimeMillis = sessionStartTimeMillis; mElapsedStartTime = elapsedStartTime; } /** * Returns SecureBox key pair. */ public KeyPair getKeyPair() { return mKeyPair; } /** * Time when the session started. */ private long getElapsedStartTimeMillis() { return mElapsedStartTime; } } private class LockscreenValidationCleanupTask implements Runnable { @Override public void run() { try { synchronized (mSessionsByUserId) { ArrayList<Integer> keysToRemove = new ArrayList<>(); for (int i = 0; i < mSessionsByUserId.size(); i++) { long now = SystemClock.elapsedRealtime(); long startTime = mSessionsByUserId.valueAt(i).getElapsedStartTimeMillis(); if (now - startTime > SESSION_TIMEOUT_MILLIS) { int userId = mSessionsByUserId.keyAt(i); keysToRemove.add(userId); } } for (Integer userId : keysToRemove) { mSessionsByUserId.delete(userId); } } } catch (Exception e) { Log.e(TAG, "Unexpected exception thrown during LockscreenValidationCleanupTask", e); } } } } services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java 0 → 100644 +84 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.locksettings.recoverablekeystore.storage; import static com.google.common.truth.Truth.assertThat; import android.os.SystemClock; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.locksettings.recoverablekeystore.SecureBox; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class RemoteLockscreenValidationSessionStorageTest { private static final int USER_ID = 0; private static final int USER_ID_2 = 2; RemoteLockscreenValidationSessionStorage mStorage; @Before public void setUp() { mStorage = new RemoteLockscreenValidationSessionStorage(); } @Test public void get_noStoredSessions_returnsNull() { assertThat(mStorage.get(USER_ID)).isNull(); } @Test public void startSession() { mStorage.startSession(USER_ID); assertThat(mStorage.get(USER_ID)).isNotNull(); assertThat(mStorage.get(USER_ID_2)).isNull(); } @Test public void finishSession_removesSessionFromStorage() { mStorage.startSession(USER_ID); mStorage.finishSession(USER_ID); assertThat(mStorage.get(USER_ID)).isNull(); } @Test public void getLockscreenValidationCleanupTask() throws Exception { long time11MinutesAgo = SystemClock.elapsedRealtime() - 11 * 60 * 1000; long time2MinutesAgo = SystemClock.elapsedRealtime() - 2 * 60 * 1000; mStorage.mSessionsByUserId.put( USER_ID, mStorage.new LockscreenVerificationSession( SecureBox.genKeyPair(), time11MinutesAgo)); mStorage.mSessionsByUserId.put( USER_ID_2, mStorage.new LockscreenVerificationSession( SecureBox.genKeyPair(), time2MinutesAgo)); mStorage.getLockscreenValidationCleanupTask().run(); assertThat(mStorage.get(USER_ID)).isNull(); assertThat(mStorage.get(USER_ID_2)).isNotNull(); } } Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorage.java +92 −19 Original line number Diff line number Diff line Loading @@ -17,9 +17,17 @@ package com.android.server.locksettings.recoverablekeystore.storage; import android.annotation.Nullable; import android.os.SystemClock; import android.text.format.DateUtils; import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.server.locksettings.recoverablekeystore.SecureBox; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; /** * Memory based storage for keyPair used to send encrypted credentials from a remote device. Loading @@ -28,8 +36,12 @@ import java.security.KeyPair; */ public class RemoteLockscreenValidationSessionStorage { private final SparseArray<LockScreenVerificationSession> mSessionsByUserId = new SparseArray<>(); private static final long SESSION_TIMEOUT_MILLIS = 10L * DateUtils.MINUTE_IN_MILLIS; private static final String TAG = "RemoteLockscreenValidation"; @VisibleForTesting final SparseArray<LockscreenVerificationSession> mSessionsByUserId = new SparseArray<>(0); /** * Returns session for given user or null. Loading @@ -40,50 +52,111 @@ public class RemoteLockscreenValidationSessionStorage { * @hide */ @Nullable public LockScreenVerificationSession get(int userId) { public LockscreenVerificationSession get(int userId) { synchronized (mSessionsByUserId) { return mSessionsByUserId.get(userId); } } /** * Creates a new session to verify lockscreen credentials guess. * Creates a new session to verify credentials guess. * * Session will be automatically removed after 10 minutes of inactivity. * @param userId The user id * * @hide */ public LockScreenVerificationSession startSession(int userId) { public LockscreenVerificationSession startSession(int userId) { synchronized (mSessionsByUserId) { if (mSessionsByUserId.get(userId) != null) { mSessionsByUserId.remove(userId); mSessionsByUserId.delete(userId); } KeyPair newKeyPair; try { newKeyPair = SecureBox.genKeyPair(); } catch (NoSuchAlgorithmException e) { // impossible throw new RuntimeException(e); } LockScreenVerificationSession newSession = null; // TODO(b/254335492): Schedule a task to remove session. LockscreenVerificationSession newSession = new LockscreenVerificationSession(newKeyPair, SystemClock.elapsedRealtime()); mSessionsByUserId.put(userId, newSession); return newSession; } } /** * Deletes session for a user. */ public void remove(int userId) { mSessionsByUserId.remove(userId); public void finishSession(int userId) { synchronized (mSessionsByUserId) { mSessionsByUserId.delete(userId); } } /** * Holder for keypair used by remote lock screen validation. * Creates a task which deletes expired sessions. */ public Runnable getLockscreenValidationCleanupTask() { return new LockscreenValidationCleanupTask(); } /** * Holder for KeyPair used by remote lock screen validation. * * @hide */ public static class LockScreenVerificationSession { public class LockscreenVerificationSession { private final KeyPair mKeyPair; private final long mSessionStartTimeMillis; private final long mElapsedStartTime; /** * @hide */ public LockScreenVerificationSession(KeyPair keyPair, long sessionStartTimeMillis) { LockscreenVerificationSession(KeyPair keyPair, long elapsedStartTime) { mKeyPair = keyPair; mSessionStartTimeMillis = sessionStartTimeMillis; mElapsedStartTime = elapsedStartTime; } /** * Returns SecureBox key pair. */ public KeyPair getKeyPair() { return mKeyPair; } /** * Time when the session started. */ private long getElapsedStartTimeMillis() { return mElapsedStartTime; } } private class LockscreenValidationCleanupTask implements Runnable { @Override public void run() { try { synchronized (mSessionsByUserId) { ArrayList<Integer> keysToRemove = new ArrayList<>(); for (int i = 0; i < mSessionsByUserId.size(); i++) { long now = SystemClock.elapsedRealtime(); long startTime = mSessionsByUserId.valueAt(i).getElapsedStartTimeMillis(); if (now - startTime > SESSION_TIMEOUT_MILLIS) { int userId = mSessionsByUserId.keyAt(i); keysToRemove.add(userId); } } for (Integer userId : keysToRemove) { mSessionsByUserId.delete(userId); } } } catch (Exception e) { Log.e(TAG, "Unexpected exception thrown during LockscreenValidationCleanupTask", e); } } } }
services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RemoteLockscreenValidationSessionStorageTest.java 0 → 100644 +84 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.locksettings.recoverablekeystore.storage; import static com.google.common.truth.Truth.assertThat; import android.os.SystemClock; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.locksettings.recoverablekeystore.SecureBox; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class RemoteLockscreenValidationSessionStorageTest { private static final int USER_ID = 0; private static final int USER_ID_2 = 2; RemoteLockscreenValidationSessionStorage mStorage; @Before public void setUp() { mStorage = new RemoteLockscreenValidationSessionStorage(); } @Test public void get_noStoredSessions_returnsNull() { assertThat(mStorage.get(USER_ID)).isNull(); } @Test public void startSession() { mStorage.startSession(USER_ID); assertThat(mStorage.get(USER_ID)).isNotNull(); assertThat(mStorage.get(USER_ID_2)).isNull(); } @Test public void finishSession_removesSessionFromStorage() { mStorage.startSession(USER_ID); mStorage.finishSession(USER_ID); assertThat(mStorage.get(USER_ID)).isNull(); } @Test public void getLockscreenValidationCleanupTask() throws Exception { long time11MinutesAgo = SystemClock.elapsedRealtime() - 11 * 60 * 1000; long time2MinutesAgo = SystemClock.elapsedRealtime() - 2 * 60 * 1000; mStorage.mSessionsByUserId.put( USER_ID, mStorage.new LockscreenVerificationSession( SecureBox.genKeyPair(), time11MinutesAgo)); mStorage.mSessionsByUserId.put( USER_ID_2, mStorage.new LockscreenVerificationSession( SecureBox.genKeyPair(), time2MinutesAgo)); mStorage.getLockscreenValidationCleanupTask().run(); assertThat(mStorage.get(USER_ID)).isNull(); assertThat(mStorage.get(USER_ID_2)).isNotNull(); } }