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