blob: d32c299074a9613be0f55285299569d6cd796bc7 [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 Crowley63ef1512018-11-28 12:48:16 -080031import android.security.GateKeeper;
Rubin Xu3bf722a2016-12-15 16:07:38 +000032import android.service.gatekeeper.GateKeeperResponse;
33import android.service.gatekeeper.IGateKeeperService;
34import android.util.ArrayMap;
35import android.util.Log;
Rubin Xu7b7424b2017-03-31 18:03:20 +010036import android.util.Slog;
Rubin Xu3bf722a2016-12-15 16:07:38 +000037
Rubin Xu7b7424b2017-03-31 18:03:20 +010038import com.android.internal.annotations.VisibleForTesting;
Rubin Xu3bf722a2016-12-15 16:07:38 +000039import com.android.internal.util.ArrayUtils;
Adrian Roos7374d3a2017-03-31 14:14:53 -070040import com.android.internal.widget.ICheckCredentialProgressCallback;
Rubin Xu3bf722a2016-12-15 16:07:38 +000041import com.android.internal.widget.LockPatternUtils;
42import com.android.internal.widget.VerifyCredentialResponse;
Adrian Roos7374d3a2017-03-31 14:14:53 -070043import com.android.server.locksettings.LockSettingsStorage.PersistentData;
Rubin Xu3bf722a2016-12-15 16:07:38 +000044
45import libcore.util.HexEncoding;
46
47import java.nio.ByteBuffer;
Rubin Xu3bf722a2016-12-15 16:07:38 +000048import java.security.NoSuchAlgorithmException;
49import java.security.SecureRandom;
Rubin Xu7b7424b2017-03-31 18:03:20 +010050import java.util.ArrayList;
Rubin Xu3bf722a2016-12-15 16:07:38 +000051import java.util.Arrays;
52import java.util.Collections;
Rubin Xu7b7424b2017-03-31 18:03:20 +010053import java.util.HashSet;
54import java.util.List;
55import java.util.Map;
56import java.util.NoSuchElementException;
Rubin Xu3bf722a2016-12-15 16:07:38 +000057import java.util.Set;
58
59
60/**
61 * A class that maintains the wrapping of synthetic password by user credentials or escrow tokens.
62 * It's (mostly) a pure storage for synthetic passwords, providing APIs to creating and destroying
63 * synthetic password blobs which are wrapped by user credentials or escrow tokens.
64 *
65 * Here is the assumptions it makes:
66 * Each user has one single synthetic password at any time.
67 * The SP has an associated password handle, which binds to the SID for that user. The password
68 * handle is persisted by SyntheticPasswordManager internally.
69 * If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD
Rubin Xu7b7424b2017-03-31 18:03:20 +010070 *
71 * Information persisted on disk:
72 * for each user (stored under DEFAULT_HANDLE):
73 * SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user
74 * credential exists, cleared when user clears their credential.
Rubin Xu128180b2017-04-12 18:02:44 +010075 * SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combined with escrow
Rubin Xu7b7424b2017-03-31 18:03:20 +010076 * tokens. Destroyed when escrow support is turned off for the given user.
77 *
78 * for each SP blob under the user (stored under the corresponding handle):
79 * SP_BLOB_NAME: The encrypted synthetic password. Always exists.
80 * PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP.
Rubin Xu128180b2017-04-12 18:02:44 +010081 * SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME for the
82 * purpose of secure deletion. Exists if this is a non-weaver SP
Rubin Xu7b7424b2017-03-31 18:03:20 +010083 * (both password and token based), or it's a token-based SP under weaver.
84 * WEAVER_SLOT: Metadata about the weaver slot used. Only exists if this is a SP under weaver.
85 *
86 *
Rubin Xu3bf722a2016-12-15 16:07:38 +000087 */
88public class SyntheticPasswordManager {
89 private static final String SP_BLOB_NAME = "spblob";
90 private static final String SP_E0_NAME = "e0";
91 private static final String SP_P1_NAME = "p1";
92 private static final String SP_HANDLE_NAME = "handle";
93 private static final String SECDISCARDABLE_NAME = "secdis";
94 private static final int SECDISCARDABLE_LENGTH = 16 * 1024;
95 private static final String PASSWORD_DATA_NAME = "pwd";
Rubin Xu7b7424b2017-03-31 18:03:20 +010096 private static final String WEAVER_SLOT_NAME = "weaver";
Rubin Xu3bf722a2016-12-15 16:07:38 +000097
Rubin Xu7b7424b2017-03-31 18:03:20 +010098 public static final long DEFAULT_HANDLE = 0L;
Rubin Xu3bf722a2016-12-15 16:07:38 +000099 private static final String DEFAULT_PASSWORD = "default-password";
100
Rubin Xu7b7424b2017-03-31 18:03:20 +0100101 private static final byte WEAVER_VERSION = 1;
102 private static final int INVALID_WEAVER_SLOT = -1;
103
Rubin Xu8c528652017-10-31 15:40:32 +0000104 private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1;
Paul Crowley63ef1512018-11-28 12:48:16 -0800105 private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2;
106 private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000107 private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
Rubin Xuf095f832017-01-31 15:23:34 +0000108 private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000109
110 // 256-bit synthetic password
111 private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
112
Rubin Xu157bddd2017-04-24 15:51:59 +0100113 private static final int PASSWORD_SCRYPT_N = 11;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000114 private static final int PASSWORD_SCRYPT_R = 3;
115 private static final int PASSWORD_SCRYPT_P = 1;
116 private static final int PASSWORD_SALT_LENGTH = 16;
117 private static final int PASSWORD_TOKEN_LENGTH = 32;
118 private static final String TAG = "SyntheticPasswordManager";
119
120 private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
121 private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes();
122 private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();
123 private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
124 private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
Andrew Sculle6527c12018-01-05 18:33:58 +0000125 private static final byte[] PERSONALIZATION_AUTHSECRET_KEY = "authsecret-hal".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000126 private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
Rubin Xuf01e9072018-03-30 20:59:28 +0100127 private static final byte[] PERSONALIZATION_PASSWORD_HASH = "pw-hash".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000128 private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100129 private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
130 private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
131 private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
Paul Crowley63ef1512018-11-28 12:48:16 -0800132 private static final byte[] PERSONALISATION_CONTEXT =
133 "android-synthetic-password-personalization-context".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000134
135 static class AuthenticationResult {
136 public AuthenticationToken authToken;
137 public VerifyCredentialResponse gkResponse;
Rubin Xu16c823e2017-06-27 14:44:58 +0100138 public int credentialType;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000139 }
140
141 static class AuthenticationToken {
Paul Crowley63ef1512018-11-28 12:48:16 -0800142 private final byte mVersion;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000143 /*
144 * Here is the relationship between all three fields:
145 * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
146 * syntheticPassword = hash(P0 || P1)
147 * E0 = P0 encrypted under syntheticPassword, stored on disk.
148 */
149 private @Nullable byte[] E0;
150 private @Nullable byte[] P1;
151 private @NonNull String syntheticPassword;
152
Paul Crowley63ef1512018-11-28 12:48:16 -0800153 AuthenticationToken(byte version) {
154 mVersion = version;
155 }
156
157 private byte[] derivePassword(byte[] personalization) {
158 if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
159 return (new SP800Derive(syntheticPassword.getBytes()))
160 .withContext(personalization, PERSONALISATION_CONTEXT);
161 } else {
162 return SyntheticPasswordCrypto.personalisedHash(personalization,
163 syntheticPassword.getBytes());
164 }
165 }
166
Rubin Xu3bf722a2016-12-15 16:07:38 +0000167 public String deriveKeyStorePassword() {
Paul Crowley63ef1512018-11-28 12:48:16 -0800168 return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000169 }
170
171 public byte[] deriveGkPassword() {
Paul Crowley63ef1512018-11-28 12:48:16 -0800172 return derivePassword(PERSONALIZATION_SP_GK_AUTH);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000173 }
174
175 public byte[] deriveDiskEncryptionKey() {
Paul Crowley63ef1512018-11-28 12:48:16 -0800176 return derivePassword(PERSONALIZATION_FBE_KEY);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000177 }
178
Andrew Sculle6527c12018-01-05 18:33:58 +0000179 public byte[] deriveVendorAuthSecret() {
Paul Crowley63ef1512018-11-28 12:48:16 -0800180 return derivePassword(PERSONALIZATION_AUTHSECRET_KEY);
Andrew Sculle6527c12018-01-05 18:33:58 +0000181 }
182
Rubin Xuf01e9072018-03-30 20:59:28 +0100183 public byte[] derivePasswordHashFactor() {
Paul Crowley63ef1512018-11-28 12:48:16 -0800184 return derivePassword(PERSONALIZATION_PASSWORD_HASH);
Rubin Xuf01e9072018-03-30 20:59:28 +0100185 }
186
Rubin Xuf095f832017-01-31 15:23:34 +0000187 private void initialize(byte[] P0, byte[] P1) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000188 this.P1 = P1;
189 this.syntheticPassword = String.valueOf(HexEncoding.encode(
190 SyntheticPasswordCrypto.personalisedHash(
191 PERSONALIZATION_SP_SPLIT, P0, P1)));
192 this.E0 = SyntheticPasswordCrypto.encrypt(this.syntheticPassword.getBytes(),
193 PERSONALIZATION_E0, P0);
194 }
195
Rubin Xuf095f832017-01-31 15:23:34 +0000196 public void recreate(byte[] secret) {
197 initialize(secret, this.P1);
198 }
199
Rubin Xu3bf722a2016-12-15 16:07:38 +0000200 protected static AuthenticationToken create() {
Paul Crowley63ef1512018-11-28 12:48:16 -0800201 AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000202 result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
203 secureRandom(SYNTHETIC_PASSWORD_LENGTH));
204 return result;
205 }
206
207 public byte[] computeP0() {
208 if (E0 == null) {
209 return null;
210 }
211 return SyntheticPasswordCrypto.decrypt(syntheticPassword.getBytes(), PERSONALIZATION_E0,
212 E0);
213 }
214 }
215
216 static class PasswordData {
217 byte scryptN;
218 byte scryptR;
219 byte scryptP;
220 public int passwordType;
221 byte[] salt;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100222 // For GateKeeper-based credential, this is the password handle returned by GK,
223 // for weaver-based credential, this is empty.
Rubin Xu3bf722a2016-12-15 16:07:38 +0000224 public byte[] passwordHandle;
225
226 public static PasswordData create(int passwordType) {
227 PasswordData result = new PasswordData();
228 result.scryptN = PASSWORD_SCRYPT_N;
229 result.scryptR = PASSWORD_SCRYPT_R;
230 result.scryptP = PASSWORD_SCRYPT_P;
231 result.passwordType = passwordType;
232 result.salt = secureRandom(PASSWORD_SALT_LENGTH);
233 return result;
234 }
235
236 public static PasswordData fromBytes(byte[] data) {
237 PasswordData result = new PasswordData();
238 ByteBuffer buffer = ByteBuffer.allocate(data.length);
239 buffer.put(data, 0, data.length);
240 buffer.flip();
241 result.passwordType = buffer.getInt();
242 result.scryptN = buffer.get();
243 result.scryptR = buffer.get();
244 result.scryptP = buffer.get();
245 int saltLen = buffer.getInt();
246 result.salt = new byte[saltLen];
247 buffer.get(result.salt);
248 int handleLen = buffer.getInt();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100249 if (handleLen > 0) {
250 result.passwordHandle = new byte[handleLen];
251 buffer.get(result.passwordHandle);
252 } else {
253 result.passwordHandle = null;
254 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000255 return result;
256 }
257
258 public byte[] toBytes() {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100259
Rubin Xu3bf722a2016-12-15 16:07:38 +0000260 ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
Rubin Xu7b7424b2017-03-31 18:03:20 +0100261 + Integer.BYTES + salt.length + Integer.BYTES +
262 (passwordHandle != null ? passwordHandle.length : 0));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000263 buffer.putInt(passwordType);
264 buffer.put(scryptN);
265 buffer.put(scryptR);
266 buffer.put(scryptP);
267 buffer.putInt(salt.length);
268 buffer.put(salt);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100269 if (passwordHandle != null && passwordHandle.length > 0) {
270 buffer.putInt(passwordHandle.length);
271 buffer.put(passwordHandle);
272 } else {
273 buffer.putInt(0);
274 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000275 return buffer.array();
276 }
277 }
278
Rubin Xu7b7424b2017-03-31 18:03:20 +0100279 static class TokenData {
280 byte[] secdiscardableOnDisk;
281 byte[] weaverSecret;
282 byte[] aggregatedSecret;
283 }
284
Adrian Roos2adc2632017-09-05 17:01:42 +0200285 private final Context mContext;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000286 private LockSettingsStorage mStorage;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100287 private IWeaver mWeaver;
288 private WeaverConfig mWeaverConfig;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000289
Adrian Roos7374d3a2017-03-31 14:14:53 -0700290 private final UserManager mUserManager;
291
Adrian Roos2adc2632017-09-05 17:01:42 +0200292 public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
293 UserManager userManager) {
294 mContext = context;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000295 mStorage = storage;
Adrian Roos7374d3a2017-03-31 14:14:53 -0700296 mUserManager = userManager;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000297 }
298
Rubin Xu7b7424b2017-03-31 18:03:20 +0100299 @VisibleForTesting
300 protected IWeaver getWeaverService() throws RemoteException {
301 try {
302 return IWeaver.getService();
303 } catch (NoSuchElementException e) {
304 Slog.i(TAG, "Device does not support weaver");
305 return null;
306 }
307 }
308
309 public synchronized void initWeaverService() {
310 if (mWeaver != null) {
311 return;
312 }
313 try {
314 mWeaverConfig = null;
315 mWeaver = getWeaverService();
316 if (mWeaver != null) {
317 mWeaver.getConfig((int status, WeaverConfig config) -> {
318 if (status == WeaverStatus.OK && config.slots > 0) {
319 mWeaverConfig = config;
320 } else {
321 Slog.e(TAG, "Failed to get weaver config, status " + status
322 + " slots: " + config.slots);
323 mWeaver = null;
324 }
325 });
326 }
327 } catch (RemoteException e) {
328 Slog.e(TAG, "Failed to get weaver service", e);
329 }
330 }
331
332 private synchronized boolean isWeaverAvailable() {
333 if (mWeaver == null) {
334 //Re-initializing weaver in case there was a transient error preventing access to it.
335 initWeaverService();
336 }
337 return mWeaver != null && mWeaverConfig.slots > 0;
338 }
339
340 /**
341 * Enroll the given key value pair into the specified weaver slot. if the given key is null,
342 * a default all-zero key is used. If the value is not specified, a fresh random secret is
343 * generated as the value.
344 *
345 * @return the value stored in the weaver slot
346 * @throws RemoteException
347 */
348 private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
349 throws RemoteException {
350 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
351 throw new RuntimeException("Invalid slot for weaver");
352 }
353 if (key == null) {
354 key = new byte[mWeaverConfig.keySize];
355 } else if (key.length != mWeaverConfig.keySize) {
356 throw new RuntimeException("Invalid key size for weaver");
357 }
358 if (value == null) {
359 value = secureRandom(mWeaverConfig.valueSize);
360 }
361 int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
362 if (writeStatus != WeaverStatus.OK) {
363 Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
364 return null;
365 }
366 return value;
367 }
368
369 /**
370 * Verify the supplied key against a weaver slot, returning a response indicating whether
371 * the verification is successful, throttled or failed. If successful, the bound secret
372 * is also returned.
373 * @throws RemoteException
374 */
375 private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
376 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
377 throw new RuntimeException("Invalid slot for weaver");
378 }
379 if (key == null) {
380 key = new byte[mWeaverConfig.keySize];
381 } else if (key.length != mWeaverConfig.keySize) {
382 throw new RuntimeException("Invalid key size for weaver");
383 }
384 final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
385 mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
386 switch (status) {
387 case WeaverReadStatus.OK:
388 response[0] = new VerifyCredentialResponse(
389 fromByteArrayList(readResponse.value));
390 break;
391 case WeaverReadStatus.THROTTLE:
392 response[0] = new VerifyCredentialResponse(readResponse.timeout);
393 Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
394 break;
395 case WeaverReadStatus.INCORRECT_KEY:
396 if (readResponse.timeout == 0) {
397 response[0] = VerifyCredentialResponse.ERROR;
398 Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
399 } else {
400 response[0] = new VerifyCredentialResponse(readResponse.timeout);
401 Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
402 }
403 break;
404 case WeaverReadStatus.FAILED:
405 response[0] = VerifyCredentialResponse.ERROR;
406 Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
407 break;
408 default:
409 response[0] = VerifyCredentialResponse.ERROR;
410 Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
411 break;
412 }
413 });
414 return response[0];
415 }
416
417 public void removeUser(int userId) {
Rubin Xu2f22ee42017-09-15 11:56:53 +0100418 for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
419 destroyWeaverSlot(handle, userId);
420 destroySPBlobKey(getHandleName(handle));
Rubin Xu7b7424b2017-03-31 18:03:20 +0100421 }
422 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000423
424 public int getCredentialType(long handle, int userId) {
425 byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
426 if (passwordData == null) {
427 Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
428 return LockPatternUtils.CREDENTIAL_TYPE_NONE;
429 }
430 return PasswordData.fromBytes(passwordData).passwordType;
431 }
432
433 /**
434 * Initializing a new Authentication token, possibly from an existing credential and hash.
435 *
436 * The authentication token would bear a randomly-generated synthetic password.
437 *
438 * This method has the side effect of rebinding the SID of the given user to the
439 * newly-generated SP.
440 *
441 * If the existing credential hash is non-null, the existing SID mill be migrated so
442 * the synthetic password in the authentication token will produce the same SID
443 * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
Rubin Xu7b7424b2017-03-31 18:03:20 +0100444 * in a per-user data storage.)
Rubin Xu3bf722a2016-12-15 16:07:38 +0000445 *
446 * If the existing credential hash is null, it means the given user should have no SID so
447 * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
448 * the supplied credential parameter is also ignored.
449 *
450 * Also saves the escrow information necessary to re-generate the synthetic password under
451 * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
452 * password escrow should be disabled completely on the given user.
453 *
454 */
455 public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
456 byte[] hash, String credential, int userId) throws RemoteException {
457 AuthenticationToken result = AuthenticationToken.create();
458 GateKeeperResponse response;
459 if (hash != null) {
460 response = gatekeeper.enroll(userId, hash, credential.getBytes(),
461 result.deriveGkPassword());
462 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
463 Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
464 clearSidForUser(userId);
465 } else {
466 saveSyntheticPasswordHandle(response.getPayload(), userId);
467 }
468 } else {
469 clearSidForUser(userId);
470 }
471 saveEscrowData(result, userId);
472 return result;
473 }
474
475 /**
476 * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
477 * Used when adding password to previously-unsecured devices.
478 */
479 public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
480 int userId) throws RemoteException {
481 GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
482 authToken.deriveGkPassword());
483 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
484 Log.e(TAG, "Fail to create new SID for user " + userId);
485 return;
486 }
487 saveSyntheticPasswordHandle(response.getPayload(), userId);
488 }
489
490 // Nuke the SP handle (and as a result, its SID) for the given user.
491 public void clearSidForUser(int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +0100492 destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000493 }
494
495 public boolean hasSidForUser(int userId) {
496 return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
497 }
498
499 // if null, it means there is no SID associated with the user
500 // This can happen if the user is migrated to SP but currently
501 // do not have a lockscreen password.
502 private byte[] loadSyntheticPasswordHandle(int userId) {
503 return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
504 }
505
506 private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
507 saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
508 }
509
510 private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
511 authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
512 authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
513 return authToken.E0 != null && authToken.P1 != null;
514 }
515
516 private void saveEscrowData(AuthenticationToken authToken, int userId) {
517 saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId);
518 saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId);
519 }
520
Rubin Xuf095f832017-01-31 15:23:34 +0000521 public boolean hasEscrowData(int userId) {
522 return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
523 && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
524 }
525
Rubin Xu3bf722a2016-12-15 16:07:38 +0000526 public void destroyEscrowData(int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +0100527 destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
528 destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000529 }
530
Rubin Xu7b7424b2017-03-31 18:03:20 +0100531 private int loadWeaverSlot(long handle, int userId) {
532 final int LENGTH = Byte.BYTES + Integer.BYTES;
533 byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId);
534 if (data == null || data.length != LENGTH) {
535 return INVALID_WEAVER_SLOT;
536 }
537 ByteBuffer buffer = ByteBuffer.allocate(LENGTH);
538 buffer.put(data, 0, data.length);
539 buffer.flip();
540 if (buffer.get() != WEAVER_VERSION) {
541 Log.e(TAG, "Invalid weaver slot version of handle " + handle);
542 return INVALID_WEAVER_SLOT;
543 }
544 return buffer.getInt();
545 }
546
547 private void saveWeaverSlot(int slot, long handle, int userId) {
548 ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
549 buffer.put(WEAVER_VERSION);
550 buffer.putInt(slot);
551 saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
552 }
553
554 private void destroyWeaverSlot(long handle, int userId) {
555 int slot = loadWeaverSlot(handle, userId);
Rubin Xua339df02018-04-10 09:44:18 +0100556 destroyState(WEAVER_SLOT_NAME, handle, userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100557 if (slot != INVALID_WEAVER_SLOT) {
Rubin Xua339df02018-04-10 09:44:18 +0100558 Set<Integer> usedSlots = getUsedWeaverSlots();
559 if (!usedSlots.contains(slot)) {
560 Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
561 try {
562 weaverEnroll(slot, null, null);
563 } catch (RemoteException e) {
564 Log.w(TAG, "Failed to destroy slot", e);
565 }
566 } else {
567 Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100568 }
569 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100570 }
571
Rubin Xua339df02018-04-10 09:44:18 +0100572 /**
573 * Return the set of weaver slots that are currently in use by all users on the device.
574 * <p>
575 * <em>Note:</em> Users who are in the process of being deleted are not tracked here
576 * (due to them being marked as partial in UserManager so not visible from
577 * {@link UserManager#getUsers}). As a result their weaver slots will not be considered
578 * taken and can be reused by new users. Care should be taken when cleaning up the
579 * deleted user in {@link #removeUser}, to prevent a reused slot from being erased
580 * unintentionally.
581 */
582 private Set<Integer> getUsedWeaverSlots() {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100583 Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
584 WEAVER_SLOT_NAME);
585 HashSet<Integer> slots = new HashSet<>();
586 for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) {
587 for (Long handle : entry.getValue()) {
588 int slot = loadWeaverSlot(handle, entry.getKey());
589 slots.add(slot);
590 }
591 }
Rubin Xua339df02018-04-10 09:44:18 +0100592 return slots;
593 }
594
595 private int getNextAvailableWeaverSlot() {
596 Set<Integer> usedSlots = getUsedWeaverSlots();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100597 for (int i = 0; i < mWeaverConfig.slots; i++) {
Rubin Xua339df02018-04-10 09:44:18 +0100598 if (!usedSlots.contains(i)) {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100599 return i;
600 }
601 }
602 throw new RuntimeException("Run out of weaver slots.");
603 }
604
Rubin Xu3bf722a2016-12-15 16:07:38 +0000605 /**
606 * Create a new password based SP blob based on the supplied authentication token, such that
607 * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
608 * in the same authentication token.
609 *
610 * This method only creates SP blob wrapping around the given synthetic password and does not
611 * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
612 * is consistent with the device state by calling other APIs in this class.
613 *
614 * @see #newSidForUser
615 * @see #clearSidForUser
616 */
617 public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
Adrian Roos7374d3a2017-03-31 14:14:53 -0700618 String credential, int credentialType, AuthenticationToken authToken,
619 int requestedQuality, int userId)
Rubin Xu3bf722a2016-12-15 16:07:38 +0000620 throws RemoteException {
621 if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
622 credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
623 credential = DEFAULT_PASSWORD;
624 }
625
626 long handle = generateHandle();
627 PasswordData pwd = PasswordData.create(credentialType);
628 byte[] pwdToken = computePasswordToken(credential, pwd);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100629 final long sid;
630 final byte[] applicationId;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000631
Rubin Xu7b7424b2017-03-31 18:03:20 +0100632 if (isWeaverAvailable()) {
633 // Weaver based user password
634 int weaverSlot = getNextAvailableWeaverSlot();
Rubin Xua339df02018-04-10 09:44:18 +0100635 Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100636 byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
637 if (weaverSecret == null) {
638 Log.e(TAG, "Fail to enroll user password under weaver " + userId);
639 return DEFAULT_HANDLE;
640 }
641 saveWeaverSlot(weaverSlot, handle, userId);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700642 synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100643
644 pwd.passwordHandle = null;
645 sid = GateKeeper.INVALID_SECURE_USER_ID;
646 applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret);
647 } else {
Rubin Xu54c19b62017-04-26 19:40:15 +0100648 // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
649 // to prevent them from accumulating and causing problems.
650 gatekeeper.clearSecureUserId(fakeUid(userId));
Rubin Xu7b7424b2017-03-31 18:03:20 +0100651 // GateKeeper based user password
652 GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
653 passwordTokenToGkInput(pwdToken));
654 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
655 Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
656 return DEFAULT_HANDLE;
657 }
658 pwd.passwordHandle = response.getPayload();
659 sid = sidFromPasswordHandle(pwd.passwordHandle);
660 applicationId = transformUnderSecdiscardable(pwdToken,
661 createSecdiscardable(handle, userId));
Adrian Roos7374d3a2017-03-31 14:14:53 -0700662 synchronizeFrpPassword(pwd, requestedQuality, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000663 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000664 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
665
Rubin Xu3bf722a2016-12-15 16:07:38 +0000666 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
667 applicationId, sid, userId);
668 return handle;
669 }
670
Adrian Roos7374d3a2017-03-31 14:14:53 -0700671 public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
672 String userCredential, int credentialType,
673 ICheckCredentialProgressCallback progressCallback) throws RemoteException {
674 PersistentData persistentData = mStorage.readPersistentDataBlock();
675 if (persistentData.type == PersistentData.TYPE_SP) {
676 PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
677 byte[] pwdToken = computePasswordToken(userCredential, pwd);
678
Adrian Roosb7d69322017-08-08 18:32:03 +0200679 GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
680 0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
Adrian Roos7374d3a2017-03-31 14:14:53 -0700681 return VerifyCredentialResponse.fromGateKeeperResponse(response);
682 } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
683 PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
684 byte[] pwdToken = computePasswordToken(userCredential, pwd);
685 int weaverSlot = persistentData.userId;
686
687 return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
688 } else {
689 Log.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
690 + persistentData.type);
691 return VerifyCredentialResponse.ERROR;
692 }
693 }
694
695
Adrian Roos60dcbbf2017-08-08 16:19:33 +0200696 public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) {
697 if (mStorage.getPersistentDataBlock() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200698 && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) {
Adrian Roos60dcbbf2017-08-08 16:19:33 +0200699 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle,
700 userInfo.id));
701 if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
702 int weaverSlot = loadWeaverSlot(handle, userInfo.id);
703 if (weaverSlot != INVALID_WEAVER_SLOT) {
704 synchronizeWeaverFrpPassword(pwd, requestedQuality, userInfo.id, weaverSlot);
705 } else {
706 synchronizeFrpPassword(pwd, requestedQuality, userInfo.id);
707 }
708 }
709 }
710 }
711
Adrian Roos7374d3a2017-03-31 14:14:53 -0700712 private void synchronizeFrpPassword(PasswordData pwd,
713 int requestedQuality, int userId) {
714 if (mStorage.getPersistentDataBlock() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200715 && LockPatternUtils.userOwnsFrpCredential(mContext,
716 mUserManager.getUserInfo(userId))) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700717 if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
718 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
719 pwd.toBytes());
720 } else {
721 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
722 }
723 }
724 }
725
726 private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
727 int weaverSlot) {
728 if (mStorage.getPersistentDataBlock() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200729 && LockPatternUtils.userOwnsFrpCredential(mContext,
730 mUserManager.getUserInfo(userId))) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700731 if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
732 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
733 requestedQuality, pwd.toBytes());
734 } else {
735 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, 0, 0, null);
736 }
737 }
738 }
739
Rubin Xu7b7424b2017-03-31 18:03:20 +0100740 private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
Rubin Xuf095f832017-01-31 15:23:34 +0000741
742 public long createTokenBasedSyntheticPassword(byte[] token, int userId) {
743 long handle = generateHandle();
Rubin Xuf095f832017-01-31 15:23:34 +0000744 if (!tokenMap.containsKey(userId)) {
745 tokenMap.put(userId, new ArrayMap<>());
746 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100747 TokenData tokenData = new TokenData();
748 final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
749 if (isWeaverAvailable()) {
750 tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
751 tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
752 PERSONALISATION_WEAVER_TOKEN, secdiscardable);
753 } else {
754 tokenData.secdiscardableOnDisk = secdiscardable;
755 tokenData.weaverSecret = null;
756 }
757 tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
758
759 tokenMap.get(userId).put(handle, tokenData);
Rubin Xuf095f832017-01-31 15:23:34 +0000760 return handle;
761 }
762
763 public Set<Long> getPendingTokensForUser(int userId) {
764 if (!tokenMap.containsKey(userId)) {
765 return Collections.emptySet();
766 }
767 return tokenMap.get(userId).keySet();
768 }
769
770 public boolean removePendingToken(long handle, int userId) {
771 if (!tokenMap.containsKey(userId)) {
772 return false;
773 }
774 return tokenMap.get(userId).remove(handle) != null;
775 }
776
777 public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
778 int userId) {
779 if (!tokenMap.containsKey(userId)) {
780 return false;
781 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100782 TokenData tokenData = tokenMap.get(userId).get(handle);
783 if (tokenData == null) {
Rubin Xuf095f832017-01-31 15:23:34 +0000784 return false;
785 }
786 if (!loadEscrowData(authToken, userId)) {
787 Log.w(TAG, "User is not escrowable");
788 return false;
789 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100790 if (isWeaverAvailable()) {
791 int slot = getNextAvailableWeaverSlot();
792 try {
Rubin Xua339df02018-04-10 09:44:18 +0100793 Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100794 weaverEnroll(slot, null, tokenData.weaverSecret);
795 } catch (RemoteException e) {
796 Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
797 return false;
798 }
799 saveWeaverSlot(slot, handle, userId);
800 }
801 saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000802 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
Rubin Xu7b7424b2017-03-31 18:03:20 +0100803 tokenData.aggregatedSecret, 0L, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000804 tokenMap.get(userId).remove(handle);
805 return true;
806 }
807
Rubin Xu3bf722a2016-12-15 16:07:38 +0000808 private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
809 byte[] applicationId, long sid, int userId) {
Rubin Xuf095f832017-01-31 15:23:34 +0000810 final byte[] secret;
811 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
812 secret = authToken.computeP0();
813 } else {
814 secret = authToken.syntheticPassword.getBytes();
815 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000816 byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
817 byte[] blob = new byte[content.length + 1 + 1];
Paul Crowley63ef1512018-11-28 12:48:16 -0800818 /*
819 * We can upgrade from v1 to v2 because that's just a change in the way that
820 * the SP is stored. However, we can't upgrade to v3 because that is a change
821 * in the way that passwords are derived from the SP.
822 */
823 if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
824 blob[0] = SYNTHETIC_PASSWORD_VERSION_V3;
825 } else {
826 blob[0] = SYNTHETIC_PASSWORD_VERSION_V2;
827 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000828 blob[1] = type;
829 System.arraycopy(content, 0, blob, 2, content.length);
830 saveState(SP_BLOB_NAME, blob, handle, userId);
831 }
832
833 /**
834 * Decrypt a synthetic password by supplying the user credential and corresponding password
835 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
836 * verification to referesh the SID & Auth token maintained by the system.
Rubin Xu16c823e2017-06-27 14:44:58 +0100837 * Note: the credential type is not validated here since there are call sites where the type is
838 * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
Rubin Xu3bf722a2016-12-15 16:07:38 +0000839 */
840 public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
Rubin Xucf326f12017-11-15 11:55:35 +0000841 long handle, String credential, int userId,
842 ICheckCredentialProgressCallback progressCallback) throws RemoteException {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000843 if (credential == null) {
844 credential = DEFAULT_PASSWORD;
845 }
846 AuthenticationResult result = new AuthenticationResult();
847 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
Rubin Xu16c823e2017-06-27 14:44:58 +0100848 result.credentialType = pwd.passwordType;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000849 byte[] pwdToken = computePasswordToken(credential, pwd);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000850
Rubin Xu7b7424b2017-03-31 18:03:20 +0100851 final byte[] applicationId;
Rubin Xu8c528652017-10-31 15:40:32 +0000852 final long sid;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100853 int weaverSlot = loadWeaverSlot(handle, userId);
854 if (weaverSlot != INVALID_WEAVER_SLOT) {
855 // Weaver based user password
856 if (!isWeaverAvailable()) {
857 Log.e(TAG, "No weaver service to unwrap password based SP");
858 result.gkResponse = VerifyCredentialResponse.ERROR;
859 return result;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000860 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100861 result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
862 if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
863 return result;
864 }
Rubin Xu8c528652017-10-31 15:40:32 +0000865 sid = GateKeeper.INVALID_SECURE_USER_ID;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100866 applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
867 } else {
868 byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
869 GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
870 pwd.passwordHandle, gkPwdToken);
871 int responseCode = response.getResponseCode();
872 if (responseCode == GateKeeperResponse.RESPONSE_OK) {
873 result.gkResponse = VerifyCredentialResponse.OK;
874 if (response.getShouldReEnroll()) {
875 GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
876 pwd.passwordHandle, gkPwdToken, gkPwdToken);
877 if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
878 pwd.passwordHandle = reenrollResponse.getPayload();
879 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700880 synchronizeFrpPassword(pwd,
881 pwd.passwordType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
882 ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
883 : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
884 /* TODO(roosa): keep the same password quality */,
885 userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100886 } else {
887 Log.w(TAG, "Fail to re-enroll user password for user " + userId);
888 // continue the flow anyway
889 }
890 }
891 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
892 result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
893 return result;
894 } else {
895 result.gkResponse = VerifyCredentialResponse.ERROR;
896 return result;
897 }
Rubin Xu8c528652017-10-31 15:40:32 +0000898 sid = sidFromPasswordHandle(pwd.passwordHandle);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100899 applicationId = transformUnderSecdiscardable(pwdToken,
900 loadSecdiscardable(handle, userId));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000901 }
Rubin Xucf326f12017-11-15 11:55:35 +0000902 // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
903 // Notify the callback so the keyguard UI can proceed immediately.
904 if (progressCallback != null) {
905 progressCallback.onCredentialVerified();
906 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000907 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
Rubin Xu8c528652017-10-31 15:40:32 +0000908 applicationId, sid, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000909
910 // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
911 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
912 return result;
913 }
914
Rubin Xuf095f832017-01-31 15:23:34 +0000915 /**
916 * Decrypt a synthetic password by supplying an escrow token and corresponding token
917 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
918 * verification to referesh the SID & Auth token maintained by the system.
919 */
920 public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
921 IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
922 throws RemoteException {
923 AuthenticationResult result = new AuthenticationResult();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100924 byte[] secdiscardable = loadSecdiscardable(handle, userId);
925 int slotId = loadWeaverSlot(handle, userId);
926 if (slotId != INVALID_WEAVER_SLOT) {
927 if (!isWeaverAvailable()) {
928 Log.e(TAG, "No weaver service to unwrap token based SP");
929 result.gkResponse = VerifyCredentialResponse.ERROR;
930 return result;
931 }
932 VerifyCredentialResponse response = weaverVerify(slotId, null);
933 if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
934 response.getPayload() == null) {
935 Log.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
936 result.gkResponse = VerifyCredentialResponse.ERROR;
937 return result;
938 }
939 secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
940 PERSONALISATION_WEAVER_TOKEN, secdiscardable);
941 }
942 byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
Rubin Xuf095f832017-01-31 15:23:34 +0000943 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
Rubin Xu8c528652017-10-31 15:40:32 +0000944 applicationId, 0L, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000945 if (result.authToken != null) {
946 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
947 if (result.gkResponse == null) {
948 // The user currently has no password. return OK with null payload so null
949 // is propagated to unlockUser()
950 result.gkResponse = VerifyCredentialResponse.OK;
951 }
952 } else {
953 result.gkResponse = VerifyCredentialResponse.ERROR;
954 }
955 return result;
956 }
957
Rubin Xu3bf722a2016-12-15 16:07:38 +0000958 private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
Rubin Xu8c528652017-10-31 15:40:32 +0000959 byte[] applicationId, long sid, int userId) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000960 byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
961 if (blob == null) {
962 return null;
963 }
Rubin Xu8c528652017-10-31 15:40:32 +0000964 final byte version = blob[0];
Paul Crowley63ef1512018-11-28 12:48:16 -0800965 if (version != SYNTHETIC_PASSWORD_VERSION_V3
966 && version != SYNTHETIC_PASSWORD_VERSION_V2
967 && version != SYNTHETIC_PASSWORD_VERSION_V1) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000968 throw new RuntimeException("Unknown blob version");
969 }
970 if (blob[1] != type) {
971 throw new RuntimeException("Invalid blob type");
972 }
Rubin Xu8c528652017-10-31 15:40:32 +0000973 final byte[] secret;
974 if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
975 secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
976 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
977 } else {
978 secret = decryptSPBlob(getHandleName(handle),
Rubin Xu3bf722a2016-12-15 16:07:38 +0000979 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
Rubin Xu8c528652017-10-31 15:40:32 +0000980 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000981 if (secret == null) {
982 Log.e(TAG, "Fail to decrypt SP for user " + userId);
983 return null;
984 }
Paul Crowley63ef1512018-11-28 12:48:16 -0800985 AuthenticationToken result = new AuthenticationToken(version);
Rubin Xuf095f832017-01-31 15:23:34 +0000986 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
987 if (!loadEscrowData(result, userId)) {
988 Log.e(TAG, "User is not escrowable: " + userId);
989 return null;
990 }
991 result.recreate(secret);
992 } else {
993 result.syntheticPassword = new String(secret);
994 }
Rubin Xu8c528652017-10-31 15:40:32 +0000995 if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
996 Log.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
997 createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
998 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000999 return result;
1000 }
1001
1002 /**
1003 * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
1004 * if required.
1005 *
1006 * Normally performing verifyChallenge with an AuthenticationToken should always return
1007 * RESPONSE_OK, since user authentication failures are detected earlier when trying to
1008 * decrypt SP.
1009 */
Rubin Xu8b30ec32017-03-05 00:47:09 +00001010 public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
Rubin Xu3bf722a2016-12-15 16:07:38 +00001011 @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
1012 byte[] spHandle = loadSyntheticPasswordHandle(userId);
1013 if (spHandle == null) {
1014 // There is no password handle associated with the given user, i.e. the user is not
1015 // secured by lockscreen and has no SID, so just return here;
1016 return null;
1017 }
1018 VerifyCredentialResponse result;
1019 GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
1020 spHandle, auth.deriveGkPassword());
1021 int responseCode = response.getResponseCode();
1022 if (responseCode == GateKeeperResponse.RESPONSE_OK) {
1023 result = new VerifyCredentialResponse(response.getPayload());
1024 if (response.getShouldReEnroll()) {
1025 response = gatekeeper.enroll(userId, spHandle,
1026 spHandle, auth.deriveGkPassword());
1027 if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
1028 spHandle = response.getPayload();
1029 saveSyntheticPasswordHandle(spHandle, userId);
1030 // Call self again to re-verify with updated handle
1031 return verifyChallenge(gatekeeper, auth, challenge, userId);
1032 } else {
1033 Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
1034 // Fall through, return existing handle
1035 }
1036 }
1037 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
1038 result = new VerifyCredentialResponse(response.getTimeout());
1039 } else {
1040 result = VerifyCredentialResponse.ERROR;
1041 }
1042 return result;
1043 }
1044
1045 public boolean existsHandle(long handle, int userId) {
1046 return hasState(SP_BLOB_NAME, handle, userId);
1047 }
1048
Rubin Xuf095f832017-01-31 15:23:34 +00001049 public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
1050 destroySyntheticPassword(handle, userId);
Rubin Xuaa32d152017-04-27 17:01:05 +01001051 destroyState(SECDISCARDABLE_NAME, handle, userId);
Rubin Xuf095f832017-01-31 15:23:34 +00001052 }
1053
Rubin Xu3bf722a2016-12-15 16:07:38 +00001054 public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
1055 destroySyntheticPassword(handle, userId);
Rubin Xuaa32d152017-04-27 17:01:05 +01001056 destroyState(SECDISCARDABLE_NAME, handle, userId);
1057 destroyState(PASSWORD_DATA_NAME, handle, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001058 }
1059
1060 private void destroySyntheticPassword(long handle, int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +01001061 destroyState(SP_BLOB_NAME, handle, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001062 destroySPBlobKey(getHandleName(handle));
Rubin Xu7b7424b2017-03-31 18:03:20 +01001063 if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
1064 destroyWeaverSlot(handle, userId);
1065 }
1066 }
1067
1068 private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
1069 byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash(
1070 PERSONALISATION_WEAVER_PASSWORD, secret);
1071 byte[] result = new byte[data.length + weaverSecret.length];
1072 System.arraycopy(data, 0, result, 0, data.length);
1073 System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length);
1074 return result;
Rubin Xu3bf722a2016-12-15 16:07:38 +00001075 }
1076
1077 private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
1078 byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
1079 PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
1080 byte[] result = new byte[data.length + secdiscardable.length];
1081 System.arraycopy(data, 0, result, 0, data.length);
1082 System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
1083 return result;
1084 }
1085
1086 private byte[] createSecdiscardable(long handle, int userId) {
1087 byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
Rubin Xu7b7424b2017-03-31 18:03:20 +01001088 saveSecdiscardable(handle, data, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001089 return data;
1090 }
1091
Rubin Xu7b7424b2017-03-31 18:03:20 +01001092 private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
1093 saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
1094 }
1095
Rubin Xu3bf722a2016-12-15 16:07:38 +00001096 private byte[] loadSecdiscardable(long handle, int userId) {
1097 return loadState(SECDISCARDABLE_NAME, handle, userId);
1098 }
1099
1100 private boolean hasState(String stateName, long handle, int userId) {
1101 return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
1102 }
1103
1104 private byte[] loadState(String stateName, long handle, int userId) {
1105 return mStorage.readSyntheticPasswordState(userId, handle, stateName);
1106 }
1107
1108 private void saveState(String stateName, byte[] data, long handle, int userId) {
1109 mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
1110 }
1111
Rubin Xuaa32d152017-04-27 17:01:05 +01001112 private void destroyState(String stateName, long handle, int userId) {
1113 mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001114 }
1115
1116 protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
1117 return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
1118 }
1119
1120 protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
1121 return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
1122 }
1123
1124 protected void destroySPBlobKey(String keyAlias) {
1125 SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
1126 }
1127
1128 public static long generateHandle() {
1129 SecureRandom rng = new SecureRandom();
1130 long result;
1131 do {
1132 result = rng.nextLong();
1133 } while (result == DEFAULT_HANDLE);
1134 return result;
1135 }
1136
1137 private int fakeUid(int uid) {
1138 return 100000 + uid;
1139 }
1140
1141 protected static byte[] secureRandom(int length) {
1142 try {
1143 return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
1144 } catch (NoSuchAlgorithmException e) {
1145 e.printStackTrace();
1146 return null;
1147 }
1148 }
1149
1150 private String getHandleName(long handle) {
1151 return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
1152 }
1153
1154 private byte[] computePasswordToken(String password, PasswordData data) {
1155 return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
1156 PASSWORD_TOKEN_LENGTH);
1157 }
1158
1159 private byte[] passwordTokenToGkInput(byte[] token) {
1160 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
1161 }
1162
Rubin Xu7b7424b2017-03-31 18:03:20 +01001163 private byte[] passwordTokenToWeaverKey(byte[] token) {
1164 byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
1165 if (key.length < mWeaverConfig.keySize) {
1166 throw new RuntimeException("weaver key length too small");
1167 }
1168 return Arrays.copyOf(key, mWeaverConfig.keySize);
1169 }
1170
Rubin Xu3bf722a2016-12-15 16:07:38 +00001171 protected long sidFromPasswordHandle(byte[] handle) {
1172 return nativeSidFromPasswordHandle(handle);
1173 }
1174
1175 protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
1176 return nativeScrypt(password.getBytes(), salt, N, r, p, outLen);
1177 }
1178
1179 native long nativeSidFromPasswordHandle(byte[] handle);
1180 native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen);
1181
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
Rubin Xu3bf722a2016-12-15 16:07:38 +00001198 final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
1199 public static String bytesToHex(byte[] bytes) {
1200 if (bytes == null) {
1201 return "null";
1202 }
1203 char[] hexChars = new char[bytes.length * 2];
1204 for ( int j = 0; j < bytes.length; j++ ) {
1205 int v = bytes[j] & 0xFF;
1206 hexChars[j * 2] = hexArray[v >>> 4];
1207 hexChars[j * 2 + 1] = hexArray[v & 0x0F];
1208 }
1209 return new String(hexChars);
1210 }
1211}