blob: ea39dff1acc928282f4dfa0137a6f76cca4e74a2 [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
19import android.annotation.NonNull;
20import android.annotation.Nullable;
Adrian Roos7374d3a2017-03-31 14:14:53 -070021import android.app.admin.DevicePolicyManager;
Adrian Roos2adc2632017-09-05 17:01:42 +020022import android.content.Context;
Adrian Roos60dcbbf2017-08-08 16:19:33 +020023import android.content.pm.UserInfo;
Rubin Xu7b7424b2017-03-31 18:03:20 +010024import android.hardware.weaver.V1_0.IWeaver;
25import android.hardware.weaver.V1_0.WeaverConfig;
26import android.hardware.weaver.V1_0.WeaverReadResponse;
27import android.hardware.weaver.V1_0.WeaverReadStatus;
28import android.hardware.weaver.V1_0.WeaverStatus;
Adrian Roos7374d3a2017-03-31 14:14:53 -070029import android.os.RemoteException;
30import android.os.UserManager;
Paul Crowley0d40d6e72018-11-28 12:48:16 -080031import android.security.GateKeeper;
Bo Zhuf2c99342018-12-06 14:52:33 -080032import android.security.Scrypt;
Rubin Xu3bf722a2016-12-15 16:07:38 +000033import android.service.gatekeeper.GateKeeperResponse;
34import android.service.gatekeeper.IGateKeeperService;
35import android.util.ArrayMap;
36import android.util.Log;
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;
43import com.android.internal.widget.VerifyCredentialResponse;
Adrian Roos7374d3a2017-03-31 14:14:53 -070044import com.android.server.locksettings.LockSettingsStorage.PersistentData;
Rubin Xu3bf722a2016-12-15 16:07:38 +000045
46import libcore.util.HexEncoding;
47
48import java.nio.ByteBuffer;
Rubin Xu3bf722a2016-12-15 16:07:38 +000049import java.security.NoSuchAlgorithmException;
50import java.security.SecureRandom;
Rubin Xu7b7424b2017-03-31 18:03:20 +010051import java.util.ArrayList;
Rubin Xu3bf722a2016-12-15 16:07:38 +000052import java.util.Arrays;
53import java.util.Collections;
Rubin Xu7b7424b2017-03-31 18:03:20 +010054import java.util.HashSet;
55import java.util.List;
56import java.util.Map;
57import java.util.NoSuchElementException;
Rubin Xu3bf722a2016-12-15 16:07:38 +000058import java.util.Set;
59
60
61/**
62 * A class that maintains the wrapping of synthetic password by user credentials or escrow tokens.
63 * It's (mostly) a pure storage for synthetic passwords, providing APIs to creating and destroying
64 * synthetic password blobs which are wrapped by user credentials or escrow tokens.
65 *
66 * Here is the assumptions it makes:
67 * Each user has one single synthetic password at any time.
68 * The SP has an associated password handle, which binds to the SID for that user. The password
69 * handle is persisted by SyntheticPasswordManager internally.
70 * If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD
Rubin Xu7b7424b2017-03-31 18:03:20 +010071 *
72 * Information persisted on disk:
73 * for each user (stored under DEFAULT_HANDLE):
74 * SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user
75 * credential exists, cleared when user clears their credential.
Rubin Xu128180b2017-04-12 18:02:44 +010076 * SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combined with escrow
Rubin Xu7b7424b2017-03-31 18:03:20 +010077 * tokens. Destroyed when escrow support is turned off for the given user.
78 *
79 * for each SP blob under the user (stored under the corresponding handle):
80 * SP_BLOB_NAME: The encrypted synthetic password. Always exists.
81 * PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP.
Rubin Xu128180b2017-04-12 18:02:44 +010082 * SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME for the
83 * purpose of secure deletion. Exists if this is a non-weaver SP
Rubin Xu7b7424b2017-03-31 18:03:20 +010084 * (both password and token based), or it's a token-based SP under weaver.
85 * WEAVER_SLOT: Metadata about the weaver slot used. Only exists if this is a SP under weaver.
86 *
87 *
Rubin Xu3bf722a2016-12-15 16:07:38 +000088 */
89public class SyntheticPasswordManager {
90 private static final String SP_BLOB_NAME = "spblob";
91 private static final String SP_E0_NAME = "e0";
92 private static final String SP_P1_NAME = "p1";
93 private static final String SP_HANDLE_NAME = "handle";
94 private static final String SECDISCARDABLE_NAME = "secdis";
95 private static final int SECDISCARDABLE_LENGTH = 16 * 1024;
96 private static final String PASSWORD_DATA_NAME = "pwd";
Rubin Xu7b7424b2017-03-31 18:03:20 +010097 private static final String WEAVER_SLOT_NAME = "weaver";
Rubin Xu3bf722a2016-12-15 16:07:38 +000098
Rubin Xu7b7424b2017-03-31 18:03:20 +010099 public static final long DEFAULT_HANDLE = 0L;
Rich Canningsf64ec632019-02-21 12:40:36 -0800100 private static final byte[] DEFAULT_PASSWORD = "default-password".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000101
Rubin Xu7b7424b2017-03-31 18:03:20 +0100102 private static final byte WEAVER_VERSION = 1;
103 private static final int INVALID_WEAVER_SLOT = -1;
104
Rubin Xu8c528652017-10-31 15:40:32 +0000105 private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1;
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800106 private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2;
107 private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000108 private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
Rubin Xuf095f832017-01-31 15:23:34 +0000109 private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000110
111 // 256-bit synthetic password
112 private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
113
Rubin Xu157bddd2017-04-24 15:51:59 +0100114 private static final int PASSWORD_SCRYPT_N = 11;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000115 private static final int PASSWORD_SCRYPT_R = 3;
116 private static final int PASSWORD_SCRYPT_P = 1;
117 private static final int PASSWORD_SALT_LENGTH = 16;
118 private static final int PASSWORD_TOKEN_LENGTH = 32;
119 private static final String TAG = "SyntheticPasswordManager";
120
121 private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
122 private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes();
123 private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();
124 private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
125 private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
Andrew Sculle6527c12018-01-05 18:33:58 +0000126 private static final byte[] PERSONALIZATION_AUTHSECRET_KEY = "authsecret-hal".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000127 private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
Rubin Xuf01e9072018-03-30 20:59:28 +0100128 private static final byte[] PERSONALIZATION_PASSWORD_HASH = "pw-hash".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000129 private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100130 private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
131 private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
132 private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800133 private static final byte[] PERSONALISATION_CONTEXT =
134 "android-synthetic-password-personalization-context".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000135
136 static class AuthenticationResult {
137 public AuthenticationToken authToken;
138 public VerifyCredentialResponse gkResponse;
Rubin Xu16c823e2017-06-27 14:44:58 +0100139 public int credentialType;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000140 }
141
142 static class AuthenticationToken {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800143 private final byte mVersion;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000144 /*
145 * Here is the relationship between all three fields:
146 * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
147 * syntheticPassword = hash(P0 || P1)
148 * E0 = P0 encrypted under syntheticPassword, stored on disk.
149 */
150 private @Nullable byte[] E0;
151 private @Nullable byte[] P1;
152 private @NonNull String syntheticPassword;
153
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800154 AuthenticationToken(byte version) {
155 mVersion = version;
156 }
157
158 private byte[] derivePassword(byte[] personalization) {
159 if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
160 return (new SP800Derive(syntheticPassword.getBytes()))
161 .withContext(personalization, PERSONALISATION_CONTEXT);
162 } else {
163 return SyntheticPasswordCrypto.personalisedHash(personalization,
164 syntheticPassword.getBytes());
165 }
166 }
167
Rich Canningsf64ec632019-02-21 12:40:36 -0800168 public byte[] deriveKeyStorePassword() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800169 return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000170 }
171
172 public byte[] deriveGkPassword() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800173 return derivePassword(PERSONALIZATION_SP_GK_AUTH);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000174 }
175
176 public byte[] deriveDiskEncryptionKey() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800177 return derivePassword(PERSONALIZATION_FBE_KEY);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000178 }
179
Andrew Sculle6527c12018-01-05 18:33:58 +0000180 public byte[] deriveVendorAuthSecret() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800181 return derivePassword(PERSONALIZATION_AUTHSECRET_KEY);
Andrew Sculle6527c12018-01-05 18:33:58 +0000182 }
183
Rubin Xuf01e9072018-03-30 20:59:28 +0100184 public byte[] derivePasswordHashFactor() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800185 return derivePassword(PERSONALIZATION_PASSWORD_HASH);
Rubin Xuf01e9072018-03-30 20:59:28 +0100186 }
187
Rubin Xuf095f832017-01-31 15:23:34 +0000188 private void initialize(byte[] P0, byte[] P1) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000189 this.P1 = P1;
190 this.syntheticPassword = String.valueOf(HexEncoding.encode(
191 SyntheticPasswordCrypto.personalisedHash(
192 PERSONALIZATION_SP_SPLIT, P0, P1)));
193 this.E0 = SyntheticPasswordCrypto.encrypt(this.syntheticPassword.getBytes(),
194 PERSONALIZATION_E0, P0);
195 }
196
Rubin Xuf095f832017-01-31 15:23:34 +0000197 public void recreate(byte[] secret) {
198 initialize(secret, this.P1);
199 }
200
Rubin Xu3bf722a2016-12-15 16:07:38 +0000201 protected static AuthenticationToken create() {
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800202 AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000203 result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
204 secureRandom(SYNTHETIC_PASSWORD_LENGTH));
205 return result;
206 }
207
208 public byte[] computeP0() {
209 if (E0 == null) {
210 return null;
211 }
212 return SyntheticPasswordCrypto.decrypt(syntheticPassword.getBytes(), PERSONALIZATION_E0,
213 E0);
214 }
215 }
216
217 static class PasswordData {
218 byte scryptN;
219 byte scryptR;
220 byte scryptP;
221 public int passwordType;
222 byte[] salt;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100223 // For GateKeeper-based credential, this is the password handle returned by GK,
224 // for weaver-based credential, this is empty.
Rubin Xu3bf722a2016-12-15 16:07:38 +0000225 public byte[] passwordHandle;
226
227 public static PasswordData create(int passwordType) {
228 PasswordData result = new PasswordData();
229 result.scryptN = PASSWORD_SCRYPT_N;
230 result.scryptR = PASSWORD_SCRYPT_R;
231 result.scryptP = PASSWORD_SCRYPT_P;
232 result.passwordType = passwordType;
233 result.salt = secureRandom(PASSWORD_SALT_LENGTH);
234 return result;
235 }
236
237 public static PasswordData fromBytes(byte[] data) {
238 PasswordData result = new PasswordData();
239 ByteBuffer buffer = ByteBuffer.allocate(data.length);
240 buffer.put(data, 0, data.length);
241 buffer.flip();
242 result.passwordType = buffer.getInt();
243 result.scryptN = buffer.get();
244 result.scryptR = buffer.get();
245 result.scryptP = buffer.get();
246 int saltLen = buffer.getInt();
247 result.salt = new byte[saltLen];
248 buffer.get(result.salt);
249 int handleLen = buffer.getInt();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100250 if (handleLen > 0) {
251 result.passwordHandle = new byte[handleLen];
252 buffer.get(result.passwordHandle);
253 } else {
254 result.passwordHandle = null;
255 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000256 return result;
257 }
258
259 public byte[] toBytes() {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100260
Rubin Xu3bf722a2016-12-15 16:07:38 +0000261 ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
Rubin Xu7b7424b2017-03-31 18:03:20 +0100262 + Integer.BYTES + salt.length + Integer.BYTES +
263 (passwordHandle != null ? passwordHandle.length : 0));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000264 buffer.putInt(passwordType);
265 buffer.put(scryptN);
266 buffer.put(scryptR);
267 buffer.put(scryptP);
268 buffer.putInt(salt.length);
269 buffer.put(salt);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100270 if (passwordHandle != null && passwordHandle.length > 0) {
271 buffer.putInt(passwordHandle.length);
272 buffer.put(passwordHandle);
273 } else {
274 buffer.putInt(0);
275 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000276 return buffer.array();
277 }
278 }
279
Rubin Xu7b7424b2017-03-31 18:03:20 +0100280 static class TokenData {
281 byte[] secdiscardableOnDisk;
282 byte[] weaverSecret;
283 byte[] aggregatedSecret;
284 }
285
Adrian Roos2adc2632017-09-05 17:01:42 +0200286 private final Context mContext;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000287 private LockSettingsStorage mStorage;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100288 private IWeaver mWeaver;
289 private WeaverConfig mWeaverConfig;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000290
Adrian Roos7374d3a2017-03-31 14:14:53 -0700291 private final UserManager mUserManager;
292
Adrian Roos2adc2632017-09-05 17:01:42 +0200293 public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
294 UserManager userManager) {
295 mContext = context;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000296 mStorage = storage;
Adrian Roos7374d3a2017-03-31 14:14:53 -0700297 mUserManager = userManager;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000298 }
299
Rubin Xu7b7424b2017-03-31 18:03:20 +0100300 @VisibleForTesting
301 protected IWeaver getWeaverService() throws RemoteException {
302 try {
303 return IWeaver.getService();
304 } catch (NoSuchElementException e) {
305 Slog.i(TAG, "Device does not support weaver");
306 return null;
307 }
308 }
309
310 public synchronized void initWeaverService() {
311 if (mWeaver != null) {
312 return;
313 }
314 try {
315 mWeaverConfig = null;
316 mWeaver = getWeaverService();
317 if (mWeaver != null) {
318 mWeaver.getConfig((int status, WeaverConfig config) -> {
319 if (status == WeaverStatus.OK && config.slots > 0) {
320 mWeaverConfig = config;
321 } else {
322 Slog.e(TAG, "Failed to get weaver config, status " + status
323 + " slots: " + config.slots);
324 mWeaver = null;
325 }
326 });
327 }
328 } catch (RemoteException e) {
329 Slog.e(TAG, "Failed to get weaver service", e);
330 }
331 }
332
333 private synchronized boolean isWeaverAvailable() {
334 if (mWeaver == null) {
335 //Re-initializing weaver in case there was a transient error preventing access to it.
336 initWeaverService();
337 }
338 return mWeaver != null && mWeaverConfig.slots > 0;
339 }
340
341 /**
342 * Enroll the given key value pair into the specified weaver slot. if the given key is null,
343 * a default all-zero key is used. If the value is not specified, a fresh random secret is
344 * generated as the value.
345 *
346 * @return the value stored in the weaver slot
347 * @throws RemoteException
348 */
349 private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
350 throws RemoteException {
351 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
352 throw new RuntimeException("Invalid slot for weaver");
353 }
354 if (key == null) {
355 key = new byte[mWeaverConfig.keySize];
356 } else if (key.length != mWeaverConfig.keySize) {
357 throw new RuntimeException("Invalid key size for weaver");
358 }
359 if (value == null) {
360 value = secureRandom(mWeaverConfig.valueSize);
361 }
362 int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
363 if (writeStatus != WeaverStatus.OK) {
364 Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
365 return null;
366 }
367 return value;
368 }
369
370 /**
371 * Verify the supplied key against a weaver slot, returning a response indicating whether
372 * the verification is successful, throttled or failed. If successful, the bound secret
373 * is also returned.
374 * @throws RemoteException
375 */
376 private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
377 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
378 throw new RuntimeException("Invalid slot for weaver");
379 }
380 if (key == null) {
381 key = new byte[mWeaverConfig.keySize];
382 } else if (key.length != mWeaverConfig.keySize) {
383 throw new RuntimeException("Invalid key size for weaver");
384 }
385 final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
386 mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
387 switch (status) {
388 case WeaverReadStatus.OK:
389 response[0] = new VerifyCredentialResponse(
390 fromByteArrayList(readResponse.value));
391 break;
392 case WeaverReadStatus.THROTTLE:
393 response[0] = new VerifyCredentialResponse(readResponse.timeout);
394 Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
395 break;
396 case WeaverReadStatus.INCORRECT_KEY:
397 if (readResponse.timeout == 0) {
398 response[0] = VerifyCredentialResponse.ERROR;
399 Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
400 } else {
401 response[0] = new VerifyCredentialResponse(readResponse.timeout);
402 Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
403 }
404 break;
405 case WeaverReadStatus.FAILED:
406 response[0] = VerifyCredentialResponse.ERROR;
407 Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
408 break;
409 default:
410 response[0] = VerifyCredentialResponse.ERROR;
411 Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
412 break;
413 }
414 });
415 return response[0];
416 }
417
418 public void removeUser(int userId) {
Rubin Xu2f22ee42017-09-15 11:56:53 +0100419 for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
420 destroyWeaverSlot(handle, userId);
421 destroySPBlobKey(getHandleName(handle));
Rubin Xu7b7424b2017-03-31 18:03:20 +0100422 }
423 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000424
425 public int getCredentialType(long handle, int userId) {
426 byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
427 if (passwordData == null) {
428 Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
429 return LockPatternUtils.CREDENTIAL_TYPE_NONE;
430 }
431 return PasswordData.fromBytes(passwordData).passwordType;
432 }
433
434 /**
435 * Initializing a new Authentication token, possibly from an existing credential and hash.
436 *
437 * The authentication token would bear a randomly-generated synthetic password.
438 *
439 * This method has the side effect of rebinding the SID of the given user to the
440 * newly-generated SP.
441 *
442 * If the existing credential hash is non-null, the existing SID mill be migrated so
443 * the synthetic password in the authentication token will produce the same SID
444 * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
Rubin Xu7b7424b2017-03-31 18:03:20 +0100445 * in a per-user data storage.)
Rubin Xu3bf722a2016-12-15 16:07:38 +0000446 *
447 * If the existing credential hash is null, it means the given user should have no SID so
448 * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
449 * the supplied credential parameter is also ignored.
450 *
451 * Also saves the escrow information necessary to re-generate the synthetic password under
452 * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
453 * password escrow should be disabled completely on the given user.
454 *
455 */
456 public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
Rich Canningsf64ec632019-02-21 12:40:36 -0800457 byte[] hash, byte[] credential, int userId) throws RemoteException {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000458 AuthenticationToken result = AuthenticationToken.create();
459 GateKeeperResponse response;
460 if (hash != null) {
Rich Canningsf64ec632019-02-21 12:40:36 -0800461 response = gatekeeper.enroll(userId, hash, credential,
Rubin Xu3bf722a2016-12-15 16:07:38 +0000462 result.deriveGkPassword());
463 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
464 Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
465 clearSidForUser(userId);
466 } else {
467 saveSyntheticPasswordHandle(response.getPayload(), userId);
468 }
469 } else {
470 clearSidForUser(userId);
471 }
472 saveEscrowData(result, userId);
473 return result;
474 }
475
476 /**
477 * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
478 * Used when adding password to previously-unsecured devices.
479 */
480 public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
481 int userId) throws RemoteException {
482 GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
483 authToken.deriveGkPassword());
484 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
485 Log.e(TAG, "Fail to create new SID for user " + userId);
486 return;
487 }
488 saveSyntheticPasswordHandle(response.getPayload(), userId);
489 }
490
491 // Nuke the SP handle (and as a result, its SID) for the given user.
492 public void clearSidForUser(int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +0100493 destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000494 }
495
496 public boolean hasSidForUser(int userId) {
497 return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
498 }
499
500 // if null, it means there is no SID associated with the user
501 // This can happen if the user is migrated to SP but currently
502 // do not have a lockscreen password.
503 private byte[] loadSyntheticPasswordHandle(int userId) {
504 return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
505 }
506
507 private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
508 saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
509 }
510
511 private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
512 authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
513 authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
514 return authToken.E0 != null && authToken.P1 != null;
515 }
516
517 private void saveEscrowData(AuthenticationToken authToken, int userId) {
518 saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId);
519 saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId);
520 }
521
Rubin Xuf095f832017-01-31 15:23:34 +0000522 public boolean hasEscrowData(int userId) {
523 return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
524 && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
525 }
526
Rubin Xu3bf722a2016-12-15 16:07:38 +0000527 public void destroyEscrowData(int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +0100528 destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
529 destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000530 }
531
Rubin Xu7b7424b2017-03-31 18:03:20 +0100532 private int loadWeaverSlot(long handle, int userId) {
533 final int LENGTH = Byte.BYTES + Integer.BYTES;
534 byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId);
535 if (data == null || data.length != LENGTH) {
536 return INVALID_WEAVER_SLOT;
537 }
538 ByteBuffer buffer = ByteBuffer.allocate(LENGTH);
539 buffer.put(data, 0, data.length);
540 buffer.flip();
541 if (buffer.get() != WEAVER_VERSION) {
542 Log.e(TAG, "Invalid weaver slot version of handle " + handle);
543 return INVALID_WEAVER_SLOT;
544 }
545 return buffer.getInt();
546 }
547
548 private void saveWeaverSlot(int slot, long handle, int userId) {
549 ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
550 buffer.put(WEAVER_VERSION);
551 buffer.putInt(slot);
552 saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
553 }
554
555 private void destroyWeaverSlot(long handle, int userId) {
556 int slot = loadWeaverSlot(handle, userId);
Rubin Xua339df02018-04-10 09:44:18 +0100557 destroyState(WEAVER_SLOT_NAME, handle, userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100558 if (slot != INVALID_WEAVER_SLOT) {
Rubin Xua339df02018-04-10 09:44:18 +0100559 Set<Integer> usedSlots = getUsedWeaverSlots();
560 if (!usedSlots.contains(slot)) {
561 Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
562 try {
563 weaverEnroll(slot, null, null);
564 } catch (RemoteException e) {
565 Log.w(TAG, "Failed to destroy slot", e);
566 }
567 } else {
568 Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100569 }
570 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100571 }
572
Rubin Xua339df02018-04-10 09:44:18 +0100573 /**
574 * Return the set of weaver slots that are currently in use by all users on the device.
575 * <p>
576 * <em>Note:</em> Users who are in the process of being deleted are not tracked here
577 * (due to them being marked as partial in UserManager so not visible from
578 * {@link UserManager#getUsers}). As a result their weaver slots will not be considered
579 * taken and can be reused by new users. Care should be taken when cleaning up the
580 * deleted user in {@link #removeUser}, to prevent a reused slot from being erased
581 * unintentionally.
582 */
583 private Set<Integer> getUsedWeaverSlots() {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100584 Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
585 WEAVER_SLOT_NAME);
586 HashSet<Integer> slots = new HashSet<>();
587 for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) {
588 for (Long handle : entry.getValue()) {
589 int slot = loadWeaverSlot(handle, entry.getKey());
590 slots.add(slot);
591 }
592 }
Rubin Xua339df02018-04-10 09:44:18 +0100593 return slots;
594 }
595
596 private int getNextAvailableWeaverSlot() {
597 Set<Integer> usedSlots = getUsedWeaverSlots();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100598 for (int i = 0; i < mWeaverConfig.slots; i++) {
Rubin Xua339df02018-04-10 09:44:18 +0100599 if (!usedSlots.contains(i)) {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100600 return i;
601 }
602 }
603 throw new RuntimeException("Run out of weaver slots.");
604 }
605
Rubin Xu3bf722a2016-12-15 16:07:38 +0000606 /**
607 * Create a new password based SP blob based on the supplied authentication token, such that
608 * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
609 * in the same authentication token.
610 *
611 * This method only creates SP blob wrapping around the given synthetic password and does not
612 * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
613 * is consistent with the device state by calling other APIs in this class.
614 *
615 * @see #newSidForUser
616 * @see #clearSidForUser
617 */
618 public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
Rich Canningsf64ec632019-02-21 12:40:36 -0800619 byte[] credential, int credentialType, AuthenticationToken authToken,
Adrian Roos7374d3a2017-03-31 14:14:53 -0700620 int requestedQuality, int userId)
Rubin Xu3bf722a2016-12-15 16:07:38 +0000621 throws RemoteException {
622 if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
623 credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
624 credential = DEFAULT_PASSWORD;
625 }
626
627 long handle = generateHandle();
628 PasswordData pwd = PasswordData.create(credentialType);
629 byte[] pwdToken = computePasswordToken(credential, pwd);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100630 final long sid;
631 final byte[] applicationId;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000632
Rubin Xu7b7424b2017-03-31 18:03:20 +0100633 if (isWeaverAvailable()) {
634 // Weaver based user password
635 int weaverSlot = getNextAvailableWeaverSlot();
Rubin Xua339df02018-04-10 09:44:18 +0100636 Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100637 byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
638 if (weaverSecret == null) {
639 Log.e(TAG, "Fail to enroll user password under weaver " + userId);
640 return DEFAULT_HANDLE;
641 }
642 saveWeaverSlot(weaverSlot, handle, userId);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700643 synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100644
645 pwd.passwordHandle = null;
646 sid = GateKeeper.INVALID_SECURE_USER_ID;
647 applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret);
648 } else {
Rubin Xu54c19b62017-04-26 19:40:15 +0100649 // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
650 // to prevent them from accumulating and causing problems.
651 gatekeeper.clearSecureUserId(fakeUid(userId));
Rubin Xu7b7424b2017-03-31 18:03:20 +0100652 // GateKeeper based user password
653 GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
654 passwordTokenToGkInput(pwdToken));
655 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
656 Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
657 return DEFAULT_HANDLE;
658 }
659 pwd.passwordHandle = response.getPayload();
660 sid = sidFromPasswordHandle(pwd.passwordHandle);
661 applicationId = transformUnderSecdiscardable(pwdToken,
662 createSecdiscardable(handle, userId));
Adrian Roos7374d3a2017-03-31 14:14:53 -0700663 synchronizeFrpPassword(pwd, requestedQuality, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000664 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000665 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
666
Rubin Xu3bf722a2016-12-15 16:07:38 +0000667 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
668 applicationId, sid, userId);
669 return handle;
670 }
671
Adrian Roos7374d3a2017-03-31 14:14:53 -0700672 public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
Rich Canningsf64ec632019-02-21 12:40:36 -0800673 byte[] userCredential, int credentialType,
Adrian Roos7374d3a2017-03-31 14:14:53 -0700674 ICheckCredentialProgressCallback progressCallback) throws RemoteException {
675 PersistentData persistentData = mStorage.readPersistentDataBlock();
676 if (persistentData.type == PersistentData.TYPE_SP) {
677 PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
678 byte[] pwdToken = computePasswordToken(userCredential, pwd);
679
Adrian Roosb7d69322017-08-08 18:32:03 +0200680 GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
681 0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
Adrian Roos7374d3a2017-03-31 14:14:53 -0700682 return VerifyCredentialResponse.fromGateKeeperResponse(response);
683 } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
684 PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
685 byte[] pwdToken = computePasswordToken(userCredential, pwd);
686 int weaverSlot = persistentData.userId;
687
688 return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
689 } else {
690 Log.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
691 + persistentData.type);
692 return VerifyCredentialResponse.ERROR;
693 }
694 }
695
696
Adrian Roos60dcbbf2017-08-08 16:19:33 +0200697 public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) {
698 if (mStorage.getPersistentDataBlock() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200699 && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) {
Adrian Roos60dcbbf2017-08-08 16:19:33 +0200700 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle,
701 userInfo.id));
702 if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
703 int weaverSlot = loadWeaverSlot(handle, userInfo.id);
704 if (weaverSlot != INVALID_WEAVER_SLOT) {
705 synchronizeWeaverFrpPassword(pwd, requestedQuality, userInfo.id, weaverSlot);
706 } else {
707 synchronizeFrpPassword(pwd, requestedQuality, userInfo.id);
708 }
709 }
710 }
711 }
712
Adrian Roos7374d3a2017-03-31 14:14:53 -0700713 private void synchronizeFrpPassword(PasswordData pwd,
714 int requestedQuality, int userId) {
715 if (mStorage.getPersistentDataBlock() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200716 && LockPatternUtils.userOwnsFrpCredential(mContext,
717 mUserManager.getUserInfo(userId))) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700718 if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
719 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
720 pwd.toBytes());
721 } else {
722 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
723 }
724 }
725 }
726
727 private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
728 int weaverSlot) {
729 if (mStorage.getPersistentDataBlock() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200730 && LockPatternUtils.userOwnsFrpCredential(mContext,
731 mUserManager.getUserInfo(userId))) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700732 if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
733 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
734 requestedQuality, pwd.toBytes());
735 } else {
736 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, 0, 0, null);
737 }
738 }
739 }
740
Rubin Xu7b7424b2017-03-31 18:03:20 +0100741 private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
Rubin Xuf095f832017-01-31 15:23:34 +0000742
743 public long createTokenBasedSyntheticPassword(byte[] token, int userId) {
744 long handle = generateHandle();
Rubin Xuf095f832017-01-31 15:23:34 +0000745 if (!tokenMap.containsKey(userId)) {
746 tokenMap.put(userId, new ArrayMap<>());
747 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100748 TokenData tokenData = new TokenData();
749 final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
750 if (isWeaverAvailable()) {
751 tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
752 tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
753 PERSONALISATION_WEAVER_TOKEN, secdiscardable);
754 } else {
755 tokenData.secdiscardableOnDisk = secdiscardable;
756 tokenData.weaverSecret = null;
757 }
758 tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
759
760 tokenMap.get(userId).put(handle, tokenData);
Rubin Xuf095f832017-01-31 15:23:34 +0000761 return handle;
762 }
763
764 public Set<Long> getPendingTokensForUser(int userId) {
765 if (!tokenMap.containsKey(userId)) {
766 return Collections.emptySet();
767 }
768 return tokenMap.get(userId).keySet();
769 }
770
771 public boolean removePendingToken(long handle, int userId) {
772 if (!tokenMap.containsKey(userId)) {
773 return false;
774 }
775 return tokenMap.get(userId).remove(handle) != null;
776 }
777
778 public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
779 int userId) {
780 if (!tokenMap.containsKey(userId)) {
781 return false;
782 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100783 TokenData tokenData = tokenMap.get(userId).get(handle);
784 if (tokenData == null) {
Rubin Xuf095f832017-01-31 15:23:34 +0000785 return false;
786 }
787 if (!loadEscrowData(authToken, userId)) {
788 Log.w(TAG, "User is not escrowable");
789 return false;
790 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100791 if (isWeaverAvailable()) {
792 int slot = getNextAvailableWeaverSlot();
793 try {
Rubin Xua339df02018-04-10 09:44:18 +0100794 Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100795 weaverEnroll(slot, null, tokenData.weaverSecret);
796 } catch (RemoteException e) {
797 Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
798 return false;
799 }
800 saveWeaverSlot(slot, handle, userId);
801 }
802 saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000803 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
Rubin Xu7b7424b2017-03-31 18:03:20 +0100804 tokenData.aggregatedSecret, 0L, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000805 tokenMap.get(userId).remove(handle);
806 return true;
807 }
808
Rubin Xu3bf722a2016-12-15 16:07:38 +0000809 private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
810 byte[] applicationId, long sid, int userId) {
Rubin Xuf095f832017-01-31 15:23:34 +0000811 final byte[] secret;
812 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
813 secret = authToken.computeP0();
814 } else {
815 secret = authToken.syntheticPassword.getBytes();
816 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000817 byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
818 byte[] blob = new byte[content.length + 1 + 1];
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800819 /*
820 * We can upgrade from v1 to v2 because that's just a change in the way that
821 * the SP is stored. However, we can't upgrade to v3 because that is a change
822 * in the way that passwords are derived from the SP.
823 */
824 if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
825 blob[0] = SYNTHETIC_PASSWORD_VERSION_V3;
826 } else {
827 blob[0] = SYNTHETIC_PASSWORD_VERSION_V2;
828 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000829 blob[1] = type;
830 System.arraycopy(content, 0, blob, 2, content.length);
831 saveState(SP_BLOB_NAME, blob, handle, userId);
832 }
833
834 /**
835 * Decrypt a synthetic password by supplying the user credential and corresponding password
836 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
837 * verification to referesh the SID & Auth token maintained by the system.
Rubin Xu16c823e2017-06-27 14:44:58 +0100838 * Note: the credential type is not validated here since there are call sites where the type is
839 * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
Rubin Xu3bf722a2016-12-15 16:07:38 +0000840 */
841 public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
Rich Canningsf64ec632019-02-21 12:40:36 -0800842 long handle, byte[] credential, int userId,
Rubin Xucf326f12017-11-15 11:55:35 +0000843 ICheckCredentialProgressCallback progressCallback) throws RemoteException {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000844 if (credential == null) {
845 credential = DEFAULT_PASSWORD;
846 }
847 AuthenticationResult result = new AuthenticationResult();
848 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
Rubin Xu16c823e2017-06-27 14:44:58 +0100849 result.credentialType = pwd.passwordType;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000850 byte[] pwdToken = computePasswordToken(credential, pwd);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000851
Rubin Xu7b7424b2017-03-31 18:03:20 +0100852 final byte[] applicationId;
Rubin Xu8c528652017-10-31 15:40:32 +0000853 final long sid;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100854 int weaverSlot = loadWeaverSlot(handle, userId);
855 if (weaverSlot != INVALID_WEAVER_SLOT) {
856 // Weaver based user password
857 if (!isWeaverAvailable()) {
858 Log.e(TAG, "No weaver service to unwrap password based SP");
859 result.gkResponse = VerifyCredentialResponse.ERROR;
860 return result;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000861 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100862 result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
863 if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
864 return result;
865 }
Rubin Xu8c528652017-10-31 15:40:32 +0000866 sid = GateKeeper.INVALID_SECURE_USER_ID;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100867 applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
868 } else {
869 byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
870 GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
871 pwd.passwordHandle, gkPwdToken);
872 int responseCode = response.getResponseCode();
873 if (responseCode == GateKeeperResponse.RESPONSE_OK) {
874 result.gkResponse = VerifyCredentialResponse.OK;
875 if (response.getShouldReEnroll()) {
876 GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
877 pwd.passwordHandle, gkPwdToken, gkPwdToken);
878 if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
879 pwd.passwordHandle = reenrollResponse.getPayload();
880 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700881 synchronizeFrpPassword(pwd,
882 pwd.passwordType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
883 ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
884 : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
885 /* TODO(roosa): keep the same password quality */,
886 userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100887 } else {
888 Log.w(TAG, "Fail to re-enroll user password for user " + userId);
889 // continue the flow anyway
890 }
891 }
892 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
893 result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
894 return result;
895 } else {
896 result.gkResponse = VerifyCredentialResponse.ERROR;
897 return result;
898 }
Rubin Xu8c528652017-10-31 15:40:32 +0000899 sid = sidFromPasswordHandle(pwd.passwordHandle);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100900 applicationId = transformUnderSecdiscardable(pwdToken,
901 loadSecdiscardable(handle, userId));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000902 }
Rubin Xucf326f12017-11-15 11:55:35 +0000903 // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
904 // Notify the callback so the keyguard UI can proceed immediately.
905 if (progressCallback != null) {
906 progressCallback.onCredentialVerified();
907 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000908 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
Rubin Xu8c528652017-10-31 15:40:32 +0000909 applicationId, sid, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000910
911 // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
912 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
913 return result;
914 }
915
Rubin Xuf095f832017-01-31 15:23:34 +0000916 /**
917 * Decrypt a synthetic password by supplying an escrow token and corresponding token
918 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
919 * verification to referesh the SID & Auth token maintained by the system.
920 */
921 public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
922 IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
923 throws RemoteException {
924 AuthenticationResult result = new AuthenticationResult();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100925 byte[] secdiscardable = loadSecdiscardable(handle, userId);
926 int slotId = loadWeaverSlot(handle, userId);
927 if (slotId != INVALID_WEAVER_SLOT) {
928 if (!isWeaverAvailable()) {
929 Log.e(TAG, "No weaver service to unwrap token based SP");
930 result.gkResponse = VerifyCredentialResponse.ERROR;
931 return result;
932 }
933 VerifyCredentialResponse response = weaverVerify(slotId, null);
934 if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
935 response.getPayload() == null) {
936 Log.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
937 result.gkResponse = VerifyCredentialResponse.ERROR;
938 return result;
939 }
940 secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
941 PERSONALISATION_WEAVER_TOKEN, secdiscardable);
942 }
943 byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
Rubin Xuf095f832017-01-31 15:23:34 +0000944 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
Rubin Xu8c528652017-10-31 15:40:32 +0000945 applicationId, 0L, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000946 if (result.authToken != null) {
947 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
948 if (result.gkResponse == null) {
949 // The user currently has no password. return OK with null payload so null
950 // is propagated to unlockUser()
951 result.gkResponse = VerifyCredentialResponse.OK;
952 }
953 } else {
954 result.gkResponse = VerifyCredentialResponse.ERROR;
955 }
956 return result;
957 }
958
Rubin Xu3bf722a2016-12-15 16:07:38 +0000959 private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
Rubin Xu8c528652017-10-31 15:40:32 +0000960 byte[] applicationId, long sid, int userId) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000961 byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
962 if (blob == null) {
963 return null;
964 }
Rubin Xu8c528652017-10-31 15:40:32 +0000965 final byte version = blob[0];
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800966 if (version != SYNTHETIC_PASSWORD_VERSION_V3
967 && version != SYNTHETIC_PASSWORD_VERSION_V2
968 && version != SYNTHETIC_PASSWORD_VERSION_V1) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000969 throw new RuntimeException("Unknown blob version");
970 }
971 if (blob[1] != type) {
972 throw new RuntimeException("Invalid blob type");
973 }
Rubin Xu8c528652017-10-31 15:40:32 +0000974 final byte[] secret;
975 if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
976 secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
977 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
978 } else {
979 secret = decryptSPBlob(getHandleName(handle),
Rubin Xu3bf722a2016-12-15 16:07:38 +0000980 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
Rubin Xu8c528652017-10-31 15:40:32 +0000981 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000982 if (secret == null) {
983 Log.e(TAG, "Fail to decrypt SP for user " + userId);
984 return null;
985 }
Paul Crowley0d40d6e72018-11-28 12:48:16 -0800986 AuthenticationToken result = new AuthenticationToken(version);
Rubin Xuf095f832017-01-31 15:23:34 +0000987 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
988 if (!loadEscrowData(result, userId)) {
989 Log.e(TAG, "User is not escrowable: " + userId);
990 return null;
991 }
992 result.recreate(secret);
993 } else {
994 result.syntheticPassword = new String(secret);
995 }
Rubin Xu8c528652017-10-31 15:40:32 +0000996 if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
997 Log.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
998 createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
999 }
Rubin Xu3bf722a2016-12-15 16:07:38 +00001000 return result;
1001 }
1002
1003 /**
1004 * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
1005 * if required.
1006 *
1007 * Normally performing verifyChallenge with an AuthenticationToken should always return
1008 * RESPONSE_OK, since user authentication failures are detected earlier when trying to
1009 * decrypt SP.
1010 */
Rubin Xu8b30ec32017-03-05 00:47:09 +00001011 public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
Rubin Xu3bf722a2016-12-15 16:07:38 +00001012 @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
1013 byte[] spHandle = loadSyntheticPasswordHandle(userId);
1014 if (spHandle == null) {
1015 // There is no password handle associated with the given user, i.e. the user is not
1016 // secured by lockscreen and has no SID, so just return here;
1017 return null;
1018 }
1019 VerifyCredentialResponse result;
1020 GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
1021 spHandle, auth.deriveGkPassword());
1022 int responseCode = response.getResponseCode();
1023 if (responseCode == GateKeeperResponse.RESPONSE_OK) {
1024 result = new VerifyCredentialResponse(response.getPayload());
1025 if (response.getShouldReEnroll()) {
1026 response = gatekeeper.enroll(userId, spHandle,
1027 spHandle, auth.deriveGkPassword());
1028 if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
1029 spHandle = response.getPayload();
1030 saveSyntheticPasswordHandle(spHandle, userId);
1031 // Call self again to re-verify with updated handle
1032 return verifyChallenge(gatekeeper, auth, challenge, userId);
1033 } else {
1034 Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
1035 // Fall through, return existing handle
1036 }
1037 }
1038 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
1039 result = new VerifyCredentialResponse(response.getTimeout());
1040 } else {
1041 result = VerifyCredentialResponse.ERROR;
1042 }
1043 return result;
1044 }
1045
1046 public boolean existsHandle(long handle, int userId) {
1047 return hasState(SP_BLOB_NAME, handle, userId);
1048 }
1049
Rubin Xuf095f832017-01-31 15:23:34 +00001050 public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
1051 destroySyntheticPassword(handle, userId);
Rubin Xuaa32d152017-04-27 17:01:05 +01001052 destroyState(SECDISCARDABLE_NAME, handle, userId);
Rubin Xuf095f832017-01-31 15:23:34 +00001053 }
1054
Rubin Xu3bf722a2016-12-15 16:07:38 +00001055 public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
1056 destroySyntheticPassword(handle, userId);
Rubin Xuaa32d152017-04-27 17:01:05 +01001057 destroyState(SECDISCARDABLE_NAME, handle, userId);
1058 destroyState(PASSWORD_DATA_NAME, handle, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001059 }
1060
1061 private void destroySyntheticPassword(long handle, int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +01001062 destroyState(SP_BLOB_NAME, handle, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001063 destroySPBlobKey(getHandleName(handle));
Rubin Xu7b7424b2017-03-31 18:03:20 +01001064 if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
1065 destroyWeaverSlot(handle, userId);
1066 }
1067 }
1068
1069 private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
1070 byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash(
1071 PERSONALISATION_WEAVER_PASSWORD, secret);
1072 byte[] result = new byte[data.length + weaverSecret.length];
1073 System.arraycopy(data, 0, result, 0, data.length);
1074 System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length);
1075 return result;
Rubin Xu3bf722a2016-12-15 16:07:38 +00001076 }
1077
1078 private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
1079 byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
1080 PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
1081 byte[] result = new byte[data.length + secdiscardable.length];
1082 System.arraycopy(data, 0, result, 0, data.length);
1083 System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
1084 return result;
1085 }
1086
1087 private byte[] createSecdiscardable(long handle, int userId) {
1088 byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
Rubin Xu7b7424b2017-03-31 18:03:20 +01001089 saveSecdiscardable(handle, data, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001090 return data;
1091 }
1092
Rubin Xu7b7424b2017-03-31 18:03:20 +01001093 private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
1094 saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
1095 }
1096
Rubin Xu3bf722a2016-12-15 16:07:38 +00001097 private byte[] loadSecdiscardable(long handle, int userId) {
1098 return loadState(SECDISCARDABLE_NAME, handle, userId);
1099 }
1100
1101 private boolean hasState(String stateName, long handle, int userId) {
1102 return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
1103 }
1104
1105 private byte[] loadState(String stateName, long handle, int userId) {
1106 return mStorage.readSyntheticPasswordState(userId, handle, stateName);
1107 }
1108
1109 private void saveState(String stateName, byte[] data, long handle, int userId) {
1110 mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
1111 }
1112
Rubin Xuaa32d152017-04-27 17:01:05 +01001113 private void destroyState(String stateName, long handle, int userId) {
1114 mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001115 }
1116
1117 protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
1118 return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
1119 }
1120
1121 protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
1122 return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
1123 }
1124
1125 protected void destroySPBlobKey(String keyAlias) {
1126 SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
1127 }
1128
1129 public static long generateHandle() {
1130 SecureRandom rng = new SecureRandom();
1131 long result;
1132 do {
1133 result = rng.nextLong();
1134 } while (result == DEFAULT_HANDLE);
1135 return result;
1136 }
1137
1138 private int fakeUid(int uid) {
1139 return 100000 + uid;
1140 }
1141
1142 protected static byte[] secureRandom(int length) {
1143 try {
1144 return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
1145 } catch (NoSuchAlgorithmException e) {
1146 e.printStackTrace();
1147 return null;
1148 }
1149 }
1150
1151 private String getHandleName(long handle) {
1152 return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
1153 }
1154
Rich Canningsf64ec632019-02-21 12:40:36 -08001155 private byte[] computePasswordToken(byte[] password, PasswordData data) {
Rubin Xu3bf722a2016-12-15 16:07:38 +00001156 return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
1157 PASSWORD_TOKEN_LENGTH);
1158 }
1159
1160 private byte[] passwordTokenToGkInput(byte[] token) {
1161 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
1162 }
1163
Rubin Xu7b7424b2017-03-31 18:03:20 +01001164 private byte[] passwordTokenToWeaverKey(byte[] token) {
1165 byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
1166 if (key.length < mWeaverConfig.keySize) {
1167 throw new RuntimeException("weaver key length too small");
1168 }
1169 return Arrays.copyOf(key, mWeaverConfig.keySize);
1170 }
1171
Rubin Xu3bf722a2016-12-15 16:07:38 +00001172 protected long sidFromPasswordHandle(byte[] handle) {
1173 return nativeSidFromPasswordHandle(handle);
1174 }
1175
Rich Canningsf64ec632019-02-21 12:40:36 -08001176 protected byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) {
1177 return new Scrypt().scrypt(password, salt, n, r, p, outLen);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001178 }
1179
1180 native long nativeSidFromPasswordHandle(byte[] handle);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001181
Rubin Xu7b7424b2017-03-31 18:03:20 +01001182 protected static ArrayList<Byte> toByteArrayList(byte[] data) {
1183 ArrayList<Byte> result = new ArrayList<Byte>(data.length);
1184 for (int i = 0; i < data.length; i++) {
1185 result.add(data[i]);
1186 }
1187 return result;
1188 }
1189
1190 protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
1191 byte[] result = new byte[data.size()];
1192 for (int i = 0; i < data.size(); i++) {
1193 result[i] = data.get(i);
1194 }
1195 return result;
1196 }
1197
Rich Canningsf64ec632019-02-21 12:40:36 -08001198 protected static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes();
1199 private static byte[] bytesToHex(byte[] bytes) {
Rubin Xu3bf722a2016-12-15 16:07:38 +00001200 if (bytes == null) {
Rich Canningsf64ec632019-02-21 12:40:36 -08001201 return "null".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +00001202 }
Rich Canningsf64ec632019-02-21 12:40:36 -08001203 byte[] hexBytes = new byte[bytes.length * 2];
Rubin Xu3bf722a2016-12-15 16:07:38 +00001204 for ( int j = 0; j < bytes.length; j++ ) {
1205 int v = bytes[j] & 0xFF;
Rich Canningsf64ec632019-02-21 12:40:36 -08001206 hexBytes[j * 2] = HEX_ARRAY[v >>> 4];
1207 hexBytes[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
Rubin Xu3bf722a2016-12-15 16:07:38 +00001208 }
Rich Canningsf64ec632019-02-21 12:40:36 -08001209 return hexBytes;
Rubin Xu3bf722a2016-12-15 16:07:38 +00001210 }
1211}