1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.locksettings.recoverablekeystore.storage; 18 19 import android.annotation.Nullable; 20 import android.os.SystemClock; 21 import android.text.format.DateUtils; 22 import android.util.Log; 23 import android.util.SparseArray; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 import com.android.security.SecureBox; 27 28 import java.security.KeyPair; 29 import java.security.NoSuchAlgorithmException; 30 import java.util.ArrayList; 31 32 /** 33 * Memory based storage for keyPair used to send encrypted credentials from a remote device. 34 * 35 * @hide 36 */ 37 public class RemoteLockscreenValidationSessionStorage { 38 39 private static final long SESSION_TIMEOUT_MILLIS = 10L * DateUtils.MINUTE_IN_MILLIS; 40 private static final String TAG = "RemoteLockscreenValidation"; 41 42 @VisibleForTesting 43 final SparseArray<LockscreenVerificationSession> mSessionsByUserId = 44 new SparseArray<>(0); 45 46 /** 47 * Returns session for given user or null. 48 * 49 * @param userId The user id 50 * @return The session info. 51 * 52 * @hide 53 */ 54 @Nullable get(int userId)55 public LockscreenVerificationSession get(int userId) { 56 synchronized (mSessionsByUserId) { 57 return mSessionsByUserId.get(userId); 58 } 59 } 60 61 /** 62 * Creates a new session to verify credentials guess. 63 * 64 * Session will be automatically removed after 10 minutes of inactivity. 65 * @param userId The user id 66 * 67 * @hide 68 */ startSession(int userId)69 public LockscreenVerificationSession startSession(int userId) { 70 synchronized (mSessionsByUserId) { 71 if (mSessionsByUserId.get(userId) != null) { 72 mSessionsByUserId.delete(userId); 73 } 74 75 KeyPair newKeyPair; 76 try { 77 newKeyPair = SecureBox.genKeyPair(); 78 } catch (NoSuchAlgorithmException e) { 79 // impossible 80 throw new RuntimeException(e); 81 } 82 LockscreenVerificationSession newSession = 83 new LockscreenVerificationSession(newKeyPair, SystemClock.elapsedRealtime()); 84 mSessionsByUserId.put(userId, newSession); 85 return newSession; 86 } 87 } 88 89 /** 90 * Deletes session for a user. 91 */ finishSession(int userId)92 public void finishSession(int userId) { 93 synchronized (mSessionsByUserId) { 94 mSessionsByUserId.delete(userId); 95 } 96 } 97 98 /** 99 * Creates a task which deletes expired sessions. 100 */ getLockscreenValidationCleanupTask()101 public Runnable getLockscreenValidationCleanupTask() { 102 return new LockscreenValidationCleanupTask(); 103 } 104 105 /** 106 * Holder for KeyPair used by remote lock screen validation. 107 * 108 * @hide 109 */ 110 public class LockscreenVerificationSession { 111 private final KeyPair mKeyPair; 112 private final long mElapsedStartTime; 113 114 /** 115 * @hide 116 */ LockscreenVerificationSession(KeyPair keyPair, long elapsedStartTime)117 LockscreenVerificationSession(KeyPair keyPair, long elapsedStartTime) { 118 mKeyPair = keyPair; 119 mElapsedStartTime = elapsedStartTime; 120 } 121 122 /** 123 * Returns SecureBox key pair. 124 */ getKeyPair()125 public KeyPair getKeyPair() { 126 return mKeyPair; 127 } 128 129 /** 130 * Time when the session started. 131 */ getElapsedStartTimeMillis()132 private long getElapsedStartTimeMillis() { 133 return mElapsedStartTime; 134 } 135 } 136 137 private class LockscreenValidationCleanupTask implements Runnable { 138 @Override run()139 public void run() { 140 try { 141 synchronized (mSessionsByUserId) { 142 ArrayList<Integer> keysToRemove = new ArrayList<>(); 143 for (int i = 0; i < mSessionsByUserId.size(); i++) { 144 long now = SystemClock.elapsedRealtime(); 145 long startTime = mSessionsByUserId.valueAt(i).getElapsedStartTimeMillis(); 146 if (now - startTime > SESSION_TIMEOUT_MILLIS) { 147 int userId = mSessionsByUserId.keyAt(i); 148 keysToRemove.add(userId); 149 } 150 } 151 for (Integer userId : keysToRemove) { 152 mSessionsByUserId.delete(userId); 153 } 154 } 155 } catch (Exception e) { 156 Log.e(TAG, "Unexpected exception thrown during LockscreenValidationCleanupTask", e); 157 } 158 } 159 160 } 161 162 } 163