blob: b726e571cf0c563985a206d0125a04a474ba7211 [file] [log] [blame]
Rubin Xu3bf722a2016-12-15 16:07:38 +00001/*
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 */
Andrew Scull507d11c2017-05-03 17:19:01 +010016
17package com.android.server.locksettings;
Rubin Xu3bf722a2016-12-15 16:07:38 +000018
Ram Periathiruvadi32d53552019-02-19 13:25:46 -080019import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
20
Rubin Xu3bf722a2016-12-15 16:07:38 +000021import android.annotation.NonNull;
22import android.annotation.Nullable;
Adrian Roos2adc2632017-09-05 17:01:42 +020023import android.content.Context;
Adrian Roos60dcbbf2017-08-08 16:19:33 +020024import android.content.pm.UserInfo;
Rubin Xu7b7424b2017-03-31 18:03:20 +010025import android.hardware.weaver.V1_0.IWeaver;
26import android.hardware.weaver.V1_0.WeaverConfig;
27import android.hardware.weaver.V1_0.WeaverReadResponse;
28import android.hardware.weaver.V1_0.WeaverReadStatus;
29import android.hardware.weaver.V1_0.WeaverStatus;
Adrian Roos7374d3a2017-03-31 14:14:53 -070030import android.os.RemoteException;
31import android.os.UserManager;
Paul Crowley0d40d6e72018-11-28 12:48:16 -080032import android.security.GateKeeper;
Bo Zhuf2c99342018-12-06 14:52:33 -080033import android.security.Scrypt;
Rubin Xu3bf722a2016-12-15 16:07:38 +000034import android.service.gatekeeper.GateKeeperResponse;
35import android.service.gatekeeper.IGateKeeperService;
36import android.util.ArrayMap;
Rubin Xu7b7424b2017-03-31 18:03:20 +010037import android.util.Slog;
Rubin Xu3bf722a2016-12-15 16:07:38 +000038
Rubin Xu7b7424b2017-03-31 18:03:20 +010039import com.android.internal.annotations.VisibleForTesting;
Rubin Xu3bf722a2016-12-15 16:07:38 +000040import com.android.internal.util.ArrayUtils;
Adrian Roos7374d3a2017-03-31 14:14:53 -070041import com.android.internal.widget.ICheckCredentialProgressCallback;
Rubin Xu3bf722a2016-12-15 16:07:38 +000042import com.android.internal.widget.LockPatternUtils;
Rubin Xubb883202019-10-09 11:22:53 +010043import com.android.internal.widget.LockscreenCredential;
Rubin Xu3bf722a2016-12-15 16:07:38 +000044import com.android.internal.widget.VerifyCredentialResponse;
Adrian Roos7374d3a2017-03-31 14:14:53 -070045import com.android.server.locksettings.LockSettingsStorage.PersistentData;
Rubin Xu3bf722a2016-12-15 16:07:38 +000046
47import libcore.util.HexEncoding;
48
49import java.nio.ByteBuffer;
Rubin Xu3bf722a2016-12-15 16:07:38 +000050import java.security.NoSuchAlgorithmException;
51import java.security.SecureRandom;
Rubin Xu7b7424b2017-03-31 18:03:20 +010052import java.util.ArrayList;
Rubin Xu3bf722a2016-12-15 16:07:38 +000053import java.util.Arrays;
54import java.util.Collections;
Rubin Xu7b7424b2017-03-31 18:03:20 +010055import java.util.HashSet;
56import java.util.List;
57import java.util.Map;
58import java.util.NoSuchElementException;
Daulet Zhanguzin8ea0a622020-01-02 17:36:31 +000059import java.util.Objects;
Rubin Xu3bf722a2016-12-15 16:07:38 +000060import java.util.Set;
61
62
63/**
64 * A class that maintains the wrapping of synthetic password by user credentials or escrow tokens.
65 * It's (mostly) a pure storage for synthetic passwords, providing APIs to creating and destroying
66 * synthetic password blobs which are wrapped by user credentials or escrow tokens.
67 *
68 * Here is the assumptions it makes:
69 * Each user has one single synthetic password at any time.
70 * The SP has an associated password handle, which binds to the SID for that user. The password
71 * handle is persisted by SyntheticPasswordManager internally.
72 * If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD
Rubin Xu7b7424b2017-03-31 18:03:20 +010073 *
74 * Information persisted on disk:
75 * for each user (stored under DEFAULT_HANDLE):
76 * SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user
77 * credential exists, cleared when user clears their credential.
Rubin Xu128180b2017-04-12 18:02:44 +010078 * SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combined with escrow
Rubin Xu7b7424b2017-03-31 18:03:20 +010079 * tokens. Destroyed when escrow support is turned off for the given user.
80 *
81 * for each SP blob under the user (stored under the corresponding handle):
82 * SP_BLOB_NAME: The encrypted synthetic password. Always exists.
83 * PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP.
Rubin Xu128180b2017-04-12 18:02:44 +010084 * SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME for the
85 * purpose of secure deletion. Exists if this is a non-weaver SP
Rubin Xu7b7424b2017-03-31 18:03:20 +010086 * (both password and token based), or it's a token-based SP under weaver.
87 * WEAVER_SLOT: Metadata about the weaver slot used. Only exists if this is a SP under weaver.
88 *
89 *
Rubin Xu3bf722a2016-12-15 16:07:38 +000090 */
91public class SyntheticPasswordManager {
92 private static final String SP_BLOB_NAME = "spblob";
93 private static final String SP_E0_NAME = "e0";
94 private static final String SP_P1_NAME = "p1";
95 private static final String SP_HANDLE_NAME = "handle";
96 private static final String SECDISCARDABLE_NAME = "secdis";
97 private static final int SECDISCARDABLE_LENGTH = 16 * 1024;
98 private static final String PASSWORD_DATA_NAME = "pwd";
Rubin Xu7b7424b2017-03-31 18:03:20 +010099 private static final String WEAVER_SLOT_NAME = "weaver";
Rubin Xu3bf722a2016-12-15 16:07:38 +0000100
Rubin Xu7b7424b2017-03-31 18:03:20 +0100101 public static final long DEFAULT_HANDLE = 0L;
Rich Canningsf64ec632019-02-21 12:40:36 -0800102 private static final byte[] DEFAULT_PASSWORD = "default-password".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000103
Rubin Xu7b7424b2017-03-31 18:03:20 +0100104 private static final byte WEAVER_VERSION = 1;
105 private static final int INVALID_WEAVER_SLOT = -1;
106
Rubin Xu8c528652017-10-31 15:40:32 +0000107 private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1;
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800108 private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2;
109 private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000110 private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
Rubin Xuf095f832017-01-31 15:23:34 +0000111 private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000112
113 // 256-bit synthetic password
114 private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
115
Rubin Xu157bddd2017-04-24 15:51:59 +0100116 private static final int PASSWORD_SCRYPT_N = 11;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000117 private static final int PASSWORD_SCRYPT_R = 3;
118 private static final int PASSWORD_SCRYPT_P = 1;
119 private static final int PASSWORD_SALT_LENGTH = 16;
120 private static final int PASSWORD_TOKEN_LENGTH = 32;
121 private static final String TAG = "SyntheticPasswordManager";
122
123 private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
124 private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes();
125 private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();
126 private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
127 private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
Andrew Sculle6527c12018-01-05 18:33:58 +0000128 private static final byte[] PERSONALIZATION_AUTHSECRET_KEY = "authsecret-hal".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000129 private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
Rubin Xuf01e9072018-03-30 20:59:28 +0100130 private static final byte[] PERSONALIZATION_PASSWORD_HASH = "pw-hash".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000131 private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100132 private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
133 private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
134 private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800135 private static final byte[] PERSONALISATION_CONTEXT =
136 "android-synthetic-password-personalization-context".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000137
138 static class AuthenticationResult {
Rubin Xud9522402019-11-06 13:54:27 +0000139 // Non-null if password/token passes verification, null otherwise
140 @Nullable public AuthenticationToken authToken;
141 // OK: password / token passes verification, user has a lockscreen
142 // null: user does not have a lockscreen (but password / token passes verification)
143 // ERROR: password / token fails verification
144 // RETRY: password / token verification is throttled at the moment.
145 @Nullable public VerifyCredentialResponse gkResponse;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000146 }
147
Rubin Xu74d155c2019-11-25 14:12:44 +0000148 /**
149 * This class represents the master cryptographic secret for a given user (a.k.a synthietic
150 * password). This secret is derived from the user's lockscreen credential or password escrow
151 * token. All other cryptograhic keys related to the user, including disk encryption key,
152 * keystore encryption key, gatekeeper auth key, vendor auth secret and others are directly
153 * derived from this token.
154 * <p>
155 * The master secret associated with an authentication token is retrievable from
156 * {@link AuthenticationToken#getSyntheticPassword()} and the authentication token can be
157 * reconsturcted from the master secret later with
158 * {@link AuthenticationToken#recreateDirectly(byte[])}. The first time an authentication token
159 * is needed, it should be created with {@link AuthenticationToken#create()} so that the
160 * necessary escrow data ({@link #mEncryptedEscrowSplit0} and {@link #mEscrowSplit1}) is
161 * properly initialized. The caller can either persist the (non-secret) esscrow data if escrow
162 * is required, or discard it to cryptograhically disable escrow. To support escrow, the caller
163 * needs to securely store the secret returned from
164 * {@link AuthenticationToken#getEscrowSecret()}, and at the time of use, load the escrow data
165 * back with {@link AuthenticationToken#setEscrowData(byte[], byte[])} and then re-create the
166 * master secret from the escrow secret via
167 * {@link AuthenticationToken#recreateFromEscrow(byte[])}.
168 */
Rubin Xu3bf722a2016-12-15 16:07:38 +0000169 static class AuthenticationToken {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800170 private final byte mVersion;
Rubin Xu74d155c2019-11-25 14:12:44 +0000171 /**
172 * Here is the relationship between these fields:
173 * Generate two random block P0 and P1. P1 is recorded in mEscrowSplit1 but P0 is not.
174 * mSyntheticPassword = hash(P0 || P1)
175 * E0 = P0 encrypted under syntheticPassword, recoreded in mEncryptedEscrowSplit0.
Rubin Xu3bf722a2016-12-15 16:07:38 +0000176 */
Rubin Xu74d155c2019-11-25 14:12:44 +0000177 private @NonNull byte[] mSyntheticPassword;
178 private @Nullable byte[] mEncryptedEscrowSplit0;
179 private @Nullable byte[] mEscrowSplit1;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000180
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800181 AuthenticationToken(byte version) {
182 mVersion = version;
183 }
184
185 private byte[] derivePassword(byte[] personalization) {
186 if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
Rubin Xu74d155c2019-11-25 14:12:44 +0000187 return (new SP800Derive(mSyntheticPassword))
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800188 .withContext(personalization, PERSONALISATION_CONTEXT);
189 } else {
190 return SyntheticPasswordCrypto.personalisedHash(personalization,
Rubin Xu74d155c2019-11-25 14:12:44 +0000191 mSyntheticPassword);
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800192 }
193 }
194
Rich Canningsf64ec632019-02-21 12:40:36 -0800195 public byte[] deriveKeyStorePassword() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800196 return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000197 }
198
199 public byte[] deriveGkPassword() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800200 return derivePassword(PERSONALIZATION_SP_GK_AUTH);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000201 }
202
203 public byte[] deriveDiskEncryptionKey() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800204 return derivePassword(PERSONALIZATION_FBE_KEY);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000205 }
206
Andrew Sculle6527c12018-01-05 18:33:58 +0000207 public byte[] deriveVendorAuthSecret() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800208 return derivePassword(PERSONALIZATION_AUTHSECRET_KEY);
Andrew Sculle6527c12018-01-05 18:33:58 +0000209 }
210
Rubin Xuf01e9072018-03-30 20:59:28 +0100211 public byte[] derivePasswordHashFactor() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800212 return derivePassword(PERSONALIZATION_PASSWORD_HASH);
Rubin Xuf01e9072018-03-30 20:59:28 +0100213 }
214
Rubin Xu74d155c2019-11-25 14:12:44 +0000215 /**
216 * Assign escrow data to this auth token. This is a prerequisite to call
217 * {@link AuthenticationToken#recreateFromEscrow}.
218 */
219 public void setEscrowData(@Nullable byte[] encryptedEscrowSplit0,
220 @Nullable byte[] escrowSplit1) {
221 mEncryptedEscrowSplit0 = encryptedEscrowSplit0;
222 mEscrowSplit1 = escrowSplit1;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000223 }
224
Rubin Xu74d155c2019-11-25 14:12:44 +0000225 /**
226 * Re-creates authentication token from escrow secret (escrowSplit0, returned from
227 * {@link AuthenticationToken#getEscrowSecret}). Escrow data needs to be loaded
228 * by {@link #setEscrowData} before calling this.
229 */
230 public void recreateFromEscrow(byte[] escrowSplit0) {
Daulet Zhanguzin8ea0a622020-01-02 17:36:31 +0000231 Objects.requireNonNull(mEscrowSplit1);
232 Objects.requireNonNull(mEncryptedEscrowSplit0);
Rubin Xu74d155c2019-11-25 14:12:44 +0000233 recreate(escrowSplit0, mEscrowSplit1);
Rubin Xuf095f832017-01-31 15:23:34 +0000234 }
235
Rubin Xu74d155c2019-11-25 14:12:44 +0000236 /**
237 * Re-creates authentication token from synthetic password directly.
238 */
239 public void recreateDirectly(byte[] syntheticPassword) {
240 this.mSyntheticPassword = Arrays.copyOf(syntheticPassword, syntheticPassword.length);
241 }
242
243 /**
244 * Generates a new random synthetic password with escrow data.
245 */
246 static AuthenticationToken create() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800247 AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3);
Rubin Xu74d155c2019-11-25 14:12:44 +0000248 byte[] escrowSplit0 = secureRandom(SYNTHETIC_PASSWORD_LENGTH);
249 byte[] escrowSplit1 = secureRandom(SYNTHETIC_PASSWORD_LENGTH);
250 result.recreate(escrowSplit0, escrowSplit1);
251 byte[] encrypteEscrowSplit0 = SyntheticPasswordCrypto.encrypt(result.mSyntheticPassword,
252 PERSONALIZATION_E0, escrowSplit0);
253 result.setEscrowData(encrypteEscrowSplit0, escrowSplit1);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000254 return result;
255 }
256
Rubin Xu74d155c2019-11-25 14:12:44 +0000257 /**
258 * Re-creates synthetic password from both escrow splits. See javadoc for
259 * AuthenticationToken.mSyntheticPassword for details on what each block means.
260 */
261 private void recreate(byte[] escrowSplit0, byte[] escrowSplit1) {
262 mSyntheticPassword = String.valueOf(HexEncoding.encode(
263 SyntheticPasswordCrypto.personalisedHash(
264 PERSONALIZATION_SP_SPLIT, escrowSplit0, escrowSplit1))).getBytes();
265 }
266
267 /**
268 * Returns the escrow secret that can be used later to reconstruct this authentication
269 * token from {@link #recreateFromEscrow(byte[])}. Only possible if escrow is not disabled
270 * (encryptedEscrowSplit0 known).
271 */
272 public byte[] getEscrowSecret() {
273 if (mEncryptedEscrowSplit0 == null) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000274 return null;
275 }
Rubin Xu74d155c2019-11-25 14:12:44 +0000276 return SyntheticPasswordCrypto.decrypt(mSyntheticPassword, PERSONALIZATION_E0,
277 mEncryptedEscrowSplit0);
278 }
279
280 /**
281 * Returns the raw synthetic password that can be used later to reconstruct this
282 * authentication token from {@link #recreateDirectly(byte[])}
283 */
284 public byte[] getSyntheticPassword() {
285 return mSyntheticPassword;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000286 }
Kenny Rootf76cfc32019-11-08 14:36:03 -0800287
288 /**
289 * Returns the version of this AuthenticationToken for use with reconstructing
290 * this with a synthetic password version.
291 */
292 public byte getVersion() {
293 return mVersion;
294 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000295 }
296
297 static class PasswordData {
298 byte scryptN;
299 byte scryptR;
300 byte scryptP;
Rubin Xu5e891bc2019-10-14 10:22:23 +0100301 public int credentialType;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000302 byte[] salt;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100303 // For GateKeeper-based credential, this is the password handle returned by GK,
304 // for weaver-based credential, this is empty.
Rubin Xu3bf722a2016-12-15 16:07:38 +0000305 public byte[] passwordHandle;
306
307 public static PasswordData create(int passwordType) {
308 PasswordData result = new PasswordData();
309 result.scryptN = PASSWORD_SCRYPT_N;
310 result.scryptR = PASSWORD_SCRYPT_R;
311 result.scryptP = PASSWORD_SCRYPT_P;
Rubin Xu5e891bc2019-10-14 10:22:23 +0100312 result.credentialType = passwordType;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000313 result.salt = secureRandom(PASSWORD_SALT_LENGTH);
314 return result;
315 }
316
317 public static PasswordData fromBytes(byte[] data) {
318 PasswordData result = new PasswordData();
319 ByteBuffer buffer = ByteBuffer.allocate(data.length);
320 buffer.put(data, 0, data.length);
321 buffer.flip();
Rubin Xu5e891bc2019-10-14 10:22:23 +0100322 result.credentialType = buffer.getInt();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000323 result.scryptN = buffer.get();
324 result.scryptR = buffer.get();
325 result.scryptP = buffer.get();
326 int saltLen = buffer.getInt();
327 result.salt = new byte[saltLen];
328 buffer.get(result.salt);
329 int handleLen = buffer.getInt();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100330 if (handleLen > 0) {
331 result.passwordHandle = new byte[handleLen];
332 buffer.get(result.passwordHandle);
333 } else {
334 result.passwordHandle = null;
335 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000336 return result;
337 }
338
339 public byte[] toBytes() {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100340
Rubin Xu3bf722a2016-12-15 16:07:38 +0000341 ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
Rubin Xu7b7424b2017-03-31 18:03:20 +0100342 + Integer.BYTES + salt.length + Integer.BYTES +
343 (passwordHandle != null ? passwordHandle.length : 0));
Rubin Xu5e891bc2019-10-14 10:22:23 +0100344 buffer.putInt(credentialType);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000345 buffer.put(scryptN);
346 buffer.put(scryptR);
347 buffer.put(scryptP);
348 buffer.putInt(salt.length);
349 buffer.put(salt);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100350 if (passwordHandle != null && passwordHandle.length > 0) {
351 buffer.putInt(passwordHandle.length);
352 buffer.put(passwordHandle);
353 } else {
354 buffer.putInt(0);
355 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000356 return buffer.array();
357 }
358 }
359
Rubin Xu7b7424b2017-03-31 18:03:20 +0100360 static class TokenData {
361 byte[] secdiscardableOnDisk;
362 byte[] weaverSecret;
363 byte[] aggregatedSecret;
Ram Periathiruvadi32d53552019-02-19 13:25:46 -0800364 EscrowTokenStateChangeCallback mCallback;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100365 }
366
Adrian Roos2adc2632017-09-05 17:01:42 +0200367 private final Context mContext;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000368 private LockSettingsStorage mStorage;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100369 private IWeaver mWeaver;
370 private WeaverConfig mWeaverConfig;
David Anderson28dea682019-02-20 13:37:51 -0800371 private PasswordSlotManager mPasswordSlotManager;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000372
Adrian Roos7374d3a2017-03-31 14:14:53 -0700373 private final UserManager mUserManager;
374
Adrian Roos2adc2632017-09-05 17:01:42 +0200375 public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
David Anderson28dea682019-02-20 13:37:51 -0800376 UserManager userManager, PasswordSlotManager passwordSlotManager) {
Adrian Roos2adc2632017-09-05 17:01:42 +0200377 mContext = context;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000378 mStorage = storage;
Adrian Roos7374d3a2017-03-31 14:14:53 -0700379 mUserManager = userManager;
David Anderson28dea682019-02-20 13:37:51 -0800380 mPasswordSlotManager = passwordSlotManager;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000381 }
382
Rubin Xu7b7424b2017-03-31 18:03:20 +0100383 @VisibleForTesting
384 protected IWeaver getWeaverService() throws RemoteException {
385 try {
386 return IWeaver.getService();
387 } catch (NoSuchElementException e) {
388 Slog.i(TAG, "Device does not support weaver");
389 return null;
390 }
391 }
392
393 public synchronized void initWeaverService() {
394 if (mWeaver != null) {
395 return;
396 }
397 try {
398 mWeaverConfig = null;
399 mWeaver = getWeaverService();
400 if (mWeaver != null) {
401 mWeaver.getConfig((int status, WeaverConfig config) -> {
402 if (status == WeaverStatus.OK && config.slots > 0) {
403 mWeaverConfig = config;
404 } else {
405 Slog.e(TAG, "Failed to get weaver config, status " + status
406 + " slots: " + config.slots);
407 mWeaver = null;
408 }
409 });
David Anderson28dea682019-02-20 13:37:51 -0800410 mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots());
Rubin Xu7b7424b2017-03-31 18:03:20 +0100411 }
412 } catch (RemoteException e) {
413 Slog.e(TAG, "Failed to get weaver service", e);
414 }
415 }
416
417 private synchronized boolean isWeaverAvailable() {
418 if (mWeaver == null) {
419 //Re-initializing weaver in case there was a transient error preventing access to it.
420 initWeaverService();
421 }
422 return mWeaver != null && mWeaverConfig.slots > 0;
423 }
424
425 /**
426 * Enroll the given key value pair into the specified weaver slot. if the given key is null,
427 * a default all-zero key is used. If the value is not specified, a fresh random secret is
428 * generated as the value.
429 *
Rubin Xuca6ece52019-07-31 15:02:13 +0100430 * @return the value stored in the weaver slot, or null if the operation fails
Rubin Xu7b7424b2017-03-31 18:03:20 +0100431 */
Rubin Xuca6ece52019-07-31 15:02:13 +0100432 private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100433 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
Rubin Xu0f9c2ff2019-08-14 16:25:57 +0100434 throw new IllegalArgumentException("Invalid slot for weaver");
Rubin Xu7b7424b2017-03-31 18:03:20 +0100435 }
436 if (key == null) {
437 key = new byte[mWeaverConfig.keySize];
438 } else if (key.length != mWeaverConfig.keySize) {
Rubin Xu0f9c2ff2019-08-14 16:25:57 +0100439 throw new IllegalArgumentException("Invalid key size for weaver");
Rubin Xu7b7424b2017-03-31 18:03:20 +0100440 }
441 if (value == null) {
442 value = secureRandom(mWeaverConfig.valueSize);
443 }
Rubin Xuca6ece52019-07-31 15:02:13 +0100444 try {
445 int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
446 if (writeStatus != WeaverStatus.OK) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100447 Slog.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
Rubin Xuca6ece52019-07-31 15:02:13 +0100448 return null;
449 }
450 } catch (RemoteException e) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100451 Slog.e(TAG, "weaver write failed", e);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100452 return null;
453 }
454 return value;
455 }
456
457 /**
458 * Verify the supplied key against a weaver slot, returning a response indicating whether
459 * the verification is successful, throttled or failed. If successful, the bound secret
460 * is also returned.
Rubin Xu7b7424b2017-03-31 18:03:20 +0100461 */
Rubin Xuca6ece52019-07-31 15:02:13 +0100462 private VerifyCredentialResponse weaverVerify(int slot, byte[] key) {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100463 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
Rubin Xu0f9c2ff2019-08-14 16:25:57 +0100464 throw new IllegalArgumentException("Invalid slot for weaver");
Rubin Xu7b7424b2017-03-31 18:03:20 +0100465 }
466 if (key == null) {
467 key = new byte[mWeaverConfig.keySize];
468 } else if (key.length != mWeaverConfig.keySize) {
Rubin Xu0f9c2ff2019-08-14 16:25:57 +0100469 throw new IllegalArgumentException("Invalid key size for weaver");
Rubin Xu7b7424b2017-03-31 18:03:20 +0100470 }
471 final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
Rubin Xuca6ece52019-07-31 15:02:13 +0100472 try {
473 mWeaver.read(slot, toByteArrayList(key),
474 (int status, WeaverReadResponse readResponse) -> {
475 switch (status) {
476 case WeaverReadStatus.OK:
477 response[0] = new VerifyCredentialResponse(
478 fromByteArrayList(readResponse.value));
479 break;
480 case WeaverReadStatus.THROTTLE:
481 response[0] = new VerifyCredentialResponse(readResponse.timeout);
Rubin Xue1beaf02019-10-22 11:36:51 +0100482 Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
Rubin Xuca6ece52019-07-31 15:02:13 +0100483 break;
484 case WeaverReadStatus.INCORRECT_KEY:
485 if (readResponse.timeout == 0) {
486 response[0] = VerifyCredentialResponse.ERROR;
Rubin Xue1beaf02019-10-22 11:36:51 +0100487 Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
Rubin Xuca6ece52019-07-31 15:02:13 +0100488 } else {
489 response[0] = new VerifyCredentialResponse(readResponse.timeout);
Rubin Xue1beaf02019-10-22 11:36:51 +0100490 Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: "
Rubin Xuca6ece52019-07-31 15:02:13 +0100491 + slot);
492 }
493 break;
494 case WeaverReadStatus.FAILED:
495 response[0] = VerifyCredentialResponse.ERROR;
Rubin Xue1beaf02019-10-22 11:36:51 +0100496 Slog.e(TAG, "weaver read failed (FAILED), slot: " + slot);
Rubin Xuca6ece52019-07-31 15:02:13 +0100497 break;
498 default:
499 response[0] = VerifyCredentialResponse.ERROR;
Rubin Xue1beaf02019-10-22 11:36:51 +0100500 Slog.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
Rubin Xuca6ece52019-07-31 15:02:13 +0100501 break;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100502 }
Rubin Xuca6ece52019-07-31 15:02:13 +0100503 });
504 } catch (RemoteException e) {
505 response[0] = VerifyCredentialResponse.ERROR;
Rubin Xue1beaf02019-10-22 11:36:51 +0100506 Slog.e(TAG, "weaver read failed, slot: " + slot, e);
Rubin Xuca6ece52019-07-31 15:02:13 +0100507 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100508 return response[0];
509 }
510
511 public void removeUser(int userId) {
Rubin Xu2f22ee42017-09-15 11:56:53 +0100512 for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
513 destroyWeaverSlot(handle, userId);
514 destroySPBlobKey(getHandleName(handle));
Rubin Xu7b7424b2017-03-31 18:03:20 +0100515 }
516 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000517
Rubin Xu5e891bc2019-10-14 10:22:23 +0100518 int getCredentialType(long handle, int userId) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000519 byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
520 if (passwordData == null) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100521 Slog.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000522 return LockPatternUtils.CREDENTIAL_TYPE_NONE;
523 }
Rubin Xu5e891bc2019-10-14 10:22:23 +0100524 return PasswordData.fromBytes(passwordData).credentialType;
525 }
526
527 static int getFrpCredentialType(byte[] payload) {
528 if (payload == null) {
529 return LockPatternUtils.CREDENTIAL_TYPE_NONE;
530 }
531 return PasswordData.fromBytes(payload).credentialType;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000532 }
533
534 /**
535 * Initializing a new Authentication token, possibly from an existing credential and hash.
536 *
537 * The authentication token would bear a randomly-generated synthetic password.
538 *
539 * This method has the side effect of rebinding the SID of the given user to the
540 * newly-generated SP.
541 *
542 * If the existing credential hash is non-null, the existing SID mill be migrated so
543 * the synthetic password in the authentication token will produce the same SID
544 * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
Rubin Xu7b7424b2017-03-31 18:03:20 +0100545 * in a per-user data storage.)
Rubin Xu3bf722a2016-12-15 16:07:38 +0000546 *
547 * If the existing credential hash is null, it means the given user should have no SID so
548 * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
549 * the supplied credential parameter is also ignored.
550 *
551 * Also saves the escrow information necessary to re-generate the synthetic password under
552 * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
553 * password escrow should be disabled completely on the given user.
554 *
555 */
556 public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
Rubin Xubb883202019-10-09 11:22:53 +0100557 byte[] hash, LockscreenCredential credential, int userId) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000558 AuthenticationToken result = AuthenticationToken.create();
559 GateKeeperResponse response;
560 if (hash != null) {
Rubin Xuca6ece52019-07-31 15:02:13 +0100561 try {
Rubin Xubb883202019-10-09 11:22:53 +0100562 response = gatekeeper.enroll(userId, hash, credential.getCredential(),
563 result.deriveGkPassword());
Rubin Xuca6ece52019-07-31 15:02:13 +0100564 } catch (RemoteException e) {
565 throw new IllegalStateException("Failed to enroll credential duing SP init", e);
566 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000567 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100568 Slog.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000569 clearSidForUser(userId);
570 } else {
571 saveSyntheticPasswordHandle(response.getPayload(), userId);
572 }
573 } else {
574 clearSidForUser(userId);
575 }
576 saveEscrowData(result, userId);
577 return result;
578 }
579
580 /**
581 * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
582 * Used when adding password to previously-unsecured devices.
583 */
584 public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
Rubin Xuca6ece52019-07-31 15:02:13 +0100585 int userId) {
586 GateKeeperResponse response;
587 try {
588 response = gatekeeper.enroll(userId, null, null, authToken.deriveGkPassword());
589 } catch (RemoteException e) {
590 throw new IllegalStateException("Failed to create new SID for user", e);
591 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000592 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100593 Slog.e(TAG, "Fail to create new SID for user " + userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000594 return;
595 }
596 saveSyntheticPasswordHandle(response.getPayload(), userId);
597 }
598
599 // Nuke the SP handle (and as a result, its SID) for the given user.
600 public void clearSidForUser(int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +0100601 destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000602 }
603
604 public boolean hasSidForUser(int userId) {
605 return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
606 }
607
608 // if null, it means there is no SID associated with the user
609 // This can happen if the user is migrated to SP but currently
610 // do not have a lockscreen password.
611 private byte[] loadSyntheticPasswordHandle(int userId) {
612 return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
613 }
614
615 private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
616 saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
617 }
618
619 private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
Rubin Xu74d155c2019-11-25 14:12:44 +0000620 byte[] e0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
621 byte[] p1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
622 authToken.setEscrowData(e0, p1);
623 return e0 != null && p1 != null;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000624 }
625
626 private void saveEscrowData(AuthenticationToken authToken, int userId) {
Rubin Xu74d155c2019-11-25 14:12:44 +0000627 saveState(SP_E0_NAME, authToken.mEncryptedEscrowSplit0, DEFAULT_HANDLE, userId);
628 saveState(SP_P1_NAME, authToken.mEscrowSplit1, DEFAULT_HANDLE, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000629 }
630
Rubin Xuf095f832017-01-31 15:23:34 +0000631 public boolean hasEscrowData(int userId) {
632 return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
633 && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
634 }
635
Rubin Xu3bf722a2016-12-15 16:07:38 +0000636 public void destroyEscrowData(int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +0100637 destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
638 destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000639 }
640
Rubin Xu7b7424b2017-03-31 18:03:20 +0100641 private int loadWeaverSlot(long handle, int userId) {
642 final int LENGTH = Byte.BYTES + Integer.BYTES;
643 byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId);
644 if (data == null || data.length != LENGTH) {
645 return INVALID_WEAVER_SLOT;
646 }
647 ByteBuffer buffer = ByteBuffer.allocate(LENGTH);
648 buffer.put(data, 0, data.length);
649 buffer.flip();
650 if (buffer.get() != WEAVER_VERSION) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100651 Slog.e(TAG, "Invalid weaver slot version of handle " + handle);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100652 return INVALID_WEAVER_SLOT;
653 }
654 return buffer.getInt();
655 }
656
657 private void saveWeaverSlot(int slot, long handle, int userId) {
658 ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
659 buffer.put(WEAVER_VERSION);
660 buffer.putInt(slot);
661 saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
662 }
663
664 private void destroyWeaverSlot(long handle, int userId) {
665 int slot = loadWeaverSlot(handle, userId);
Rubin Xua339df02018-04-10 09:44:18 +0100666 destroyState(WEAVER_SLOT_NAME, handle, userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100667 if (slot != INVALID_WEAVER_SLOT) {
Rubin Xua339df02018-04-10 09:44:18 +0100668 Set<Integer> usedSlots = getUsedWeaverSlots();
669 if (!usedSlots.contains(slot)) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100670 Slog.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
Rubin Xuca6ece52019-07-31 15:02:13 +0100671 weaverEnroll(slot, null, null);
672 mPasswordSlotManager.markSlotDeleted(slot);
Rubin Xua339df02018-04-10 09:44:18 +0100673 } else {
Rubin Xue1beaf02019-10-22 11:36:51 +0100674 Slog.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100675 }
676 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100677 }
678
Rubin Xua339df02018-04-10 09:44:18 +0100679 /**
680 * Return the set of weaver slots that are currently in use by all users on the device.
681 * <p>
682 * <em>Note:</em> Users who are in the process of being deleted are not tracked here
683 * (due to them being marked as partial in UserManager so not visible from
684 * {@link UserManager#getUsers}). As a result their weaver slots will not be considered
685 * taken and can be reused by new users. Care should be taken when cleaning up the
686 * deleted user in {@link #removeUser}, to prevent a reused slot from being erased
687 * unintentionally.
688 */
689 private Set<Integer> getUsedWeaverSlots() {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100690 Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
691 WEAVER_SLOT_NAME);
692 HashSet<Integer> slots = new HashSet<>();
693 for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) {
694 for (Long handle : entry.getValue()) {
695 int slot = loadWeaverSlot(handle, entry.getKey());
696 slots.add(slot);
697 }
698 }
Rubin Xua339df02018-04-10 09:44:18 +0100699 return slots;
700 }
701
702 private int getNextAvailableWeaverSlot() {
703 Set<Integer> usedSlots = getUsedWeaverSlots();
David Anderson28dea682019-02-20 13:37:51 -0800704 usedSlots.addAll(mPasswordSlotManager.getUsedSlots());
Rubin Xu7b7424b2017-03-31 18:03:20 +0100705 for (int i = 0; i < mWeaverConfig.slots; i++) {
Rubin Xua339df02018-04-10 09:44:18 +0100706 if (!usedSlots.contains(i)) {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100707 return i;
708 }
709 }
Rubin Xu0f9c2ff2019-08-14 16:25:57 +0100710 throw new IllegalStateException("Run out of weaver slots.");
Rubin Xu7b7424b2017-03-31 18:03:20 +0100711 }
712
Rubin Xu3bf722a2016-12-15 16:07:38 +0000713 /**
714 * Create a new password based SP blob based on the supplied authentication token, such that
715 * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
716 * in the same authentication token.
717 *
718 * This method only creates SP blob wrapping around the given synthetic password and does not
719 * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
720 * is consistent with the device state by calling other APIs in this class.
721 *
722 * @see #newSidForUser
723 * @see #clearSidForUser
Rubin Xuca6ece52019-07-31 15:02:13 +0100724 * @return a new password handle for the wrapped SP blob
725 * @throw IllegalStateException if creation fails.
Rubin Xu3bf722a2016-12-15 16:07:38 +0000726 */
727 public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
Rubin Xubb883202019-10-09 11:22:53 +0100728 LockscreenCredential credential, AuthenticationToken authToken, int userId) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000729 long handle = generateHandle();
Rubin Xubb883202019-10-09 11:22:53 +0100730 PasswordData pwd = PasswordData.create(credential.getType());
Rubin Xu3bf722a2016-12-15 16:07:38 +0000731 byte[] pwdToken = computePasswordToken(credential, pwd);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100732 final long sid;
733 final byte[] applicationId;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000734
Rubin Xu7b7424b2017-03-31 18:03:20 +0100735 if (isWeaverAvailable()) {
736 // Weaver based user password
737 int weaverSlot = getNextAvailableWeaverSlot();
Rubin Xue1beaf02019-10-22 11:36:51 +0100738 Slog.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
Rubin Xuca6ece52019-07-31 15:02:13 +0100739 byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken),
740 null);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100741 if (weaverSecret == null) {
Rubin Xuca6ece52019-07-31 15:02:13 +0100742 throw new IllegalStateException(
743 "Fail to enroll user password under weaver " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100744 }
745 saveWeaverSlot(weaverSlot, handle, userId);
David Anderson28dea682019-02-20 13:37:51 -0800746 mPasswordSlotManager.markSlotInUse(weaverSlot);
Rubin Xu5e891bc2019-10-14 10:22:23 +0100747 // No need to pass in quality since the credential type already encodes sufficient info
748 synchronizeWeaverFrpPassword(pwd, 0, userId, weaverSlot);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100749
750 pwd.passwordHandle = null;
751 sid = GateKeeper.INVALID_SECURE_USER_ID;
752 applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret);
753 } else {
Rubin Xu54c19b62017-04-26 19:40:15 +0100754 // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
755 // to prevent them from accumulating and causing problems.
Rubin Xuca6ece52019-07-31 15:02:13 +0100756 try {
757 gatekeeper.clearSecureUserId(fakeUid(userId));
758 } catch (RemoteException ignore) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100759 Slog.w(TAG, "Failed to clear SID from gatekeeper");
Rubin Xuca6ece52019-07-31 15:02:13 +0100760 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100761 // GateKeeper based user password
Rubin Xuca6ece52019-07-31 15:02:13 +0100762 GateKeeperResponse response;
763 try {
764 response = gatekeeper.enroll(fakeUid(userId), null, null,
765 passwordTokenToGkInput(pwdToken));
766 } catch (RemoteException e) {
767 throw new IllegalStateException("Failed to enroll password for new SP blob", e);
768 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100769 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
Rubin Xuca6ece52019-07-31 15:02:13 +0100770 throw new IllegalStateException(
771 "Fail to enroll user password when creating SP for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100772 }
773 pwd.passwordHandle = response.getPayload();
774 sid = sidFromPasswordHandle(pwd.passwordHandle);
775 applicationId = transformUnderSecdiscardable(pwdToken,
776 createSecdiscardable(handle, userId));
Rubin Xu5e891bc2019-10-14 10:22:23 +0100777 // No need to pass in quality since the credential type already encodes sufficient info
778 synchronizeFrpPassword(pwd, 0, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000779 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000780 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
781
Rubin Xu3bf722a2016-12-15 16:07:38 +0000782 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
783 applicationId, sid, userId);
784 return handle;
785 }
786
Adrian Roos7374d3a2017-03-31 14:14:53 -0700787 public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
Rubin Xubb883202019-10-09 11:22:53 +0100788 LockscreenCredential userCredential,
Rubin Xuca6ece52019-07-31 15:02:13 +0100789 ICheckCredentialProgressCallback progressCallback) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700790 PersistentData persistentData = mStorage.readPersistentDataBlock();
791 if (persistentData.type == PersistentData.TYPE_SP) {
792 PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
793 byte[] pwdToken = computePasswordToken(userCredential, pwd);
794
Rubin Xuca6ece52019-07-31 15:02:13 +0100795 GateKeeperResponse response;
796 try {
797 response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
798 0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
799 } catch (RemoteException e) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100800 Slog.e(TAG, "FRP verifyChallenge failed", e);
Rubin Xuca6ece52019-07-31 15:02:13 +0100801 return VerifyCredentialResponse.ERROR;
802 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700803 return VerifyCredentialResponse.fromGateKeeperResponse(response);
804 } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
Rubin Xu3f8bfc82019-11-13 11:51:58 +0000805 if (!isWeaverAvailable()) {
806 Slog.e(TAG, "No weaver service to verify SP-based FRP credential");
807 return VerifyCredentialResponse.ERROR;
808 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700809 PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
810 byte[] pwdToken = computePasswordToken(userCredential, pwd);
811 int weaverSlot = persistentData.userId;
812
813 return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
814 } else {
Rubin Xue1beaf02019-10-22 11:36:51 +0100815 Slog.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
Adrian Roos7374d3a2017-03-31 14:14:53 -0700816 + persistentData.type);
817 return VerifyCredentialResponse.ERROR;
818 }
819 }
820
821
Adrian Roos60dcbbf2017-08-08 16:19:33 +0200822 public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) {
Rubin Xu5e891bc2019-10-14 10:22:23 +0100823 if (mStorage.getPersistentDataBlockManager() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200824 && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) {
Adrian Roos60dcbbf2017-08-08 16:19:33 +0200825 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle,
826 userInfo.id));
Rubin Xu5e891bc2019-10-14 10:22:23 +0100827 if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
Adrian Roos60dcbbf2017-08-08 16:19:33 +0200828 int weaverSlot = loadWeaverSlot(handle, userInfo.id);
829 if (weaverSlot != INVALID_WEAVER_SLOT) {
830 synchronizeWeaverFrpPassword(pwd, requestedQuality, userInfo.id, weaverSlot);
831 } else {
832 synchronizeFrpPassword(pwd, requestedQuality, userInfo.id);
833 }
834 }
835 }
836 }
837
Adrian Roos7374d3a2017-03-31 14:14:53 -0700838 private void synchronizeFrpPassword(PasswordData pwd,
839 int requestedQuality, int userId) {
Rubin Xu5e891bc2019-10-14 10:22:23 +0100840 if (mStorage.getPersistentDataBlockManager() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200841 && LockPatternUtils.userOwnsFrpCredential(mContext,
842 mUserManager.getUserInfo(userId))) {
Rubin Xu5e891bc2019-10-14 10:22:23 +0100843 if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700844 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
845 pwd.toBytes());
846 } else {
847 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
848 }
849 }
850 }
851
852 private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
853 int weaverSlot) {
Rubin Xu5e891bc2019-10-14 10:22:23 +0100854 if (mStorage.getPersistentDataBlockManager() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200855 && LockPatternUtils.userOwnsFrpCredential(mContext,
856 mUserManager.getUserInfo(userId))) {
Rubin Xu5e891bc2019-10-14 10:22:23 +0100857 if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700858 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
859 requestedQuality, pwd.toBytes());
860 } else {
861 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, 0, 0, null);
862 }
863 }
864 }
865
Rubin Xu7b7424b2017-03-31 18:03:20 +0100866 private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
Rubin Xuf095f832017-01-31 15:23:34 +0000867
Ram Periathiruvadi32d53552019-02-19 13:25:46 -0800868 /**
869 * Create a token based Synthetic password for the given user.
Rubin Xufc067732019-03-18 11:01:18 +0000870 * @return the handle of the token
Ram Periathiruvadi32d53552019-02-19 13:25:46 -0800871 */
872 public long createTokenBasedSyntheticPassword(byte[] token, int userId,
873 @Nullable EscrowTokenStateChangeCallback changeCallback) {
Rubin Xuf095f832017-01-31 15:23:34 +0000874 long handle = generateHandle();
Rubin Xuf095f832017-01-31 15:23:34 +0000875 if (!tokenMap.containsKey(userId)) {
876 tokenMap.put(userId, new ArrayMap<>());
877 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100878 TokenData tokenData = new TokenData();
879 final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
880 if (isWeaverAvailable()) {
881 tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
882 tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
883 PERSONALISATION_WEAVER_TOKEN, secdiscardable);
884 } else {
885 tokenData.secdiscardableOnDisk = secdiscardable;
886 tokenData.weaverSecret = null;
887 }
888 tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
Ram Periathiruvadi32d53552019-02-19 13:25:46 -0800889 tokenData.mCallback = changeCallback;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100890
891 tokenMap.get(userId).put(handle, tokenData);
Rubin Xuf095f832017-01-31 15:23:34 +0000892 return handle;
893 }
894
895 public Set<Long> getPendingTokensForUser(int userId) {
896 if (!tokenMap.containsKey(userId)) {
897 return Collections.emptySet();
898 }
899 return tokenMap.get(userId).keySet();
900 }
901
902 public boolean removePendingToken(long handle, int userId) {
903 if (!tokenMap.containsKey(userId)) {
904 return false;
905 }
906 return tokenMap.get(userId).remove(handle) != null;
907 }
908
909 public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
910 int userId) {
911 if (!tokenMap.containsKey(userId)) {
912 return false;
913 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100914 TokenData tokenData = tokenMap.get(userId).get(handle);
915 if (tokenData == null) {
Rubin Xuf095f832017-01-31 15:23:34 +0000916 return false;
917 }
918 if (!loadEscrowData(authToken, userId)) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100919 Slog.w(TAG, "User is not escrowable");
Rubin Xuf095f832017-01-31 15:23:34 +0000920 return false;
921 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100922 if (isWeaverAvailable()) {
923 int slot = getNextAvailableWeaverSlot();
Rubin Xue1beaf02019-10-22 11:36:51 +0100924 Slog.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
Rubin Xuca6ece52019-07-31 15:02:13 +0100925 if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100926 Slog.e(TAG, "Failed to enroll weaver secret when activating token");
Rubin Xu7b7424b2017-03-31 18:03:20 +0100927 return false;
928 }
929 saveWeaverSlot(slot, handle, userId);
David Anderson28dea682019-02-20 13:37:51 -0800930 mPasswordSlotManager.markSlotInUse(slot);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100931 }
932 saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000933 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
Rubin Xu7b7424b2017-03-31 18:03:20 +0100934 tokenData.aggregatedSecret, 0L, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000935 tokenMap.get(userId).remove(handle);
Ram Periathiruvadi32d53552019-02-19 13:25:46 -0800936 if (tokenData.mCallback != null) {
937 tokenData.mCallback.onEscrowTokenActivated(handle, userId);
938 }
Rubin Xuf095f832017-01-31 15:23:34 +0000939 return true;
940 }
941
Rubin Xu3bf722a2016-12-15 16:07:38 +0000942 private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
943 byte[] applicationId, long sid, int userId) {
Rubin Xuf095f832017-01-31 15:23:34 +0000944 final byte[] secret;
945 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
Rubin Xu74d155c2019-11-25 14:12:44 +0000946 secret = authToken.getEscrowSecret();
Rubin Xuf095f832017-01-31 15:23:34 +0000947 } else {
Rubin Xu74d155c2019-11-25 14:12:44 +0000948 secret = authToken.getSyntheticPassword();
Rubin Xuf095f832017-01-31 15:23:34 +0000949 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000950 byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
951 byte[] blob = new byte[content.length + 1 + 1];
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800952 /*
953 * We can upgrade from v1 to v2 because that's just a change in the way that
954 * the SP is stored. However, we can't upgrade to v3 because that is a change
955 * in the way that passwords are derived from the SP.
956 */
957 if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
958 blob[0] = SYNTHETIC_PASSWORD_VERSION_V3;
959 } else {
960 blob[0] = SYNTHETIC_PASSWORD_VERSION_V2;
961 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000962 blob[1] = type;
963 System.arraycopy(content, 0, blob, 2, content.length);
964 saveState(SP_BLOB_NAME, blob, handle, userId);
965 }
966
967 /**
968 * Decrypt a synthetic password by supplying the user credential and corresponding password
969 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
970 * verification to referesh the SID & Auth token maintained by the system.
971 */
972 public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
Rubin Xubb883202019-10-09 11:22:53 +0100973 long handle, @NonNull LockscreenCredential credential, int userId,
Rubin Xuca6ece52019-07-31 15:02:13 +0100974 ICheckCredentialProgressCallback progressCallback) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000975 AuthenticationResult result = new AuthenticationResult();
976 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
Rubin Xu5e891bc2019-10-14 10:22:23 +0100977
978 if (!credential.checkAgainstStoredType(pwd.credentialType)) {
979 Slog.e(TAG, String.format("Credential type mismatch: expected %d actual %d",
980 pwd.credentialType, credential.getType()));
981 result.gkResponse = VerifyCredentialResponse.ERROR;
982 return result;
983 }
984
Rubin Xu3bf722a2016-12-15 16:07:38 +0000985 byte[] pwdToken = computePasswordToken(credential, pwd);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000986
Rubin Xu7b7424b2017-03-31 18:03:20 +0100987 final byte[] applicationId;
Rubin Xu8c528652017-10-31 15:40:32 +0000988 final long sid;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100989 int weaverSlot = loadWeaverSlot(handle, userId);
990 if (weaverSlot != INVALID_WEAVER_SLOT) {
991 // Weaver based user password
992 if (!isWeaverAvailable()) {
Rubin Xue1beaf02019-10-22 11:36:51 +0100993 Slog.e(TAG, "No weaver service to unwrap password based SP");
Rubin Xu7b7424b2017-03-31 18:03:20 +0100994 result.gkResponse = VerifyCredentialResponse.ERROR;
995 return result;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000996 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100997 result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
998 if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
999 return result;
1000 }
Rubin Xu8c528652017-10-31 15:40:32 +00001001 sid = GateKeeper.INVALID_SECURE_USER_ID;
Rubin Xu7b7424b2017-03-31 18:03:20 +01001002 applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
1003 } else {
1004 byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
Rubin Xuca6ece52019-07-31 15:02:13 +01001005 GateKeeperResponse response;
1006 try {
1007 response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
1008 pwd.passwordHandle, gkPwdToken);
1009 } catch (RemoteException e) {
Rubin Xue1beaf02019-10-22 11:36:51 +01001010 Slog.e(TAG, "gatekeeper verify failed", e);
Rubin Xuca6ece52019-07-31 15:02:13 +01001011 result.gkResponse = VerifyCredentialResponse.ERROR;
1012 return result;
1013 }
Rubin Xu7b7424b2017-03-31 18:03:20 +01001014 int responseCode = response.getResponseCode();
1015 if (responseCode == GateKeeperResponse.RESPONSE_OK) {
1016 result.gkResponse = VerifyCredentialResponse.OK;
1017 if (response.getShouldReEnroll()) {
Rubin Xuca6ece52019-07-31 15:02:13 +01001018 GateKeeperResponse reenrollResponse;
1019 try {
1020 reenrollResponse = gatekeeper.enroll(fakeUid(userId),
1021 pwd.passwordHandle, gkPwdToken, gkPwdToken);
1022 } catch (RemoteException e) {
Rubin Xue1beaf02019-10-22 11:36:51 +01001023 Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e);
Rubin Xuca6ece52019-07-31 15:02:13 +01001024 reenrollResponse = GateKeeperResponse.ERROR;
1025 // continue the flow anyway
1026 }
Rubin Xu7b7424b2017-03-31 18:03:20 +01001027 if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
1028 pwd.passwordHandle = reenrollResponse.getPayload();
Rubin Xu5e891bc2019-10-14 10:22:23 +01001029 // Use the reenrollment opportunity to update credential type
1030 // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN)
1031 pwd.credentialType = credential.getType();
Rubin Xu7b7424b2017-03-31 18:03:20 +01001032 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
Rubin Xu5e891bc2019-10-14 10:22:23 +01001033 synchronizeFrpPassword(pwd, 0, userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +01001034 } else {
Rubin Xue1beaf02019-10-22 11:36:51 +01001035 Slog.w(TAG, "Fail to re-enroll user password for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +01001036 // continue the flow anyway
1037 }
1038 }
1039 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
1040 result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
1041 return result;
1042 } else {
1043 result.gkResponse = VerifyCredentialResponse.ERROR;
1044 return result;
1045 }
Rubin Xu8c528652017-10-31 15:40:32 +00001046 sid = sidFromPasswordHandle(pwd.passwordHandle);
Rubin Xu7b7424b2017-03-31 18:03:20 +01001047 applicationId = transformUnderSecdiscardable(pwdToken,
1048 loadSecdiscardable(handle, userId));
Rubin Xu3bf722a2016-12-15 16:07:38 +00001049 }
Rubin Xucf326f12017-11-15 11:55:35 +00001050 // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
1051 // Notify the callback so the keyguard UI can proceed immediately.
1052 if (progressCallback != null) {
Rubin Xuca6ece52019-07-31 15:02:13 +01001053 try {
1054 progressCallback.onCredentialVerified();
1055 } catch (RemoteException e) {
Rubin Xue1beaf02019-10-22 11:36:51 +01001056 Slog.w(TAG, "progressCallback throws exception", e);
Rubin Xuca6ece52019-07-31 15:02:13 +01001057 }
Rubin Xucf326f12017-11-15 11:55:35 +00001058 }
Rubin Xu3bf722a2016-12-15 16:07:38 +00001059 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
Rubin Xu8c528652017-10-31 15:40:32 +00001060 applicationId, sid, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001061
1062 // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
1063 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
1064 return result;
1065 }
1066
Rubin Xuf095f832017-01-31 15:23:34 +00001067 /**
1068 * Decrypt a synthetic password by supplying an escrow token and corresponding token
1069 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
1070 * verification to referesh the SID & Auth token maintained by the system.
1071 */
1072 public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
Rubin Xuca6ece52019-07-31 15:02:13 +01001073 IGateKeeperService gatekeeper, long handle, byte[] token, int userId) {
Rubin Xuf095f832017-01-31 15:23:34 +00001074 AuthenticationResult result = new AuthenticationResult();
Rubin Xu7b7424b2017-03-31 18:03:20 +01001075 byte[] secdiscardable = loadSecdiscardable(handle, userId);
1076 int slotId = loadWeaverSlot(handle, userId);
1077 if (slotId != INVALID_WEAVER_SLOT) {
1078 if (!isWeaverAvailable()) {
Rubin Xue1beaf02019-10-22 11:36:51 +01001079 Slog.e(TAG, "No weaver service to unwrap token based SP");
Rubin Xu7b7424b2017-03-31 18:03:20 +01001080 result.gkResponse = VerifyCredentialResponse.ERROR;
1081 return result;
1082 }
1083 VerifyCredentialResponse response = weaverVerify(slotId, null);
1084 if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
1085 response.getPayload() == null) {
Rubin Xue1beaf02019-10-22 11:36:51 +01001086 Slog.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
Rubin Xu7b7424b2017-03-31 18:03:20 +01001087 result.gkResponse = VerifyCredentialResponse.ERROR;
1088 return result;
1089 }
1090 secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
1091 PERSONALISATION_WEAVER_TOKEN, secdiscardable);
1092 }
1093 byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
Rubin Xuf095f832017-01-31 15:23:34 +00001094 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
Rubin Xu8c528652017-10-31 15:40:32 +00001095 applicationId, 0L, userId);
Rubin Xuf095f832017-01-31 15:23:34 +00001096 if (result.authToken != null) {
1097 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
1098 if (result.gkResponse == null) {
1099 // The user currently has no password. return OK with null payload so null
1100 // is propagated to unlockUser()
1101 result.gkResponse = VerifyCredentialResponse.OK;
1102 }
1103 } else {
1104 result.gkResponse = VerifyCredentialResponse.ERROR;
1105 }
1106 return result;
1107 }
1108
Rubin Xu3bf722a2016-12-15 16:07:38 +00001109 private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
Rubin Xu8c528652017-10-31 15:40:32 +00001110 byte[] applicationId, long sid, int userId) {
Rubin Xu3bf722a2016-12-15 16:07:38 +00001111 byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
1112 if (blob == null) {
1113 return null;
1114 }
Rubin Xu8c528652017-10-31 15:40:32 +00001115 final byte version = blob[0];
Paul Crowley0d40d6e72018-11-28 12:48:16 -08001116 if (version != SYNTHETIC_PASSWORD_VERSION_V3
1117 && version != SYNTHETIC_PASSWORD_VERSION_V2
1118 && version != SYNTHETIC_PASSWORD_VERSION_V1) {
Rubin Xu0f9c2ff2019-08-14 16:25:57 +01001119 throw new IllegalArgumentException("Unknown blob version");
Rubin Xu3bf722a2016-12-15 16:07:38 +00001120 }
1121 if (blob[1] != type) {
Rubin Xu0f9c2ff2019-08-14 16:25:57 +01001122 throw new IllegalArgumentException("Invalid blob type");
Rubin Xu3bf722a2016-12-15 16:07:38 +00001123 }
Rubin Xu8c528652017-10-31 15:40:32 +00001124 final byte[] secret;
1125 if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
1126 secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
1127 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
1128 } else {
1129 secret = decryptSPBlob(getHandleName(handle),
Rubin Xu3bf722a2016-12-15 16:07:38 +00001130 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
Rubin Xu8c528652017-10-31 15:40:32 +00001131 }
Rubin Xu3bf722a2016-12-15 16:07:38 +00001132 if (secret == null) {
Rubin Xue1beaf02019-10-22 11:36:51 +01001133 Slog.e(TAG, "Fail to decrypt SP for user " + userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001134 return null;
1135 }
Paul Crowley0d40d6e72018-11-28 12:48:16 -08001136 AuthenticationToken result = new AuthenticationToken(version);
Rubin Xuf095f832017-01-31 15:23:34 +00001137 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
1138 if (!loadEscrowData(result, userId)) {
Rubin Xue1beaf02019-10-22 11:36:51 +01001139 Slog.e(TAG, "User is not escrowable: " + userId);
Rubin Xuf095f832017-01-31 15:23:34 +00001140 return null;
1141 }
Rubin Xu74d155c2019-11-25 14:12:44 +00001142 result.recreateFromEscrow(secret);
Rubin Xuf095f832017-01-31 15:23:34 +00001143 } else {
Rubin Xu74d155c2019-11-25 14:12:44 +00001144 result.recreateDirectly(secret);
Rubin Xuf095f832017-01-31 15:23:34 +00001145 }
Rubin Xu8c528652017-10-31 15:40:32 +00001146 if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
Rubin Xue1beaf02019-10-22 11:36:51 +01001147 Slog.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
Rubin Xu8c528652017-10-31 15:40:32 +00001148 createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
1149 }
Rubin Xu3bf722a2016-12-15 16:07:38 +00001150 return result;
1151 }
1152
1153 /**
1154 * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
1155 * if required.
1156 *
1157 * Normally performing verifyChallenge with an AuthenticationToken should always return
1158 * RESPONSE_OK, since user authentication failures are detected earlier when trying to
1159 * decrypt SP.
1160 */
Rubin Xu8b30ec32017-03-05 00:47:09 +00001161 public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
Rubin Xuca6ece52019-07-31 15:02:13 +01001162 @NonNull AuthenticationToken auth, long challenge, int userId) {
Rubin Xu3bf722a2016-12-15 16:07:38 +00001163 byte[] spHandle = loadSyntheticPasswordHandle(userId);
1164 if (spHandle == null) {
1165 // There is no password handle associated with the given user, i.e. the user is not
1166 // secured by lockscreen and has no SID, so just return here;
1167 return null;
1168 }
Rubin Xuca6ece52019-07-31 15:02:13 +01001169 GateKeeperResponse response;
1170 try {
1171 response = gatekeeper.verifyChallenge(userId, challenge,
1172 spHandle, auth.deriveGkPassword());
1173 } catch (RemoteException e) {
Rubin Xue1beaf02019-10-22 11:36:51 +01001174 Slog.e(TAG, "Fail to verify with gatekeeper " + userId, e);
Rubin Xuca6ece52019-07-31 15:02:13 +01001175 return VerifyCredentialResponse.ERROR;
1176 }
Rubin Xu3bf722a2016-12-15 16:07:38 +00001177 int responseCode = response.getResponseCode();
1178 if (responseCode == GateKeeperResponse.RESPONSE_OK) {
Rubin Xuca6ece52019-07-31 15:02:13 +01001179 VerifyCredentialResponse result = new VerifyCredentialResponse(response.getPayload());
Rubin Xu3bf722a2016-12-15 16:07:38 +00001180 if (response.getShouldReEnroll()) {
Rubin Xuca6ece52019-07-31 15:02:13 +01001181 try {
1182 response = gatekeeper.enroll(userId, spHandle, spHandle,
1183 auth.deriveGkPassword());
1184 } catch (RemoteException e) {
Rubin Xue1beaf02019-10-22 11:36:51 +01001185 Slog.e(TAG, "Failed to invoke gatekeeper.enroll", e);
Rubin Xuca6ece52019-07-31 15:02:13 +01001186 response = GateKeeperResponse.ERROR;
1187 }
Rubin Xu3bf722a2016-12-15 16:07:38 +00001188 if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
1189 spHandle = response.getPayload();
1190 saveSyntheticPasswordHandle(spHandle, userId);
1191 // Call self again to re-verify with updated handle
1192 return verifyChallenge(gatekeeper, auth, challenge, userId);
1193 } else {
Rubin Xuca6ece52019-07-31 15:02:13 +01001194 // Fall through, return result from the previous verification attempt.
Rubin Xue1beaf02019-10-22 11:36:51 +01001195 Slog.w(TAG, "Fail to re-enroll SP handle for user " + userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001196 }
1197 }
Rubin Xuca6ece52019-07-31 15:02:13 +01001198 return result;
Rubin Xu3bf722a2016-12-15 16:07:38 +00001199 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
Rubin Xuca6ece52019-07-31 15:02:13 +01001200 return new VerifyCredentialResponse(response.getTimeout());
Rubin Xu3bf722a2016-12-15 16:07:38 +00001201 } else {
Rubin Xuca6ece52019-07-31 15:02:13 +01001202 return VerifyCredentialResponse.ERROR;
Rubin Xu3bf722a2016-12-15 16:07:38 +00001203 }
Rubin Xu3bf722a2016-12-15 16:07:38 +00001204 }
1205
1206 public boolean existsHandle(long handle, int userId) {
1207 return hasState(SP_BLOB_NAME, handle, userId);
1208 }
1209
Rubin Xuf095f832017-01-31 15:23:34 +00001210 public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
1211 destroySyntheticPassword(handle, userId);
Rubin Xuaa32d152017-04-27 17:01:05 +01001212 destroyState(SECDISCARDABLE_NAME, handle, userId);
Rubin Xuf095f832017-01-31 15:23:34 +00001213 }
1214
Rubin Xu3bf722a2016-12-15 16:07:38 +00001215 public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
1216 destroySyntheticPassword(handle, userId);
Rubin Xuaa32d152017-04-27 17:01:05 +01001217 destroyState(SECDISCARDABLE_NAME, handle, userId);
1218 destroyState(PASSWORD_DATA_NAME, handle, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001219 }
1220
1221 private void destroySyntheticPassword(long handle, int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +01001222 destroyState(SP_BLOB_NAME, handle, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001223 destroySPBlobKey(getHandleName(handle));
Rubin Xu7b7424b2017-03-31 18:03:20 +01001224 if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
1225 destroyWeaverSlot(handle, userId);
1226 }
1227 }
1228
1229 private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
1230 byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash(
1231 PERSONALISATION_WEAVER_PASSWORD, secret);
1232 byte[] result = new byte[data.length + weaverSecret.length];
1233 System.arraycopy(data, 0, result, 0, data.length);
1234 System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length);
1235 return result;
Rubin Xu3bf722a2016-12-15 16:07:38 +00001236 }
1237
1238 private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
1239 byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
1240 PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
1241 byte[] result = new byte[data.length + secdiscardable.length];
1242 System.arraycopy(data, 0, result, 0, data.length);
1243 System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
1244 return result;
1245 }
1246
1247 private byte[] createSecdiscardable(long handle, int userId) {
1248 byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
Rubin Xu7b7424b2017-03-31 18:03:20 +01001249 saveSecdiscardable(handle, data, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001250 return data;
1251 }
1252
Rubin Xu7b7424b2017-03-31 18:03:20 +01001253 private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
1254 saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
1255 }
1256
Rubin Xu3bf722a2016-12-15 16:07:38 +00001257 private byte[] loadSecdiscardable(long handle, int userId) {
1258 return loadState(SECDISCARDABLE_NAME, handle, userId);
1259 }
1260
1261 private boolean hasState(String stateName, long handle, int userId) {
1262 return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
1263 }
1264
1265 private byte[] loadState(String stateName, long handle, int userId) {
1266 return mStorage.readSyntheticPasswordState(userId, handle, stateName);
1267 }
1268
1269 private void saveState(String stateName, byte[] data, long handle, int userId) {
1270 mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
1271 }
1272
Rubin Xuaa32d152017-04-27 17:01:05 +01001273 private void destroyState(String stateName, long handle, int userId) {
1274 mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001275 }
1276
1277 protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
1278 return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
1279 }
1280
1281 protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
1282 return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
1283 }
1284
1285 protected void destroySPBlobKey(String keyAlias) {
1286 SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
1287 }
1288
1289 public static long generateHandle() {
1290 SecureRandom rng = new SecureRandom();
1291 long result;
1292 do {
1293 result = rng.nextLong();
1294 } while (result == DEFAULT_HANDLE);
1295 return result;
1296 }
1297
1298 private int fakeUid(int uid) {
1299 return 100000 + uid;
1300 }
1301
1302 protected static byte[] secureRandom(int length) {
1303 try {
1304 return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
1305 } catch (NoSuchAlgorithmException e) {
1306 e.printStackTrace();
1307 return null;
1308 }
1309 }
1310
1311 private String getHandleName(long handle) {
1312 return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
1313 }
1314
Rubin Xubb883202019-10-09 11:22:53 +01001315 private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) {
1316 final byte[] password = credential.isNone() ? DEFAULT_PASSWORD : credential.getCredential();
Rubin Xu3bf722a2016-12-15 16:07:38 +00001317 return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
1318 PASSWORD_TOKEN_LENGTH);
1319 }
1320
1321 private byte[] passwordTokenToGkInput(byte[] token) {
1322 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
1323 }
1324
Rubin Xu7b7424b2017-03-31 18:03:20 +01001325 private byte[] passwordTokenToWeaverKey(byte[] token) {
1326 byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
1327 if (key.length < mWeaverConfig.keySize) {
Rubin Xu0f9c2ff2019-08-14 16:25:57 +01001328 throw new IllegalArgumentException("weaver key length too small");
Rubin Xu7b7424b2017-03-31 18:03:20 +01001329 }
1330 return Arrays.copyOf(key, mWeaverConfig.keySize);
1331 }
1332
Rubin Xu3bf722a2016-12-15 16:07:38 +00001333 protected long sidFromPasswordHandle(byte[] handle) {
1334 return nativeSidFromPasswordHandle(handle);
1335 }
1336
Rich Canningsf64ec632019-02-21 12:40:36 -08001337 protected byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) {
1338 return new Scrypt().scrypt(password, salt, n, r, p, outLen);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001339 }
1340
1341 native long nativeSidFromPasswordHandle(byte[] handle);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001342
Rubin Xu7b7424b2017-03-31 18:03:20 +01001343 protected static ArrayList<Byte> toByteArrayList(byte[] data) {
1344 ArrayList<Byte> result = new ArrayList<Byte>(data.length);
1345 for (int i = 0; i < data.length; i++) {
1346 result.add(data[i]);
1347 }
1348 return result;
1349 }
1350
1351 protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
1352 byte[] result = new byte[data.size()];
1353 for (int i = 0; i < data.size(); i++) {
1354 result[i] = data.get(i);
1355 }
1356 return result;
1357 }
1358
Rich Canningsf64ec632019-02-21 12:40:36 -08001359 protected static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes();
1360 private static byte[] bytesToHex(byte[] bytes) {
Rubin Xu3bf722a2016-12-15 16:07:38 +00001361 if (bytes == null) {
Rich Canningsf64ec632019-02-21 12:40:36 -08001362 return "null".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +00001363 }
Rich Canningsf64ec632019-02-21 12:40:36 -08001364 byte[] hexBytes = new byte[bytes.length * 2];
Rubin Xu3bf722a2016-12-15 16:07:38 +00001365 for ( int j = 0; j < bytes.length; j++ ) {
1366 int v = bytes[j] & 0xFF;
Rich Canningsf64ec632019-02-21 12:40:36 -08001367 hexBytes[j * 2] = HEX_ARRAY[v >>> 4];
1368 hexBytes[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
Rubin Xu3bf722a2016-12-15 16:07:38 +00001369 }
Rich Canningsf64ec632019-02-21 12:40:36 -08001370 return hexBytes;
Rubin Xu3bf722a2016-12-15 16:07:38 +00001371 }
1372}