blob: 142ad53136fce76a01a03ede05e207f8af67e98c [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 Roos7374d3a2017-03-31 14:14:53 -070023import android.app.admin.DevicePolicyManager;
Adrian Roos2adc2632017-09-05 17:01:42 +020024import android.content.Context;
Adrian Roos60dcbbf2017-08-08 16:19:33 +020025import android.content.pm.UserInfo;
Rubin Xu7b7424b2017-03-31 18:03:20 +010026import android.hardware.weaver.V1_0.IWeaver;
27import android.hardware.weaver.V1_0.WeaverConfig;
28import android.hardware.weaver.V1_0.WeaverReadResponse;
29import android.hardware.weaver.V1_0.WeaverReadStatus;
30import android.hardware.weaver.V1_0.WeaverStatus;
Adrian Roos7374d3a2017-03-31 14:14:53 -070031import android.os.RemoteException;
32import android.os.UserManager;
Paul Crowley0d40d6e72018-11-28 12:48:16 -080033import android.security.GateKeeper;
Bo Zhuf2c99342018-12-06 14:52:33 -080034import android.security.Scrypt;
Rubin Xu3bf722a2016-12-15 16:07:38 +000035import android.service.gatekeeper.GateKeeperResponse;
36import android.service.gatekeeper.IGateKeeperService;
37import android.util.ArrayMap;
38import android.util.Log;
Rubin Xu7b7424b2017-03-31 18:03:20 +010039import android.util.Slog;
Rubin Xu3bf722a2016-12-15 16:07:38 +000040
Rubin Xu7b7424b2017-03-31 18:03:20 +010041import com.android.internal.annotations.VisibleForTesting;
Rubin Xu3bf722a2016-12-15 16:07:38 +000042import com.android.internal.util.ArrayUtils;
Adrian Roos7374d3a2017-03-31 14:14:53 -070043import com.android.internal.widget.ICheckCredentialProgressCallback;
Rubin Xu3bf722a2016-12-15 16:07:38 +000044import com.android.internal.widget.LockPatternUtils;
45import com.android.internal.widget.VerifyCredentialResponse;
Adrian Roos7374d3a2017-03-31 14:14:53 -070046import com.android.server.locksettings.LockSettingsStorage.PersistentData;
Rubin Xu3bf722a2016-12-15 16:07:38 +000047
48import libcore.util.HexEncoding;
49
50import java.nio.ByteBuffer;
Rubin Xu3bf722a2016-12-15 16:07:38 +000051import java.security.NoSuchAlgorithmException;
52import java.security.SecureRandom;
Rubin Xu7b7424b2017-03-31 18:03:20 +010053import java.util.ArrayList;
Rubin Xu3bf722a2016-12-15 16:07:38 +000054import java.util.Arrays;
55import java.util.Collections;
Rubin Xu7b7424b2017-03-31 18:03:20 +010056import java.util.HashSet;
57import java.util.List;
58import java.util.Map;
59import java.util.NoSuchElementException;
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 {
139 public AuthenticationToken authToken;
140 public VerifyCredentialResponse gkResponse;
Rubin Xu16c823e2017-06-27 14:44:58 +0100141 public int credentialType;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000142 }
143
144 static class AuthenticationToken {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800145 private final byte mVersion;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000146 /*
147 * Here is the relationship between all three fields:
148 * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
149 * syntheticPassword = hash(P0 || P1)
150 * E0 = P0 encrypted under syntheticPassword, stored on disk.
151 */
152 private @Nullable byte[] E0;
153 private @Nullable byte[] P1;
154 private @NonNull String syntheticPassword;
155
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800156 AuthenticationToken(byte version) {
157 mVersion = version;
158 }
159
160 private byte[] derivePassword(byte[] personalization) {
161 if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
162 return (new SP800Derive(syntheticPassword.getBytes()))
163 .withContext(personalization, PERSONALISATION_CONTEXT);
164 } else {
165 return SyntheticPasswordCrypto.personalisedHash(personalization,
166 syntheticPassword.getBytes());
167 }
168 }
169
Rich Canningsf64ec632019-02-21 12:40:36 -0800170 public byte[] deriveKeyStorePassword() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800171 return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000172 }
173
174 public byte[] deriveGkPassword() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800175 return derivePassword(PERSONALIZATION_SP_GK_AUTH);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000176 }
177
178 public byte[] deriveDiskEncryptionKey() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800179 return derivePassword(PERSONALIZATION_FBE_KEY);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000180 }
181
Andrew Sculle6527c12018-01-05 18:33:58 +0000182 public byte[] deriveVendorAuthSecret() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800183 return derivePassword(PERSONALIZATION_AUTHSECRET_KEY);
Andrew Sculle6527c12018-01-05 18:33:58 +0000184 }
185
Rubin Xuf01e9072018-03-30 20:59:28 +0100186 public byte[] derivePasswordHashFactor() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800187 return derivePassword(PERSONALIZATION_PASSWORD_HASH);
Rubin Xuf01e9072018-03-30 20:59:28 +0100188 }
189
Rubin Xuf095f832017-01-31 15:23:34 +0000190 private void initialize(byte[] P0, byte[] P1) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000191 this.P1 = P1;
192 this.syntheticPassword = String.valueOf(HexEncoding.encode(
193 SyntheticPasswordCrypto.personalisedHash(
194 PERSONALIZATION_SP_SPLIT, P0, P1)));
195 this.E0 = SyntheticPasswordCrypto.encrypt(this.syntheticPassword.getBytes(),
196 PERSONALIZATION_E0, P0);
197 }
198
Rubin Xuf095f832017-01-31 15:23:34 +0000199 public void recreate(byte[] secret) {
200 initialize(secret, this.P1);
201 }
202
Rubin Xu3bf722a2016-12-15 16:07:38 +0000203 protected static AuthenticationToken create() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800204 AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000205 result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
206 secureRandom(SYNTHETIC_PASSWORD_LENGTH));
207 return result;
208 }
209
210 public byte[] computeP0() {
211 if (E0 == null) {
212 return null;
213 }
214 return SyntheticPasswordCrypto.decrypt(syntheticPassword.getBytes(), PERSONALIZATION_E0,
215 E0);
216 }
217 }
218
219 static class PasswordData {
220 byte scryptN;
221 byte scryptR;
222 byte scryptP;
223 public int passwordType;
224 byte[] salt;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100225 // For GateKeeper-based credential, this is the password handle returned by GK,
226 // for weaver-based credential, this is empty.
Rubin Xu3bf722a2016-12-15 16:07:38 +0000227 public byte[] passwordHandle;
228
229 public static PasswordData create(int passwordType) {
230 PasswordData result = new PasswordData();
231 result.scryptN = PASSWORD_SCRYPT_N;
232 result.scryptR = PASSWORD_SCRYPT_R;
233 result.scryptP = PASSWORD_SCRYPT_P;
234 result.passwordType = passwordType;
235 result.salt = secureRandom(PASSWORD_SALT_LENGTH);
236 return result;
237 }
238
239 public static PasswordData fromBytes(byte[] data) {
240 PasswordData result = new PasswordData();
241 ByteBuffer buffer = ByteBuffer.allocate(data.length);
242 buffer.put(data, 0, data.length);
243 buffer.flip();
244 result.passwordType = buffer.getInt();
245 result.scryptN = buffer.get();
246 result.scryptR = buffer.get();
247 result.scryptP = buffer.get();
248 int saltLen = buffer.getInt();
249 result.salt = new byte[saltLen];
250 buffer.get(result.salt);
251 int handleLen = buffer.getInt();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100252 if (handleLen > 0) {
253 result.passwordHandle = new byte[handleLen];
254 buffer.get(result.passwordHandle);
255 } else {
256 result.passwordHandle = null;
257 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000258 return result;
259 }
260
261 public byte[] toBytes() {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100262
Rubin Xu3bf722a2016-12-15 16:07:38 +0000263 ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
Rubin Xu7b7424b2017-03-31 18:03:20 +0100264 + Integer.BYTES + salt.length + Integer.BYTES +
265 (passwordHandle != null ? passwordHandle.length : 0));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000266 buffer.putInt(passwordType);
267 buffer.put(scryptN);
268 buffer.put(scryptR);
269 buffer.put(scryptP);
270 buffer.putInt(salt.length);
271 buffer.put(salt);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100272 if (passwordHandle != null && passwordHandle.length > 0) {
273 buffer.putInt(passwordHandle.length);
274 buffer.put(passwordHandle);
275 } else {
276 buffer.putInt(0);
277 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000278 return buffer.array();
279 }
280 }
281
Rubin Xu7b7424b2017-03-31 18:03:20 +0100282 static class TokenData {
283 byte[] secdiscardableOnDisk;
284 byte[] weaverSecret;
285 byte[] aggregatedSecret;
Ram Periathiruvadi32d53552019-02-19 13:25:46 -0800286 EscrowTokenStateChangeCallback mCallback;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100287 }
288
Adrian Roos2adc2632017-09-05 17:01:42 +0200289 private final Context mContext;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000290 private LockSettingsStorage mStorage;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100291 private IWeaver mWeaver;
292 private WeaverConfig mWeaverConfig;
David Anderson28dea682019-02-20 13:37:51 -0800293 private PasswordSlotManager mPasswordSlotManager;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000294
Adrian Roos7374d3a2017-03-31 14:14:53 -0700295 private final UserManager mUserManager;
296
Adrian Roos2adc2632017-09-05 17:01:42 +0200297 public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
David Anderson28dea682019-02-20 13:37:51 -0800298 UserManager userManager, PasswordSlotManager passwordSlotManager) {
Adrian Roos2adc2632017-09-05 17:01:42 +0200299 mContext = context;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000300 mStorage = storage;
Adrian Roos7374d3a2017-03-31 14:14:53 -0700301 mUserManager = userManager;
David Anderson28dea682019-02-20 13:37:51 -0800302 mPasswordSlotManager = passwordSlotManager;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000303 }
304
Rubin Xu7b7424b2017-03-31 18:03:20 +0100305 @VisibleForTesting
306 protected IWeaver getWeaverService() throws RemoteException {
307 try {
308 return IWeaver.getService();
309 } catch (NoSuchElementException e) {
310 Slog.i(TAG, "Device does not support weaver");
311 return null;
312 }
313 }
314
315 public synchronized void initWeaverService() {
316 if (mWeaver != null) {
317 return;
318 }
319 try {
320 mWeaverConfig = null;
321 mWeaver = getWeaverService();
322 if (mWeaver != null) {
323 mWeaver.getConfig((int status, WeaverConfig config) -> {
324 if (status == WeaverStatus.OK && config.slots > 0) {
325 mWeaverConfig = config;
326 } else {
327 Slog.e(TAG, "Failed to get weaver config, status " + status
328 + " slots: " + config.slots);
329 mWeaver = null;
330 }
331 });
David Anderson28dea682019-02-20 13:37:51 -0800332 mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots());
Rubin Xu7b7424b2017-03-31 18:03:20 +0100333 }
334 } catch (RemoteException e) {
335 Slog.e(TAG, "Failed to get weaver service", e);
336 }
337 }
338
339 private synchronized boolean isWeaverAvailable() {
340 if (mWeaver == null) {
341 //Re-initializing weaver in case there was a transient error preventing access to it.
342 initWeaverService();
343 }
344 return mWeaver != null && mWeaverConfig.slots > 0;
345 }
346
347 /**
348 * Enroll the given key value pair into the specified weaver slot. if the given key is null,
349 * a default all-zero key is used. If the value is not specified, a fresh random secret is
350 * generated as the value.
351 *
352 * @return the value stored in the weaver slot
353 * @throws RemoteException
354 */
355 private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
356 throws RemoteException {
357 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
358 throw new RuntimeException("Invalid slot for weaver");
359 }
360 if (key == null) {
361 key = new byte[mWeaverConfig.keySize];
362 } else if (key.length != mWeaverConfig.keySize) {
363 throw new RuntimeException("Invalid key size for weaver");
364 }
365 if (value == null) {
366 value = secureRandom(mWeaverConfig.valueSize);
367 }
368 int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
369 if (writeStatus != WeaverStatus.OK) {
370 Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
371 return null;
372 }
373 return value;
374 }
375
376 /**
377 * Verify the supplied key against a weaver slot, returning a response indicating whether
378 * the verification is successful, throttled or failed. If successful, the bound secret
379 * is also returned.
380 * @throws RemoteException
381 */
382 private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
383 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
384 throw new RuntimeException("Invalid slot for weaver");
385 }
386 if (key == null) {
387 key = new byte[mWeaverConfig.keySize];
388 } else if (key.length != mWeaverConfig.keySize) {
389 throw new RuntimeException("Invalid key size for weaver");
390 }
391 final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
392 mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
393 switch (status) {
394 case WeaverReadStatus.OK:
395 response[0] = new VerifyCredentialResponse(
396 fromByteArrayList(readResponse.value));
397 break;
398 case WeaverReadStatus.THROTTLE:
399 response[0] = new VerifyCredentialResponse(readResponse.timeout);
400 Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
401 break;
402 case WeaverReadStatus.INCORRECT_KEY:
403 if (readResponse.timeout == 0) {
404 response[0] = VerifyCredentialResponse.ERROR;
405 Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
406 } else {
407 response[0] = new VerifyCredentialResponse(readResponse.timeout);
408 Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
409 }
410 break;
411 case WeaverReadStatus.FAILED:
412 response[0] = VerifyCredentialResponse.ERROR;
413 Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
414 break;
415 default:
416 response[0] = VerifyCredentialResponse.ERROR;
417 Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
418 break;
419 }
420 });
421 return response[0];
422 }
423
424 public void removeUser(int userId) {
Rubin Xu2f22ee42017-09-15 11:56:53 +0100425 for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
426 destroyWeaverSlot(handle, userId);
427 destroySPBlobKey(getHandleName(handle));
Rubin Xu7b7424b2017-03-31 18:03:20 +0100428 }
429 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000430
431 public int getCredentialType(long handle, int userId) {
432 byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
433 if (passwordData == null) {
434 Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
435 return LockPatternUtils.CREDENTIAL_TYPE_NONE;
436 }
437 return PasswordData.fromBytes(passwordData).passwordType;
438 }
439
440 /**
441 * Initializing a new Authentication token, possibly from an existing credential and hash.
442 *
443 * The authentication token would bear a randomly-generated synthetic password.
444 *
445 * This method has the side effect of rebinding the SID of the given user to the
446 * newly-generated SP.
447 *
448 * If the existing credential hash is non-null, the existing SID mill be migrated so
449 * the synthetic password in the authentication token will produce the same SID
450 * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
Rubin Xu7b7424b2017-03-31 18:03:20 +0100451 * in a per-user data storage.)
Rubin Xu3bf722a2016-12-15 16:07:38 +0000452 *
453 * If the existing credential hash is null, it means the given user should have no SID so
454 * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
455 * the supplied credential parameter is also ignored.
456 *
457 * Also saves the escrow information necessary to re-generate the synthetic password under
458 * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
459 * password escrow should be disabled completely on the given user.
460 *
461 */
462 public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
Rich Canningsf64ec632019-02-21 12:40:36 -0800463 byte[] hash, byte[] credential, int userId) throws RemoteException {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000464 AuthenticationToken result = AuthenticationToken.create();
465 GateKeeperResponse response;
466 if (hash != null) {
Rich Canningsf64ec632019-02-21 12:40:36 -0800467 response = gatekeeper.enroll(userId, hash, credential,
Rubin Xu3bf722a2016-12-15 16:07:38 +0000468 result.deriveGkPassword());
469 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
470 Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
471 clearSidForUser(userId);
472 } else {
473 saveSyntheticPasswordHandle(response.getPayload(), userId);
474 }
475 } else {
476 clearSidForUser(userId);
477 }
478 saveEscrowData(result, userId);
479 return result;
480 }
481
482 /**
483 * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
484 * Used when adding password to previously-unsecured devices.
485 */
486 public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
487 int userId) throws RemoteException {
488 GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
489 authToken.deriveGkPassword());
490 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
491 Log.e(TAG, "Fail to create new SID for user " + userId);
492 return;
493 }
494 saveSyntheticPasswordHandle(response.getPayload(), userId);
495 }
496
497 // Nuke the SP handle (and as a result, its SID) for the given user.
498 public void clearSidForUser(int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +0100499 destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000500 }
501
502 public boolean hasSidForUser(int userId) {
503 return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
504 }
505
506 // if null, it means there is no SID associated with the user
507 // This can happen if the user is migrated to SP but currently
508 // do not have a lockscreen password.
509 private byte[] loadSyntheticPasswordHandle(int userId) {
510 return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
511 }
512
513 private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
514 saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
515 }
516
517 private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
518 authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
519 authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
520 return authToken.E0 != null && authToken.P1 != null;
521 }
522
523 private void saveEscrowData(AuthenticationToken authToken, int userId) {
524 saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId);
525 saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId);
526 }
527
Rubin Xuf095f832017-01-31 15:23:34 +0000528 public boolean hasEscrowData(int userId) {
529 return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
530 && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
531 }
532
Rubin Xu3bf722a2016-12-15 16:07:38 +0000533 public void destroyEscrowData(int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +0100534 destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
535 destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000536 }
537
Rubin Xu7b7424b2017-03-31 18:03:20 +0100538 private int loadWeaverSlot(long handle, int userId) {
539 final int LENGTH = Byte.BYTES + Integer.BYTES;
540 byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId);
541 if (data == null || data.length != LENGTH) {
542 return INVALID_WEAVER_SLOT;
543 }
544 ByteBuffer buffer = ByteBuffer.allocate(LENGTH);
545 buffer.put(data, 0, data.length);
546 buffer.flip();
547 if (buffer.get() != WEAVER_VERSION) {
548 Log.e(TAG, "Invalid weaver slot version of handle " + handle);
549 return INVALID_WEAVER_SLOT;
550 }
551 return buffer.getInt();
552 }
553
554 private void saveWeaverSlot(int slot, long handle, int userId) {
555 ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
556 buffer.put(WEAVER_VERSION);
557 buffer.putInt(slot);
558 saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
559 }
560
561 private void destroyWeaverSlot(long handle, int userId) {
562 int slot = loadWeaverSlot(handle, userId);
Rubin Xua339df02018-04-10 09:44:18 +0100563 destroyState(WEAVER_SLOT_NAME, handle, userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100564 if (slot != INVALID_WEAVER_SLOT) {
Rubin Xua339df02018-04-10 09:44:18 +0100565 Set<Integer> usedSlots = getUsedWeaverSlots();
566 if (!usedSlots.contains(slot)) {
567 Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
568 try {
569 weaverEnroll(slot, null, null);
David Anderson28dea682019-02-20 13:37:51 -0800570 mPasswordSlotManager.markSlotDeleted(slot);
Rubin Xua339df02018-04-10 09:44:18 +0100571 } catch (RemoteException e) {
572 Log.w(TAG, "Failed to destroy slot", e);
573 }
574 } else {
575 Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100576 }
577 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100578 }
579
Rubin Xua339df02018-04-10 09:44:18 +0100580 /**
581 * Return the set of weaver slots that are currently in use by all users on the device.
582 * <p>
583 * <em>Note:</em> Users who are in the process of being deleted are not tracked here
584 * (due to them being marked as partial in UserManager so not visible from
585 * {@link UserManager#getUsers}). As a result their weaver slots will not be considered
586 * taken and can be reused by new users. Care should be taken when cleaning up the
587 * deleted user in {@link #removeUser}, to prevent a reused slot from being erased
588 * unintentionally.
589 */
590 private Set<Integer> getUsedWeaverSlots() {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100591 Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
592 WEAVER_SLOT_NAME);
593 HashSet<Integer> slots = new HashSet<>();
594 for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) {
595 for (Long handle : entry.getValue()) {
596 int slot = loadWeaverSlot(handle, entry.getKey());
597 slots.add(slot);
598 }
599 }
Rubin Xua339df02018-04-10 09:44:18 +0100600 return slots;
601 }
602
603 private int getNextAvailableWeaverSlot() {
604 Set<Integer> usedSlots = getUsedWeaverSlots();
David Anderson28dea682019-02-20 13:37:51 -0800605 usedSlots.addAll(mPasswordSlotManager.getUsedSlots());
Rubin Xu7b7424b2017-03-31 18:03:20 +0100606 for (int i = 0; i < mWeaverConfig.slots; i++) {
Rubin Xua339df02018-04-10 09:44:18 +0100607 if (!usedSlots.contains(i)) {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100608 return i;
609 }
610 }
611 throw new RuntimeException("Run out of weaver slots.");
612 }
613
Rubin Xu3bf722a2016-12-15 16:07:38 +0000614 /**
615 * Create a new password based SP blob based on the supplied authentication token, such that
616 * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
617 * in the same authentication token.
618 *
619 * This method only creates SP blob wrapping around the given synthetic password and does not
620 * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
621 * is consistent with the device state by calling other APIs in this class.
622 *
623 * @see #newSidForUser
624 * @see #clearSidForUser
625 */
626 public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
Rich Canningsf64ec632019-02-21 12:40:36 -0800627 byte[] credential, int credentialType, AuthenticationToken authToken,
Adrian Roos7374d3a2017-03-31 14:14:53 -0700628 int requestedQuality, int userId)
Rubin Xu3bf722a2016-12-15 16:07:38 +0000629 throws RemoteException {
630 if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
631 credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
632 credential = DEFAULT_PASSWORD;
633 }
634
635 long handle = generateHandle();
636 PasswordData pwd = PasswordData.create(credentialType);
637 byte[] pwdToken = computePasswordToken(credential, pwd);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100638 final long sid;
639 final byte[] applicationId;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000640
Rubin Xu7b7424b2017-03-31 18:03:20 +0100641 if (isWeaverAvailable()) {
642 // Weaver based user password
643 int weaverSlot = getNextAvailableWeaverSlot();
Rubin Xua339df02018-04-10 09:44:18 +0100644 Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100645 byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
646 if (weaverSecret == null) {
647 Log.e(TAG, "Fail to enroll user password under weaver " + userId);
648 return DEFAULT_HANDLE;
649 }
650 saveWeaverSlot(weaverSlot, handle, userId);
David Anderson28dea682019-02-20 13:37:51 -0800651 mPasswordSlotManager.markSlotInUse(weaverSlot);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700652 synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100653
654 pwd.passwordHandle = null;
655 sid = GateKeeper.INVALID_SECURE_USER_ID;
656 applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret);
657 } else {
Rubin Xu54c19b62017-04-26 19:40:15 +0100658 // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
659 // to prevent them from accumulating and causing problems.
660 gatekeeper.clearSecureUserId(fakeUid(userId));
Rubin Xu7b7424b2017-03-31 18:03:20 +0100661 // GateKeeper based user password
662 GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
663 passwordTokenToGkInput(pwdToken));
664 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
665 Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
666 return DEFAULT_HANDLE;
667 }
668 pwd.passwordHandle = response.getPayload();
669 sid = sidFromPasswordHandle(pwd.passwordHandle);
670 applicationId = transformUnderSecdiscardable(pwdToken,
671 createSecdiscardable(handle, userId));
Adrian Roos7374d3a2017-03-31 14:14:53 -0700672 synchronizeFrpPassword(pwd, requestedQuality, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000673 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000674 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
675
Rubin Xu3bf722a2016-12-15 16:07:38 +0000676 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
677 applicationId, sid, userId);
678 return handle;
679 }
680
Adrian Roos7374d3a2017-03-31 14:14:53 -0700681 public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
Rich Canningsf64ec632019-02-21 12:40:36 -0800682 byte[] userCredential, int credentialType,
Adrian Roos7374d3a2017-03-31 14:14:53 -0700683 ICheckCredentialProgressCallback progressCallback) throws RemoteException {
684 PersistentData persistentData = mStorage.readPersistentDataBlock();
685 if (persistentData.type == PersistentData.TYPE_SP) {
686 PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
687 byte[] pwdToken = computePasswordToken(userCredential, pwd);
688
Adrian Roosb7d69322017-08-08 18:32:03 +0200689 GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
690 0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
Adrian Roos7374d3a2017-03-31 14:14:53 -0700691 return VerifyCredentialResponse.fromGateKeeperResponse(response);
692 } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
693 PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
694 byte[] pwdToken = computePasswordToken(userCredential, pwd);
695 int weaverSlot = persistentData.userId;
696
697 return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
698 } else {
699 Log.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
700 + persistentData.type);
701 return VerifyCredentialResponse.ERROR;
702 }
703 }
704
705
Adrian Roos60dcbbf2017-08-08 16:19:33 +0200706 public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) {
707 if (mStorage.getPersistentDataBlock() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200708 && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) {
Adrian Roos60dcbbf2017-08-08 16:19:33 +0200709 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle,
710 userInfo.id));
711 if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
712 int weaverSlot = loadWeaverSlot(handle, userInfo.id);
713 if (weaverSlot != INVALID_WEAVER_SLOT) {
714 synchronizeWeaverFrpPassword(pwd, requestedQuality, userInfo.id, weaverSlot);
715 } else {
716 synchronizeFrpPassword(pwd, requestedQuality, userInfo.id);
717 }
718 }
719 }
720 }
721
Adrian Roos7374d3a2017-03-31 14:14:53 -0700722 private void synchronizeFrpPassword(PasswordData pwd,
723 int requestedQuality, int userId) {
724 if (mStorage.getPersistentDataBlock() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200725 && LockPatternUtils.userOwnsFrpCredential(mContext,
726 mUserManager.getUserInfo(userId))) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700727 if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
728 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
729 pwd.toBytes());
730 } else {
731 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
732 }
733 }
734 }
735
736 private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
737 int weaverSlot) {
738 if (mStorage.getPersistentDataBlock() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200739 && LockPatternUtils.userOwnsFrpCredential(mContext,
740 mUserManager.getUserInfo(userId))) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700741 if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
742 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
743 requestedQuality, pwd.toBytes());
744 } else {
745 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, 0, 0, null);
746 }
747 }
748 }
749
Rubin Xu7b7424b2017-03-31 18:03:20 +0100750 private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
Rubin Xuf095f832017-01-31 15:23:34 +0000751
Ram Periathiruvadi32d53552019-02-19 13:25:46 -0800752 /**
753 * Create a token based Synthetic password for the given user.
754 * @return
755 */
756 public long createTokenBasedSyntheticPassword(byte[] token, int userId,
757 @Nullable EscrowTokenStateChangeCallback changeCallback) {
Rubin Xuf095f832017-01-31 15:23:34 +0000758 long handle = generateHandle();
Rubin Xuf095f832017-01-31 15:23:34 +0000759 if (!tokenMap.containsKey(userId)) {
760 tokenMap.put(userId, new ArrayMap<>());
761 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100762 TokenData tokenData = new TokenData();
763 final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
764 if (isWeaverAvailable()) {
765 tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
766 tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
767 PERSONALISATION_WEAVER_TOKEN, secdiscardable);
768 } else {
769 tokenData.secdiscardableOnDisk = secdiscardable;
770 tokenData.weaverSecret = null;
771 }
772 tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
Ram Periathiruvadi32d53552019-02-19 13:25:46 -0800773 tokenData.mCallback = changeCallback;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100774
775 tokenMap.get(userId).put(handle, tokenData);
Rubin Xuf095f832017-01-31 15:23:34 +0000776 return handle;
777 }
778
779 public Set<Long> getPendingTokensForUser(int userId) {
780 if (!tokenMap.containsKey(userId)) {
781 return Collections.emptySet();
782 }
783 return tokenMap.get(userId).keySet();
784 }
785
786 public boolean removePendingToken(long handle, int userId) {
787 if (!tokenMap.containsKey(userId)) {
788 return false;
789 }
790 return tokenMap.get(userId).remove(handle) != null;
791 }
792
793 public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
794 int userId) {
795 if (!tokenMap.containsKey(userId)) {
796 return false;
797 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100798 TokenData tokenData = tokenMap.get(userId).get(handle);
799 if (tokenData == null) {
Rubin Xuf095f832017-01-31 15:23:34 +0000800 return false;
801 }
802 if (!loadEscrowData(authToken, userId)) {
803 Log.w(TAG, "User is not escrowable");
804 return false;
805 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100806 if (isWeaverAvailable()) {
807 int slot = getNextAvailableWeaverSlot();
808 try {
Rubin Xua339df02018-04-10 09:44:18 +0100809 Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100810 weaverEnroll(slot, null, tokenData.weaverSecret);
811 } catch (RemoteException e) {
812 Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
813 return false;
814 }
815 saveWeaverSlot(slot, handle, userId);
David Anderson28dea682019-02-20 13:37:51 -0800816 mPasswordSlotManager.markSlotInUse(slot);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100817 }
818 saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000819 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
Rubin Xu7b7424b2017-03-31 18:03:20 +0100820 tokenData.aggregatedSecret, 0L, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000821 tokenMap.get(userId).remove(handle);
Ram Periathiruvadi32d53552019-02-19 13:25:46 -0800822 if (tokenData.mCallback != null) {
823 tokenData.mCallback.onEscrowTokenActivated(handle, userId);
824 }
Rubin Xuf095f832017-01-31 15:23:34 +0000825 return true;
826 }
827
Rubin Xu3bf722a2016-12-15 16:07:38 +0000828 private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
829 byte[] applicationId, long sid, int userId) {
Rubin Xuf095f832017-01-31 15:23:34 +0000830 final byte[] secret;
831 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
832 secret = authToken.computeP0();
833 } else {
834 secret = authToken.syntheticPassword.getBytes();
835 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000836 byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
837 byte[] blob = new byte[content.length + 1 + 1];
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800838 /*
839 * We can upgrade from v1 to v2 because that's just a change in the way that
840 * the SP is stored. However, we can't upgrade to v3 because that is a change
841 * in the way that passwords are derived from the SP.
842 */
843 if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
844 blob[0] = SYNTHETIC_PASSWORD_VERSION_V3;
845 } else {
846 blob[0] = SYNTHETIC_PASSWORD_VERSION_V2;
847 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000848 blob[1] = type;
849 System.arraycopy(content, 0, blob, 2, content.length);
850 saveState(SP_BLOB_NAME, blob, handle, userId);
851 }
852
853 /**
854 * Decrypt a synthetic password by supplying the user credential and corresponding password
855 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
856 * verification to referesh the SID & Auth token maintained by the system.
Rubin Xu16c823e2017-06-27 14:44:58 +0100857 * Note: the credential type is not validated here since there are call sites where the type is
858 * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
Rubin Xu3bf722a2016-12-15 16:07:38 +0000859 */
860 public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
Rich Canningsf64ec632019-02-21 12:40:36 -0800861 long handle, byte[] credential, int userId,
Rubin Xucf326f12017-11-15 11:55:35 +0000862 ICheckCredentialProgressCallback progressCallback) throws RemoteException {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000863 if (credential == null) {
864 credential = DEFAULT_PASSWORD;
865 }
866 AuthenticationResult result = new AuthenticationResult();
867 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
Rubin Xu16c823e2017-06-27 14:44:58 +0100868 result.credentialType = pwd.passwordType;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000869 byte[] pwdToken = computePasswordToken(credential, pwd);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000870
Rubin Xu7b7424b2017-03-31 18:03:20 +0100871 final byte[] applicationId;
Rubin Xu8c528652017-10-31 15:40:32 +0000872 final long sid;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100873 int weaverSlot = loadWeaverSlot(handle, userId);
874 if (weaverSlot != INVALID_WEAVER_SLOT) {
875 // Weaver based user password
876 if (!isWeaverAvailable()) {
877 Log.e(TAG, "No weaver service to unwrap password based SP");
878 result.gkResponse = VerifyCredentialResponse.ERROR;
879 return result;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000880 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100881 result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
882 if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
883 return result;
884 }
Rubin Xu8c528652017-10-31 15:40:32 +0000885 sid = GateKeeper.INVALID_SECURE_USER_ID;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100886 applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
887 } else {
888 byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
889 GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
890 pwd.passwordHandle, gkPwdToken);
891 int responseCode = response.getResponseCode();
892 if (responseCode == GateKeeperResponse.RESPONSE_OK) {
893 result.gkResponse = VerifyCredentialResponse.OK;
894 if (response.getShouldReEnroll()) {
895 GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
896 pwd.passwordHandle, gkPwdToken, gkPwdToken);
897 if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
898 pwd.passwordHandle = reenrollResponse.getPayload();
899 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700900 synchronizeFrpPassword(pwd,
901 pwd.passwordType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
902 ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
903 : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
904 /* TODO(roosa): keep the same password quality */,
905 userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100906 } else {
907 Log.w(TAG, "Fail to re-enroll user password for user " + userId);
908 // continue the flow anyway
909 }
910 }
911 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
912 result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
913 return result;
914 } else {
915 result.gkResponse = VerifyCredentialResponse.ERROR;
916 return result;
917 }
Rubin Xu8c528652017-10-31 15:40:32 +0000918 sid = sidFromPasswordHandle(pwd.passwordHandle);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100919 applicationId = transformUnderSecdiscardable(pwdToken,
920 loadSecdiscardable(handle, userId));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000921 }
Rubin Xucf326f12017-11-15 11:55:35 +0000922 // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
923 // Notify the callback so the keyguard UI can proceed immediately.
924 if (progressCallback != null) {
925 progressCallback.onCredentialVerified();
926 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000927 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
Rubin Xu8c528652017-10-31 15:40:32 +0000928 applicationId, sid, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000929
930 // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
931 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
932 return result;
933 }
934
Rubin Xuf095f832017-01-31 15:23:34 +0000935 /**
936 * Decrypt a synthetic password by supplying an escrow token and corresponding token
937 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
938 * verification to referesh the SID & Auth token maintained by the system.
939 */
940 public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
941 IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
942 throws RemoteException {
943 AuthenticationResult result = new AuthenticationResult();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100944 byte[] secdiscardable = loadSecdiscardable(handle, userId);
945 int slotId = loadWeaverSlot(handle, userId);
946 if (slotId != INVALID_WEAVER_SLOT) {
947 if (!isWeaverAvailable()) {
948 Log.e(TAG, "No weaver service to unwrap token based SP");
949 result.gkResponse = VerifyCredentialResponse.ERROR;
950 return result;
951 }
952 VerifyCredentialResponse response = weaverVerify(slotId, null);
953 if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
954 response.getPayload() == null) {
955 Log.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
956 result.gkResponse = VerifyCredentialResponse.ERROR;
957 return result;
958 }
959 secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
960 PERSONALISATION_WEAVER_TOKEN, secdiscardable);
961 }
962 byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
Rubin Xuf095f832017-01-31 15:23:34 +0000963 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
Rubin Xu8c528652017-10-31 15:40:32 +0000964 applicationId, 0L, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000965 if (result.authToken != null) {
966 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
967 if (result.gkResponse == null) {
968 // The user currently has no password. return OK with null payload so null
969 // is propagated to unlockUser()
970 result.gkResponse = VerifyCredentialResponse.OK;
971 }
972 } else {
973 result.gkResponse = VerifyCredentialResponse.ERROR;
974 }
975 return result;
976 }
977
Rubin Xu3bf722a2016-12-15 16:07:38 +0000978 private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
Rubin Xu8c528652017-10-31 15:40:32 +0000979 byte[] applicationId, long sid, int userId) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000980 byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
981 if (blob == null) {
982 return null;
983 }
Rubin Xu8c528652017-10-31 15:40:32 +0000984 final byte version = blob[0];
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800985 if (version != SYNTHETIC_PASSWORD_VERSION_V3
986 && version != SYNTHETIC_PASSWORD_VERSION_V2
987 && version != SYNTHETIC_PASSWORD_VERSION_V1) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000988 throw new RuntimeException("Unknown blob version");
989 }
990 if (blob[1] != type) {
991 throw new RuntimeException("Invalid blob type");
992 }
Rubin Xu8c528652017-10-31 15:40:32 +0000993 final byte[] secret;
994 if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
995 secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
996 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
997 } else {
998 secret = decryptSPBlob(getHandleName(handle),
Rubin Xu3bf722a2016-12-15 16:07:38 +0000999 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
Rubin Xu8c528652017-10-31 15:40:32 +00001000 }
Rubin Xu3bf722a2016-12-15 16:07:38 +00001001 if (secret == null) {
1002 Log.e(TAG, "Fail to decrypt SP for user " + userId);
1003 return null;
1004 }
Paul Crowley0d40d6e72018-11-28 12:48:16 -08001005 AuthenticationToken result = new AuthenticationToken(version);
Rubin Xuf095f832017-01-31 15:23:34 +00001006 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
1007 if (!loadEscrowData(result, userId)) {
1008 Log.e(TAG, "User is not escrowable: " + userId);
1009 return null;
1010 }
1011 result.recreate(secret);
1012 } else {
1013 result.syntheticPassword = new String(secret);
1014 }
Rubin Xu8c528652017-10-31 15:40:32 +00001015 if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
1016 Log.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
1017 createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
1018 }
Rubin Xu3bf722a2016-12-15 16:07:38 +00001019 return result;
1020 }
1021
1022 /**
1023 * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
1024 * if required.
1025 *
1026 * Normally performing verifyChallenge with an AuthenticationToken should always return
1027 * RESPONSE_OK, since user authentication failures are detected earlier when trying to
1028 * decrypt SP.
1029 */
Rubin Xu8b30ec32017-03-05 00:47:09 +00001030 public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
Rubin Xu3bf722a2016-12-15 16:07:38 +00001031 @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
1032 byte[] spHandle = loadSyntheticPasswordHandle(userId);
1033 if (spHandle == null) {
1034 // There is no password handle associated with the given user, i.e. the user is not
1035 // secured by lockscreen and has no SID, so just return here;
1036 return null;
1037 }
1038 VerifyCredentialResponse result;
1039 GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
1040 spHandle, auth.deriveGkPassword());
1041 int responseCode = response.getResponseCode();
1042 if (responseCode == GateKeeperResponse.RESPONSE_OK) {
1043 result = new VerifyCredentialResponse(response.getPayload());
1044 if (response.getShouldReEnroll()) {
1045 response = gatekeeper.enroll(userId, spHandle,
1046 spHandle, auth.deriveGkPassword());
1047 if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
1048 spHandle = response.getPayload();
1049 saveSyntheticPasswordHandle(spHandle, userId);
1050 // Call self again to re-verify with updated handle
1051 return verifyChallenge(gatekeeper, auth, challenge, userId);
1052 } else {
1053 Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
1054 // Fall through, return existing handle
1055 }
1056 }
1057 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
1058 result = new VerifyCredentialResponse(response.getTimeout());
1059 } else {
1060 result = VerifyCredentialResponse.ERROR;
1061 }
1062 return result;
1063 }
1064
1065 public boolean existsHandle(long handle, int userId) {
1066 return hasState(SP_BLOB_NAME, handle, userId);
1067 }
1068
Rubin Xuf095f832017-01-31 15:23:34 +00001069 public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
1070 destroySyntheticPassword(handle, userId);
Rubin Xuaa32d152017-04-27 17:01:05 +01001071 destroyState(SECDISCARDABLE_NAME, handle, userId);
Rubin Xuf095f832017-01-31 15:23:34 +00001072 }
1073
Rubin Xu3bf722a2016-12-15 16:07:38 +00001074 public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
1075 destroySyntheticPassword(handle, userId);
Rubin Xuaa32d152017-04-27 17:01:05 +01001076 destroyState(SECDISCARDABLE_NAME, handle, userId);
1077 destroyState(PASSWORD_DATA_NAME, handle, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001078 }
1079
1080 private void destroySyntheticPassword(long handle, int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +01001081 destroyState(SP_BLOB_NAME, handle, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001082 destroySPBlobKey(getHandleName(handle));
Rubin Xu7b7424b2017-03-31 18:03:20 +01001083 if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
1084 destroyWeaverSlot(handle, userId);
1085 }
1086 }
1087
1088 private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
1089 byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash(
1090 PERSONALISATION_WEAVER_PASSWORD, secret);
1091 byte[] result = new byte[data.length + weaverSecret.length];
1092 System.arraycopy(data, 0, result, 0, data.length);
1093 System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length);
1094 return result;
Rubin Xu3bf722a2016-12-15 16:07:38 +00001095 }
1096
1097 private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
1098 byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
1099 PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
1100 byte[] result = new byte[data.length + secdiscardable.length];
1101 System.arraycopy(data, 0, result, 0, data.length);
1102 System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
1103 return result;
1104 }
1105
1106 private byte[] createSecdiscardable(long handle, int userId) {
1107 byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
Rubin Xu7b7424b2017-03-31 18:03:20 +01001108 saveSecdiscardable(handle, data, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001109 return data;
1110 }
1111
Rubin Xu7b7424b2017-03-31 18:03:20 +01001112 private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
1113 saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
1114 }
1115
Rubin Xu3bf722a2016-12-15 16:07:38 +00001116 private byte[] loadSecdiscardable(long handle, int userId) {
1117 return loadState(SECDISCARDABLE_NAME, handle, userId);
1118 }
1119
1120 private boolean hasState(String stateName, long handle, int userId) {
1121 return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
1122 }
1123
1124 private byte[] loadState(String stateName, long handle, int userId) {
1125 return mStorage.readSyntheticPasswordState(userId, handle, stateName);
1126 }
1127
1128 private void saveState(String stateName, byte[] data, long handle, int userId) {
1129 mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
1130 }
1131
Rubin Xuaa32d152017-04-27 17:01:05 +01001132 private void destroyState(String stateName, long handle, int userId) {
1133 mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001134 }
1135
1136 protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
1137 return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
1138 }
1139
1140 protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
1141 return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
1142 }
1143
1144 protected void destroySPBlobKey(String keyAlias) {
1145 SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
1146 }
1147
1148 public static long generateHandle() {
1149 SecureRandom rng = new SecureRandom();
1150 long result;
1151 do {
1152 result = rng.nextLong();
1153 } while (result == DEFAULT_HANDLE);
1154 return result;
1155 }
1156
1157 private int fakeUid(int uid) {
1158 return 100000 + uid;
1159 }
1160
1161 protected static byte[] secureRandom(int length) {
1162 try {
1163 return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
1164 } catch (NoSuchAlgorithmException e) {
1165 e.printStackTrace();
1166 return null;
1167 }
1168 }
1169
1170 private String getHandleName(long handle) {
1171 return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
1172 }
1173
Rich Canningsf64ec632019-02-21 12:40:36 -08001174 private byte[] computePasswordToken(byte[] password, PasswordData data) {
Rubin Xu3bf722a2016-12-15 16:07:38 +00001175 return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
1176 PASSWORD_TOKEN_LENGTH);
1177 }
1178
1179 private byte[] passwordTokenToGkInput(byte[] token) {
1180 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
1181 }
1182
Rubin Xu7b7424b2017-03-31 18:03:20 +01001183 private byte[] passwordTokenToWeaverKey(byte[] token) {
1184 byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
1185 if (key.length < mWeaverConfig.keySize) {
1186 throw new RuntimeException("weaver key length too small");
1187 }
1188 return Arrays.copyOf(key, mWeaverConfig.keySize);
1189 }
1190
Rubin Xu3bf722a2016-12-15 16:07:38 +00001191 protected long sidFromPasswordHandle(byte[] handle) {
1192 return nativeSidFromPasswordHandle(handle);
1193 }
1194
Rich Canningsf64ec632019-02-21 12:40:36 -08001195 protected byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) {
1196 return new Scrypt().scrypt(password, salt, n, r, p, outLen);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001197 }
1198
1199 native long nativeSidFromPasswordHandle(byte[] handle);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001200
Rubin Xu7b7424b2017-03-31 18:03:20 +01001201 protected static ArrayList<Byte> toByteArrayList(byte[] data) {
1202 ArrayList<Byte> result = new ArrayList<Byte>(data.length);
1203 for (int i = 0; i < data.length; i++) {
1204 result.add(data[i]);
1205 }
1206 return result;
1207 }
1208
1209 protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
1210 byte[] result = new byte[data.size()];
1211 for (int i = 0; i < data.size(); i++) {
1212 result[i] = data.get(i);
1213 }
1214 return result;
1215 }
1216
Rich Canningsf64ec632019-02-21 12:40:36 -08001217 protected static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes();
1218 private static byte[] bytesToHex(byte[] bytes) {
Rubin Xu3bf722a2016-12-15 16:07:38 +00001219 if (bytes == null) {
Rich Canningsf64ec632019-02-21 12:40:36 -08001220 return "null".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +00001221 }
Rich Canningsf64ec632019-02-21 12:40:36 -08001222 byte[] hexBytes = new byte[bytes.length * 2];
Rubin Xu3bf722a2016-12-15 16:07:38 +00001223 for ( int j = 0; j < bytes.length; j++ ) {
1224 int v = bytes[j] & 0xFF;
Rich Canningsf64ec632019-02-21 12:40:36 -08001225 hexBytes[j * 2] = HEX_ARRAY[v >>> 4];
1226 hexBytes[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
Rubin Xu3bf722a2016-12-15 16:07:38 +00001227 }
Rich Canningsf64ec632019-02-21 12:40:36 -08001228 return hexBytes;
Rubin Xu3bf722a2016-12-15 16:07:38 +00001229 }
1230}