1 /* 2 * Copyright (C) 2017 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.util.SparseArray; 21 22 import java.util.ArrayList; 23 import java.util.Arrays; 24 25 import javax.security.auth.Destroyable; 26 27 /** 28 * Stores pending recovery sessions in memory. We do not write these to disk, as it contains hashes 29 * of the user's lock screen. 30 * 31 * @hide 32 */ 33 public class RecoverySessionStorage implements Destroyable { 34 35 private final SparseArray<ArrayList<Entry>> mSessionsByUid = new SparseArray<>(); 36 37 /** 38 * Returns the session for the given user with the given id. 39 * 40 * @param uid The uid of the recovery agent who created the session. 41 * @param sessionId The unique identifier for the session. 42 * @return The session info. 43 * 44 * @hide 45 */ 46 @Nullable get(int uid, String sessionId)47 public Entry get(int uid, String sessionId) { 48 ArrayList<Entry> userEntries = mSessionsByUid.get(uid); 49 if (userEntries == null) { 50 return null; 51 } 52 for (Entry entry : userEntries) { 53 if (sessionId.equals(entry.mSessionId)) { 54 return entry; 55 } 56 } 57 return null; 58 } 59 60 /** 61 * Adds a pending session for the given user. 62 * 63 * @param uid The uid of the recovery agent who created the session. 64 * @param entry The session info. 65 * 66 * @hide 67 */ add(int uid, Entry entry)68 public void add(int uid, Entry entry) { 69 if (mSessionsByUid.get(uid) == null) { 70 mSessionsByUid.put(uid, new ArrayList<>()); 71 } 72 mSessionsByUid.get(uid).add(entry); 73 } 74 75 /** 76 * Deletes the session with {@code sessionId} created by app with {@code uid}. 77 */ remove(int uid, String sessionId)78 public void remove(int uid, String sessionId) { 79 if (mSessionsByUid.get(uid) == null) { 80 return; 81 } 82 mSessionsByUid.get(uid).removeIf(session -> session.mSessionId.equals(sessionId)); 83 } 84 85 /** 86 * Removes all sessions associated with the given recovery agent uid. 87 * 88 * @param uid The uid of the recovery agent whose sessions to remove. 89 * 90 * @hide 91 */ remove(int uid)92 public void remove(int uid) { 93 ArrayList<Entry> entries = mSessionsByUid.get(uid); 94 if (entries == null) { 95 return; 96 } 97 for (Entry entry : entries) { 98 entry.destroy(); 99 } 100 mSessionsByUid.remove(uid); 101 } 102 103 /** 104 * Returns the total count of pending sessions. 105 * 106 * @hide 107 */ size()108 public int size() { 109 int size = 0; 110 int numberOfUsers = mSessionsByUid.size(); 111 for (int i = 0; i < numberOfUsers; i++) { 112 ArrayList<Entry> entries = mSessionsByUid.valueAt(i); 113 size += entries.size(); 114 } 115 return size; 116 } 117 118 /** 119 * Wipes the memory of any sensitive information (i.e., lock screen hashes and key claimants). 120 * 121 * @hide 122 */ 123 @Override destroy()124 public void destroy() { 125 int numberOfUids = mSessionsByUid.size(); 126 for (int i = 0; i < numberOfUids; i++) { 127 ArrayList<Entry> entries = mSessionsByUid.valueAt(i); 128 for (Entry entry : entries) { 129 entry.destroy(); 130 } 131 } 132 } 133 134 /** 135 * Information about a recovery session. 136 * 137 * @hide 138 */ 139 public static class Entry implements Destroyable { 140 private final byte[] mLskfHash; 141 private final byte[] mKeyClaimant; 142 private final byte[] mVaultParams; 143 private final String mSessionId; 144 145 /** 146 * @hide 147 */ Entry(String sessionId, byte[] lskfHash, byte[] keyClaimant, byte[] vaultParams)148 public Entry(String sessionId, byte[] lskfHash, byte[] keyClaimant, byte[] vaultParams) { 149 mLskfHash = lskfHash; 150 mSessionId = sessionId; 151 mKeyClaimant = keyClaimant; 152 mVaultParams = vaultParams; 153 } 154 155 /** 156 * Returns the hash of the lock screen associated with the recovery attempt. 157 * 158 * @hide 159 */ getLskfHash()160 public byte[] getLskfHash() { 161 return mLskfHash; 162 } 163 164 /** 165 * Returns the key generated for this recovery attempt (used to decrypt data returned by 166 * the server). 167 * 168 * @hide 169 */ getKeyClaimant()170 public byte[] getKeyClaimant() { 171 return mKeyClaimant; 172 } 173 174 /** 175 * Returns the vault params associated with the session. 176 * 177 * @hide 178 */ getVaultParams()179 public byte[] getVaultParams() { 180 return mVaultParams; 181 } 182 183 /** 184 * Overwrites the memory for the lskf hash and key claimant. 185 * 186 * @hide 187 */ 188 @Override destroy()189 public void destroy() { 190 Arrays.fill(mLskfHash, (byte) 0); 191 Arrays.fill(mKeyClaimant, (byte) 0); 192 } 193 } 194 } 195