blob: 596daeb1427bbc37070b205d0c1e9517a8a411a8 [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;
Rubin Xu7b7424b2017-03-31 18:03:20 +010029import android.security.GateKeeper;
Adrian Roos7374d3a2017-03-31 14:14:53 -070030import android.os.RemoteException;
31import android.os.UserManager;
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;
105 private static final byte SYNTHETIC_PASSWORD_VERSION = 2;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000106 private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
Rubin Xuf095f832017-01-31 15:23:34 +0000107 private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000108
109 // 256-bit synthetic password
110 private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
111
Rubin Xu157bddd2017-04-24 15:51:59 +0100112 private static final int PASSWORD_SCRYPT_N = 11;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000113 private static final int PASSWORD_SCRYPT_R = 3;
114 private static final int PASSWORD_SCRYPT_P = 1;
115 private static final int PASSWORD_SALT_LENGTH = 16;
116 private static final int PASSWORD_TOKEN_LENGTH = 32;
117 private static final String TAG = "SyntheticPasswordManager";
118
119 private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
120 private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes();
121 private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();
122 private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
123 private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
Andrew Sculle6527c12018-01-05 18:33:58 +0000124 private static final byte[] PERSONALIZATION_AUTHSECRET_KEY = "authsecret-hal".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000125 private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
Rubin Xuf01e9072018-03-30 20:59:28 +0100126 private static final byte[] PERSONALIZATION_PASSWORD_HASH = "pw-hash".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000127 private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100128 private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
129 private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
130 private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
Rubin Xu3bf722a2016-12-15 16:07:38 +0000131
132 static class AuthenticationResult {
133 public AuthenticationToken authToken;
134 public VerifyCredentialResponse gkResponse;
Rubin Xu16c823e2017-06-27 14:44:58 +0100135 public int credentialType;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000136 }
137
138 static class AuthenticationToken {
139 /*
140 * Here is the relationship between all three fields:
141 * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
142 * syntheticPassword = hash(P0 || P1)
143 * E0 = P0 encrypted under syntheticPassword, stored on disk.
144 */
145 private @Nullable byte[] E0;
146 private @Nullable byte[] P1;
147 private @NonNull String syntheticPassword;
148
149 public String deriveKeyStorePassword() {
150 return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
151 PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
152 }
153
154 public byte[] deriveGkPassword() {
155 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
156 syntheticPassword.getBytes());
157 }
158
159 public byte[] deriveDiskEncryptionKey() {
160 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
161 syntheticPassword.getBytes());
162 }
163
Andrew Sculle6527c12018-01-05 18:33:58 +0000164 public byte[] deriveVendorAuthSecret() {
165 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_AUTHSECRET_KEY,
166 syntheticPassword.getBytes());
167 }
168
Rubin Xuf01e9072018-03-30 20:59:28 +0100169 public byte[] derivePasswordHashFactor() {
170 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_PASSWORD_HASH,
171 syntheticPassword.getBytes());
172 }
173
Rubin Xuf095f832017-01-31 15:23:34 +0000174 private void initialize(byte[] P0, byte[] P1) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000175 this.P1 = P1;
176 this.syntheticPassword = String.valueOf(HexEncoding.encode(
177 SyntheticPasswordCrypto.personalisedHash(
178 PERSONALIZATION_SP_SPLIT, P0, P1)));
179 this.E0 = SyntheticPasswordCrypto.encrypt(this.syntheticPassword.getBytes(),
180 PERSONALIZATION_E0, P0);
181 }
182
Rubin Xuf095f832017-01-31 15:23:34 +0000183 public void recreate(byte[] secret) {
184 initialize(secret, this.P1);
185 }
186
Rubin Xu3bf722a2016-12-15 16:07:38 +0000187 protected static AuthenticationToken create() {
188 AuthenticationToken result = new AuthenticationToken();
189 result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
190 secureRandom(SYNTHETIC_PASSWORD_LENGTH));
191 return result;
192 }
193
194 public byte[] computeP0() {
195 if (E0 == null) {
196 return null;
197 }
198 return SyntheticPasswordCrypto.decrypt(syntheticPassword.getBytes(), PERSONALIZATION_E0,
199 E0);
200 }
201 }
202
203 static class PasswordData {
204 byte scryptN;
205 byte scryptR;
206 byte scryptP;
207 public int passwordType;
208 byte[] salt;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100209 // For GateKeeper-based credential, this is the password handle returned by GK,
210 // for weaver-based credential, this is empty.
Rubin Xu3bf722a2016-12-15 16:07:38 +0000211 public byte[] passwordHandle;
212
213 public static PasswordData create(int passwordType) {
214 PasswordData result = new PasswordData();
215 result.scryptN = PASSWORD_SCRYPT_N;
216 result.scryptR = PASSWORD_SCRYPT_R;
217 result.scryptP = PASSWORD_SCRYPT_P;
218 result.passwordType = passwordType;
219 result.salt = secureRandom(PASSWORD_SALT_LENGTH);
220 return result;
221 }
222
223 public static PasswordData fromBytes(byte[] data) {
224 PasswordData result = new PasswordData();
225 ByteBuffer buffer = ByteBuffer.allocate(data.length);
226 buffer.put(data, 0, data.length);
227 buffer.flip();
228 result.passwordType = buffer.getInt();
229 result.scryptN = buffer.get();
230 result.scryptR = buffer.get();
231 result.scryptP = buffer.get();
232 int saltLen = buffer.getInt();
233 result.salt = new byte[saltLen];
234 buffer.get(result.salt);
235 int handleLen = buffer.getInt();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100236 if (handleLen > 0) {
237 result.passwordHandle = new byte[handleLen];
238 buffer.get(result.passwordHandle);
239 } else {
240 result.passwordHandle = null;
241 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000242 return result;
243 }
244
245 public byte[] toBytes() {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100246
Rubin Xu3bf722a2016-12-15 16:07:38 +0000247 ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
Rubin Xu7b7424b2017-03-31 18:03:20 +0100248 + Integer.BYTES + salt.length + Integer.BYTES +
249 (passwordHandle != null ? passwordHandle.length : 0));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000250 buffer.putInt(passwordType);
251 buffer.put(scryptN);
252 buffer.put(scryptR);
253 buffer.put(scryptP);
254 buffer.putInt(salt.length);
255 buffer.put(salt);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100256 if (passwordHandle != null && passwordHandle.length > 0) {
257 buffer.putInt(passwordHandle.length);
258 buffer.put(passwordHandle);
259 } else {
260 buffer.putInt(0);
261 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000262 return buffer.array();
263 }
264 }
265
Rubin Xu7b7424b2017-03-31 18:03:20 +0100266 static class TokenData {
267 byte[] secdiscardableOnDisk;
268 byte[] weaverSecret;
269 byte[] aggregatedSecret;
270 }
271
Adrian Roos2adc2632017-09-05 17:01:42 +0200272 private final Context mContext;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000273 private LockSettingsStorage mStorage;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100274 private IWeaver mWeaver;
275 private WeaverConfig mWeaverConfig;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000276
Adrian Roos7374d3a2017-03-31 14:14:53 -0700277 private final UserManager mUserManager;
278
Adrian Roos2adc2632017-09-05 17:01:42 +0200279 public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
280 UserManager userManager) {
281 mContext = context;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000282 mStorage = storage;
Adrian Roos7374d3a2017-03-31 14:14:53 -0700283 mUserManager = userManager;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000284 }
285
Rubin Xu7b7424b2017-03-31 18:03:20 +0100286 @VisibleForTesting
287 protected IWeaver getWeaverService() throws RemoteException {
288 try {
289 return IWeaver.getService();
290 } catch (NoSuchElementException e) {
291 Slog.i(TAG, "Device does not support weaver");
292 return null;
293 }
294 }
295
296 public synchronized void initWeaverService() {
297 if (mWeaver != null) {
298 return;
299 }
300 try {
301 mWeaverConfig = null;
302 mWeaver = getWeaverService();
303 if (mWeaver != null) {
304 mWeaver.getConfig((int status, WeaverConfig config) -> {
305 if (status == WeaverStatus.OK && config.slots > 0) {
306 mWeaverConfig = config;
307 } else {
308 Slog.e(TAG, "Failed to get weaver config, status " + status
309 + " slots: " + config.slots);
310 mWeaver = null;
311 }
312 });
313 }
314 } catch (RemoteException e) {
315 Slog.e(TAG, "Failed to get weaver service", e);
316 }
317 }
318
319 private synchronized boolean isWeaverAvailable() {
320 if (mWeaver == null) {
321 //Re-initializing weaver in case there was a transient error preventing access to it.
322 initWeaverService();
323 }
324 return mWeaver != null && mWeaverConfig.slots > 0;
325 }
326
327 /**
328 * Enroll the given key value pair into the specified weaver slot. if the given key is null,
329 * a default all-zero key is used. If the value is not specified, a fresh random secret is
330 * generated as the value.
331 *
332 * @return the value stored in the weaver slot
333 * @throws RemoteException
334 */
335 private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
336 throws RemoteException {
337 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
338 throw new RuntimeException("Invalid slot for weaver");
339 }
340 if (key == null) {
341 key = new byte[mWeaverConfig.keySize];
342 } else if (key.length != mWeaverConfig.keySize) {
343 throw new RuntimeException("Invalid key size for weaver");
344 }
345 if (value == null) {
346 value = secureRandom(mWeaverConfig.valueSize);
347 }
348 int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
349 if (writeStatus != WeaverStatus.OK) {
350 Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
351 return null;
352 }
353 return value;
354 }
355
356 /**
357 * Verify the supplied key against a weaver slot, returning a response indicating whether
358 * the verification is successful, throttled or failed. If successful, the bound secret
359 * is also returned.
360 * @throws RemoteException
361 */
362 private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
363 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
364 throw new RuntimeException("Invalid slot for weaver");
365 }
366 if (key == null) {
367 key = new byte[mWeaverConfig.keySize];
368 } else if (key.length != mWeaverConfig.keySize) {
369 throw new RuntimeException("Invalid key size for weaver");
370 }
371 final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
372 mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
373 switch (status) {
374 case WeaverReadStatus.OK:
375 response[0] = new VerifyCredentialResponse(
376 fromByteArrayList(readResponse.value));
377 break;
378 case WeaverReadStatus.THROTTLE:
379 response[0] = new VerifyCredentialResponse(readResponse.timeout);
380 Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
381 break;
382 case WeaverReadStatus.INCORRECT_KEY:
383 if (readResponse.timeout == 0) {
384 response[0] = VerifyCredentialResponse.ERROR;
385 Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
386 } else {
387 response[0] = new VerifyCredentialResponse(readResponse.timeout);
388 Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
389 }
390 break;
391 case WeaverReadStatus.FAILED:
392 response[0] = VerifyCredentialResponse.ERROR;
393 Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
394 break;
395 default:
396 response[0] = VerifyCredentialResponse.ERROR;
397 Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
398 break;
399 }
400 });
401 return response[0];
402 }
403
404 public void removeUser(int userId) {
Rubin Xu2f22ee42017-09-15 11:56:53 +0100405 for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
406 destroyWeaverSlot(handle, userId);
407 destroySPBlobKey(getHandleName(handle));
Rubin Xu7b7424b2017-03-31 18:03:20 +0100408 }
409 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000410
411 public int getCredentialType(long handle, int userId) {
412 byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
413 if (passwordData == null) {
414 Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
415 return LockPatternUtils.CREDENTIAL_TYPE_NONE;
416 }
417 return PasswordData.fromBytes(passwordData).passwordType;
418 }
419
420 /**
421 * Initializing a new Authentication token, possibly from an existing credential and hash.
422 *
423 * The authentication token would bear a randomly-generated synthetic password.
424 *
425 * This method has the side effect of rebinding the SID of the given user to the
426 * newly-generated SP.
427 *
428 * If the existing credential hash is non-null, the existing SID mill be migrated so
429 * the synthetic password in the authentication token will produce the same SID
430 * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
Rubin Xu7b7424b2017-03-31 18:03:20 +0100431 * in a per-user data storage.)
Rubin Xu3bf722a2016-12-15 16:07:38 +0000432 *
433 * If the existing credential hash is null, it means the given user should have no SID so
434 * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
435 * the supplied credential parameter is also ignored.
436 *
437 * Also saves the escrow information necessary to re-generate the synthetic password under
438 * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
439 * password escrow should be disabled completely on the given user.
440 *
441 */
442 public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
443 byte[] hash, String credential, int userId) throws RemoteException {
444 AuthenticationToken result = AuthenticationToken.create();
445 GateKeeperResponse response;
446 if (hash != null) {
447 response = gatekeeper.enroll(userId, hash, credential.getBytes(),
448 result.deriveGkPassword());
449 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
450 Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
451 clearSidForUser(userId);
452 } else {
453 saveSyntheticPasswordHandle(response.getPayload(), userId);
454 }
455 } else {
456 clearSidForUser(userId);
457 }
458 saveEscrowData(result, userId);
459 return result;
460 }
461
462 /**
463 * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
464 * Used when adding password to previously-unsecured devices.
465 */
466 public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
467 int userId) throws RemoteException {
468 GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
469 authToken.deriveGkPassword());
470 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
471 Log.e(TAG, "Fail to create new SID for user " + userId);
472 return;
473 }
474 saveSyntheticPasswordHandle(response.getPayload(), userId);
475 }
476
477 // Nuke the SP handle (and as a result, its SID) for the given user.
478 public void clearSidForUser(int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +0100479 destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000480 }
481
482 public boolean hasSidForUser(int userId) {
483 return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
484 }
485
486 // if null, it means there is no SID associated with the user
487 // This can happen if the user is migrated to SP but currently
488 // do not have a lockscreen password.
489 private byte[] loadSyntheticPasswordHandle(int userId) {
490 return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
491 }
492
493 private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
494 saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
495 }
496
497 private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
498 authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
499 authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
500 return authToken.E0 != null && authToken.P1 != null;
501 }
502
503 private void saveEscrowData(AuthenticationToken authToken, int userId) {
504 saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId);
505 saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId);
506 }
507
Rubin Xuf095f832017-01-31 15:23:34 +0000508 public boolean hasEscrowData(int userId) {
509 return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
510 && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
511 }
512
Rubin Xu3bf722a2016-12-15 16:07:38 +0000513 public void destroyEscrowData(int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +0100514 destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
515 destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000516 }
517
Rubin Xu7b7424b2017-03-31 18:03:20 +0100518 private int loadWeaverSlot(long handle, int userId) {
519 final int LENGTH = Byte.BYTES + Integer.BYTES;
520 byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId);
521 if (data == null || data.length != LENGTH) {
522 return INVALID_WEAVER_SLOT;
523 }
524 ByteBuffer buffer = ByteBuffer.allocate(LENGTH);
525 buffer.put(data, 0, data.length);
526 buffer.flip();
527 if (buffer.get() != WEAVER_VERSION) {
528 Log.e(TAG, "Invalid weaver slot version of handle " + handle);
529 return INVALID_WEAVER_SLOT;
530 }
531 return buffer.getInt();
532 }
533
534 private void saveWeaverSlot(int slot, long handle, int userId) {
535 ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
536 buffer.put(WEAVER_VERSION);
537 buffer.putInt(slot);
538 saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
539 }
540
541 private void destroyWeaverSlot(long handle, int userId) {
542 int slot = loadWeaverSlot(handle, userId);
Rubin Xua339df02018-04-10 09:44:18 +0100543 destroyState(WEAVER_SLOT_NAME, handle, userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100544 if (slot != INVALID_WEAVER_SLOT) {
Rubin Xua339df02018-04-10 09:44:18 +0100545 Set<Integer> usedSlots = getUsedWeaverSlots();
546 if (!usedSlots.contains(slot)) {
547 Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
548 try {
549 weaverEnroll(slot, null, null);
550 } catch (RemoteException e) {
551 Log.w(TAG, "Failed to destroy slot", e);
552 }
553 } else {
554 Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100555 }
556 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100557 }
558
Rubin Xua339df02018-04-10 09:44:18 +0100559 /**
560 * Return the set of weaver slots that are currently in use by all users on the device.
561 * <p>
562 * <em>Note:</em> Users who are in the process of being deleted are not tracked here
563 * (due to them being marked as partial in UserManager so not visible from
564 * {@link UserManager#getUsers}). As a result their weaver slots will not be considered
565 * taken and can be reused by new users. Care should be taken when cleaning up the
566 * deleted user in {@link #removeUser}, to prevent a reused slot from being erased
567 * unintentionally.
568 */
569 private Set<Integer> getUsedWeaverSlots() {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100570 Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
571 WEAVER_SLOT_NAME);
572 HashSet<Integer> slots = new HashSet<>();
573 for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) {
574 for (Long handle : entry.getValue()) {
575 int slot = loadWeaverSlot(handle, entry.getKey());
576 slots.add(slot);
577 }
578 }
Rubin Xua339df02018-04-10 09:44:18 +0100579 return slots;
580 }
581
582 private int getNextAvailableWeaverSlot() {
583 Set<Integer> usedSlots = getUsedWeaverSlots();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100584 for (int i = 0; i < mWeaverConfig.slots; i++) {
Rubin Xua339df02018-04-10 09:44:18 +0100585 if (!usedSlots.contains(i)) {
Rubin Xu7b7424b2017-03-31 18:03:20 +0100586 return i;
587 }
588 }
589 throw new RuntimeException("Run out of weaver slots.");
590 }
591
Rubin Xu3bf722a2016-12-15 16:07:38 +0000592 /**
593 * Create a new password based SP blob based on the supplied authentication token, such that
594 * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
595 * in the same authentication token.
596 *
597 * This method only creates SP blob wrapping around the given synthetic password and does not
598 * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
599 * is consistent with the device state by calling other APIs in this class.
600 *
601 * @see #newSidForUser
602 * @see #clearSidForUser
603 */
604 public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
Adrian Roos7374d3a2017-03-31 14:14:53 -0700605 String credential, int credentialType, AuthenticationToken authToken,
606 int requestedQuality, int userId)
Rubin Xu3bf722a2016-12-15 16:07:38 +0000607 throws RemoteException {
608 if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
609 credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
610 credential = DEFAULT_PASSWORD;
611 }
612
613 long handle = generateHandle();
614 PasswordData pwd = PasswordData.create(credentialType);
615 byte[] pwdToken = computePasswordToken(credential, pwd);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100616 final long sid;
617 final byte[] applicationId;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000618
Rubin Xu7b7424b2017-03-31 18:03:20 +0100619 if (isWeaverAvailable()) {
620 // Weaver based user password
621 int weaverSlot = getNextAvailableWeaverSlot();
Rubin Xua339df02018-04-10 09:44:18 +0100622 Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100623 byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
624 if (weaverSecret == null) {
625 Log.e(TAG, "Fail to enroll user password under weaver " + userId);
626 return DEFAULT_HANDLE;
627 }
628 saveWeaverSlot(weaverSlot, handle, userId);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700629 synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100630
631 pwd.passwordHandle = null;
632 sid = GateKeeper.INVALID_SECURE_USER_ID;
633 applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret);
634 } else {
Rubin Xu54c19b62017-04-26 19:40:15 +0100635 // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
636 // to prevent them from accumulating and causing problems.
637 gatekeeper.clearSecureUserId(fakeUid(userId));
Rubin Xu7b7424b2017-03-31 18:03:20 +0100638 // GateKeeper based user password
639 GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
640 passwordTokenToGkInput(pwdToken));
641 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
642 Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
643 return DEFAULT_HANDLE;
644 }
645 pwd.passwordHandle = response.getPayload();
646 sid = sidFromPasswordHandle(pwd.passwordHandle);
647 applicationId = transformUnderSecdiscardable(pwdToken,
648 createSecdiscardable(handle, userId));
Adrian Roos7374d3a2017-03-31 14:14:53 -0700649 synchronizeFrpPassword(pwd, requestedQuality, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000650 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000651 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
652
Rubin Xu3bf722a2016-12-15 16:07:38 +0000653 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
654 applicationId, sid, userId);
655 return handle;
656 }
657
Adrian Roos7374d3a2017-03-31 14:14:53 -0700658 public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
659 String userCredential, int credentialType,
660 ICheckCredentialProgressCallback progressCallback) throws RemoteException {
661 PersistentData persistentData = mStorage.readPersistentDataBlock();
662 if (persistentData.type == PersistentData.TYPE_SP) {
663 PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
664 byte[] pwdToken = computePasswordToken(userCredential, pwd);
665
Adrian Roosb7d69322017-08-08 18:32:03 +0200666 GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
667 0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
Adrian Roos7374d3a2017-03-31 14:14:53 -0700668 return VerifyCredentialResponse.fromGateKeeperResponse(response);
669 } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
670 PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
671 byte[] pwdToken = computePasswordToken(userCredential, pwd);
672 int weaverSlot = persistentData.userId;
673
674 return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
675 } else {
676 Log.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
677 + persistentData.type);
678 return VerifyCredentialResponse.ERROR;
679 }
680 }
681
682
Adrian Roos60dcbbf2017-08-08 16:19:33 +0200683 public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) {
684 if (mStorage.getPersistentDataBlock() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200685 && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) {
Adrian Roos60dcbbf2017-08-08 16:19:33 +0200686 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle,
687 userInfo.id));
688 if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
689 int weaverSlot = loadWeaverSlot(handle, userInfo.id);
690 if (weaverSlot != INVALID_WEAVER_SLOT) {
691 synchronizeWeaverFrpPassword(pwd, requestedQuality, userInfo.id, weaverSlot);
692 } else {
693 synchronizeFrpPassword(pwd, requestedQuality, userInfo.id);
694 }
695 }
696 }
697 }
698
Adrian Roos7374d3a2017-03-31 14:14:53 -0700699 private void synchronizeFrpPassword(PasswordData pwd,
700 int requestedQuality, int userId) {
701 if (mStorage.getPersistentDataBlock() != null
Adrian Roos2adc2632017-09-05 17:01:42 +0200702 && LockPatternUtils.userOwnsFrpCredential(mContext,
703 mUserManager.getUserInfo(userId))) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700704 if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
705 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
706 pwd.toBytes());
707 } else {
708 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
709 }
710 }
711 }
712
713 private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
714 int weaverSlot) {
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_WEAVER, weaverSlot,
720 requestedQuality, pwd.toBytes());
721 } else {
722 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, 0, 0, null);
723 }
724 }
725 }
726
Rubin Xu7b7424b2017-03-31 18:03:20 +0100727 private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
Rubin Xuf095f832017-01-31 15:23:34 +0000728
729 public long createTokenBasedSyntheticPassword(byte[] token, int userId) {
730 long handle = generateHandle();
Rubin Xuf095f832017-01-31 15:23:34 +0000731 if (!tokenMap.containsKey(userId)) {
732 tokenMap.put(userId, new ArrayMap<>());
733 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100734 TokenData tokenData = new TokenData();
735 final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
736 if (isWeaverAvailable()) {
737 tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
738 tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
739 PERSONALISATION_WEAVER_TOKEN, secdiscardable);
740 } else {
741 tokenData.secdiscardableOnDisk = secdiscardable;
742 tokenData.weaverSecret = null;
743 }
744 tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
745
746 tokenMap.get(userId).put(handle, tokenData);
Rubin Xuf095f832017-01-31 15:23:34 +0000747 return handle;
748 }
749
750 public Set<Long> getPendingTokensForUser(int userId) {
751 if (!tokenMap.containsKey(userId)) {
752 return Collections.emptySet();
753 }
754 return tokenMap.get(userId).keySet();
755 }
756
757 public boolean removePendingToken(long handle, int userId) {
758 if (!tokenMap.containsKey(userId)) {
759 return false;
760 }
761 return tokenMap.get(userId).remove(handle) != null;
762 }
763
764 public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
765 int userId) {
766 if (!tokenMap.containsKey(userId)) {
767 return false;
768 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100769 TokenData tokenData = tokenMap.get(userId).get(handle);
770 if (tokenData == null) {
Rubin Xuf095f832017-01-31 15:23:34 +0000771 return false;
772 }
773 if (!loadEscrowData(authToken, userId)) {
774 Log.w(TAG, "User is not escrowable");
775 return false;
776 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100777 if (isWeaverAvailable()) {
778 int slot = getNextAvailableWeaverSlot();
779 try {
Rubin Xua339df02018-04-10 09:44:18 +0100780 Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100781 weaverEnroll(slot, null, tokenData.weaverSecret);
782 } catch (RemoteException e) {
783 Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
784 return false;
785 }
786 saveWeaverSlot(slot, handle, userId);
787 }
788 saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000789 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
Rubin Xu7b7424b2017-03-31 18:03:20 +0100790 tokenData.aggregatedSecret, 0L, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000791 tokenMap.get(userId).remove(handle);
792 return true;
793 }
794
Rubin Xu3bf722a2016-12-15 16:07:38 +0000795 private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
796 byte[] applicationId, long sid, int userId) {
Rubin Xuf095f832017-01-31 15:23:34 +0000797 final byte[] secret;
798 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
799 secret = authToken.computeP0();
800 } else {
801 secret = authToken.syntheticPassword.getBytes();
802 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000803 byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
804 byte[] blob = new byte[content.length + 1 + 1];
805 blob[0] = SYNTHETIC_PASSWORD_VERSION;
806 blob[1] = type;
807 System.arraycopy(content, 0, blob, 2, content.length);
808 saveState(SP_BLOB_NAME, blob, handle, userId);
809 }
810
811 /**
812 * Decrypt a synthetic password by supplying the user credential and corresponding password
813 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
814 * verification to referesh the SID & Auth token maintained by the system.
Rubin Xu16c823e2017-06-27 14:44:58 +0100815 * Note: the credential type is not validated here since there are call sites where the type is
816 * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
Rubin Xu3bf722a2016-12-15 16:07:38 +0000817 */
818 public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
Rubin Xucf326f12017-11-15 11:55:35 +0000819 long handle, String credential, int userId,
820 ICheckCredentialProgressCallback progressCallback) throws RemoteException {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000821 if (credential == null) {
822 credential = DEFAULT_PASSWORD;
823 }
824 AuthenticationResult result = new AuthenticationResult();
825 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
Rubin Xu16c823e2017-06-27 14:44:58 +0100826 result.credentialType = pwd.passwordType;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000827 byte[] pwdToken = computePasswordToken(credential, pwd);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000828
Rubin Xu7b7424b2017-03-31 18:03:20 +0100829 final byte[] applicationId;
Rubin Xu8c528652017-10-31 15:40:32 +0000830 final long sid;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100831 int weaverSlot = loadWeaverSlot(handle, userId);
832 if (weaverSlot != INVALID_WEAVER_SLOT) {
833 // Weaver based user password
834 if (!isWeaverAvailable()) {
835 Log.e(TAG, "No weaver service to unwrap password based SP");
836 result.gkResponse = VerifyCredentialResponse.ERROR;
837 return result;
Rubin Xu3bf722a2016-12-15 16:07:38 +0000838 }
Rubin Xu7b7424b2017-03-31 18:03:20 +0100839 result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
840 if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
841 return result;
842 }
Rubin Xu8c528652017-10-31 15:40:32 +0000843 sid = GateKeeper.INVALID_SECURE_USER_ID;
Rubin Xu7b7424b2017-03-31 18:03:20 +0100844 applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
845 } else {
846 byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
847 GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
848 pwd.passwordHandle, gkPwdToken);
849 int responseCode = response.getResponseCode();
850 if (responseCode == GateKeeperResponse.RESPONSE_OK) {
851 result.gkResponse = VerifyCredentialResponse.OK;
852 if (response.getShouldReEnroll()) {
853 GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
854 pwd.passwordHandle, gkPwdToken, gkPwdToken);
855 if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
856 pwd.passwordHandle = reenrollResponse.getPayload();
857 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700858 synchronizeFrpPassword(pwd,
859 pwd.passwordType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
860 ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
861 : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
862 /* TODO(roosa): keep the same password quality */,
863 userId);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100864 } else {
865 Log.w(TAG, "Fail to re-enroll user password for user " + userId);
866 // continue the flow anyway
867 }
868 }
869 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
870 result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
871 return result;
872 } else {
873 result.gkResponse = VerifyCredentialResponse.ERROR;
874 return result;
875 }
Rubin Xu8c528652017-10-31 15:40:32 +0000876 sid = sidFromPasswordHandle(pwd.passwordHandle);
Rubin Xu7b7424b2017-03-31 18:03:20 +0100877 applicationId = transformUnderSecdiscardable(pwdToken,
878 loadSecdiscardable(handle, userId));
Rubin Xu3bf722a2016-12-15 16:07:38 +0000879 }
Rubin Xucf326f12017-11-15 11:55:35 +0000880 // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
881 // Notify the callback so the keyguard UI can proceed immediately.
882 if (progressCallback != null) {
883 progressCallback.onCredentialVerified();
884 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000885 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
Rubin Xu8c528652017-10-31 15:40:32 +0000886 applicationId, sid, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +0000887
888 // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
889 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
890 return result;
891 }
892
Rubin Xuf095f832017-01-31 15:23:34 +0000893 /**
894 * Decrypt a synthetic password by supplying an escrow token and corresponding token
895 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
896 * verification to referesh the SID & Auth token maintained by the system.
897 */
898 public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
899 IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
900 throws RemoteException {
901 AuthenticationResult result = new AuthenticationResult();
Rubin Xu7b7424b2017-03-31 18:03:20 +0100902 byte[] secdiscardable = loadSecdiscardable(handle, userId);
903 int slotId = loadWeaverSlot(handle, userId);
904 if (slotId != INVALID_WEAVER_SLOT) {
905 if (!isWeaverAvailable()) {
906 Log.e(TAG, "No weaver service to unwrap token based SP");
907 result.gkResponse = VerifyCredentialResponse.ERROR;
908 return result;
909 }
910 VerifyCredentialResponse response = weaverVerify(slotId, null);
911 if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
912 response.getPayload() == null) {
913 Log.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
914 result.gkResponse = VerifyCredentialResponse.ERROR;
915 return result;
916 }
917 secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
918 PERSONALISATION_WEAVER_TOKEN, secdiscardable);
919 }
920 byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
Rubin Xuf095f832017-01-31 15:23:34 +0000921 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
Rubin Xu8c528652017-10-31 15:40:32 +0000922 applicationId, 0L, userId);
Rubin Xuf095f832017-01-31 15:23:34 +0000923 if (result.authToken != null) {
924 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
925 if (result.gkResponse == null) {
926 // The user currently has no password. return OK with null payload so null
927 // is propagated to unlockUser()
928 result.gkResponse = VerifyCredentialResponse.OK;
929 }
930 } else {
931 result.gkResponse = VerifyCredentialResponse.ERROR;
932 }
933 return result;
934 }
935
Rubin Xu3bf722a2016-12-15 16:07:38 +0000936 private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
Rubin Xu8c528652017-10-31 15:40:32 +0000937 byte[] applicationId, long sid, int userId) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000938 byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
939 if (blob == null) {
940 return null;
941 }
Rubin Xu8c528652017-10-31 15:40:32 +0000942 final byte version = blob[0];
943 if (version != SYNTHETIC_PASSWORD_VERSION && version != SYNTHETIC_PASSWORD_VERSION_V1) {
Rubin Xu3bf722a2016-12-15 16:07:38 +0000944 throw new RuntimeException("Unknown blob version");
945 }
946 if (blob[1] != type) {
947 throw new RuntimeException("Invalid blob type");
948 }
Rubin Xu8c528652017-10-31 15:40:32 +0000949 final byte[] secret;
950 if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
951 secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
952 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
953 } else {
954 secret = decryptSPBlob(getHandleName(handle),
Rubin Xu3bf722a2016-12-15 16:07:38 +0000955 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
Rubin Xu8c528652017-10-31 15:40:32 +0000956 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000957 if (secret == null) {
958 Log.e(TAG, "Fail to decrypt SP for user " + userId);
959 return null;
960 }
961 AuthenticationToken result = new AuthenticationToken();
Rubin Xuf095f832017-01-31 15:23:34 +0000962 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
963 if (!loadEscrowData(result, userId)) {
964 Log.e(TAG, "User is not escrowable: " + userId);
965 return null;
966 }
967 result.recreate(secret);
968 } else {
969 result.syntheticPassword = new String(secret);
970 }
Rubin Xu8c528652017-10-31 15:40:32 +0000971 if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
972 Log.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
973 createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
974 }
Rubin Xu3bf722a2016-12-15 16:07:38 +0000975 return result;
976 }
977
978 /**
979 * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
980 * if required.
981 *
982 * Normally performing verifyChallenge with an AuthenticationToken should always return
983 * RESPONSE_OK, since user authentication failures are detected earlier when trying to
984 * decrypt SP.
985 */
Rubin Xu8b30ec32017-03-05 00:47:09 +0000986 public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
Rubin Xu3bf722a2016-12-15 16:07:38 +0000987 @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
988 byte[] spHandle = loadSyntheticPasswordHandle(userId);
989 if (spHandle == null) {
990 // There is no password handle associated with the given user, i.e. the user is not
991 // secured by lockscreen and has no SID, so just return here;
992 return null;
993 }
994 VerifyCredentialResponse result;
995 GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
996 spHandle, auth.deriveGkPassword());
997 int responseCode = response.getResponseCode();
998 if (responseCode == GateKeeperResponse.RESPONSE_OK) {
999 result = new VerifyCredentialResponse(response.getPayload());
1000 if (response.getShouldReEnroll()) {
1001 response = gatekeeper.enroll(userId, spHandle,
1002 spHandle, auth.deriveGkPassword());
1003 if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
1004 spHandle = response.getPayload();
1005 saveSyntheticPasswordHandle(spHandle, userId);
1006 // Call self again to re-verify with updated handle
1007 return verifyChallenge(gatekeeper, auth, challenge, userId);
1008 } else {
1009 Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
1010 // Fall through, return existing handle
1011 }
1012 }
1013 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
1014 result = new VerifyCredentialResponse(response.getTimeout());
1015 } else {
1016 result = VerifyCredentialResponse.ERROR;
1017 }
1018 return result;
1019 }
1020
1021 public boolean existsHandle(long handle, int userId) {
1022 return hasState(SP_BLOB_NAME, handle, userId);
1023 }
1024
Rubin Xuf095f832017-01-31 15:23:34 +00001025 public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
1026 destroySyntheticPassword(handle, userId);
Rubin Xuaa32d152017-04-27 17:01:05 +01001027 destroyState(SECDISCARDABLE_NAME, handle, userId);
Rubin Xuf095f832017-01-31 15:23:34 +00001028 }
1029
Rubin Xu3bf722a2016-12-15 16:07:38 +00001030 public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
1031 destroySyntheticPassword(handle, userId);
Rubin Xuaa32d152017-04-27 17:01:05 +01001032 destroyState(SECDISCARDABLE_NAME, handle, userId);
1033 destroyState(PASSWORD_DATA_NAME, handle, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001034 }
1035
1036 private void destroySyntheticPassword(long handle, int userId) {
Rubin Xuaa32d152017-04-27 17:01:05 +01001037 destroyState(SP_BLOB_NAME, handle, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001038 destroySPBlobKey(getHandleName(handle));
Rubin Xu7b7424b2017-03-31 18:03:20 +01001039 if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
1040 destroyWeaverSlot(handle, userId);
1041 }
1042 }
1043
1044 private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
1045 byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash(
1046 PERSONALISATION_WEAVER_PASSWORD, secret);
1047 byte[] result = new byte[data.length + weaverSecret.length];
1048 System.arraycopy(data, 0, result, 0, data.length);
1049 System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length);
1050 return result;
Rubin Xu3bf722a2016-12-15 16:07:38 +00001051 }
1052
1053 private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
1054 byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
1055 PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
1056 byte[] result = new byte[data.length + secdiscardable.length];
1057 System.arraycopy(data, 0, result, 0, data.length);
1058 System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
1059 return result;
1060 }
1061
1062 private byte[] createSecdiscardable(long handle, int userId) {
1063 byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
Rubin Xu7b7424b2017-03-31 18:03:20 +01001064 saveSecdiscardable(handle, data, userId);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001065 return data;
1066 }
1067
Rubin Xu7b7424b2017-03-31 18:03:20 +01001068 private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
1069 saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
1070 }
1071
Rubin Xu3bf722a2016-12-15 16:07:38 +00001072 private byte[] loadSecdiscardable(long handle, int userId) {
1073 return loadState(SECDISCARDABLE_NAME, handle, userId);
1074 }
1075
1076 private boolean hasState(String stateName, long handle, int userId) {
1077 return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
1078 }
1079
1080 private byte[] loadState(String stateName, long handle, int userId) {
1081 return mStorage.readSyntheticPasswordState(userId, handle, stateName);
1082 }
1083
1084 private void saveState(String stateName, byte[] data, long handle, int userId) {
1085 mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
1086 }
1087
Rubin Xuaa32d152017-04-27 17:01:05 +01001088 private void destroyState(String stateName, long handle, int userId) {
1089 mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
Rubin Xu3bf722a2016-12-15 16:07:38 +00001090 }
1091
1092 protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
1093 return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
1094 }
1095
1096 protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
1097 return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
1098 }
1099
1100 protected void destroySPBlobKey(String keyAlias) {
1101 SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
1102 }
1103
1104 public static long generateHandle() {
1105 SecureRandom rng = new SecureRandom();
1106 long result;
1107 do {
1108 result = rng.nextLong();
1109 } while (result == DEFAULT_HANDLE);
1110 return result;
1111 }
1112
1113 private int fakeUid(int uid) {
1114 return 100000 + uid;
1115 }
1116
1117 protected static byte[] secureRandom(int length) {
1118 try {
1119 return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
1120 } catch (NoSuchAlgorithmException e) {
1121 e.printStackTrace();
1122 return null;
1123 }
1124 }
1125
1126 private String getHandleName(long handle) {
1127 return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
1128 }
1129
1130 private byte[] computePasswordToken(String password, PasswordData data) {
1131 return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
1132 PASSWORD_TOKEN_LENGTH);
1133 }
1134
1135 private byte[] passwordTokenToGkInput(byte[] token) {
1136 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
1137 }
1138
Rubin Xu7b7424b2017-03-31 18:03:20 +01001139 private byte[] passwordTokenToWeaverKey(byte[] token) {
1140 byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
1141 if (key.length < mWeaverConfig.keySize) {
1142 throw new RuntimeException("weaver key length too small");
1143 }
1144 return Arrays.copyOf(key, mWeaverConfig.keySize);
1145 }
1146
Rubin Xu3bf722a2016-12-15 16:07:38 +00001147 protected long sidFromPasswordHandle(byte[] handle) {
1148 return nativeSidFromPasswordHandle(handle);
1149 }
1150
1151 protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
1152 return nativeScrypt(password.getBytes(), salt, N, r, p, outLen);
1153 }
1154
1155 native long nativeSidFromPasswordHandle(byte[] handle);
1156 native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen);
1157
Rubin Xu7b7424b2017-03-31 18:03:20 +01001158 protected static ArrayList<Byte> toByteArrayList(byte[] data) {
1159 ArrayList<Byte> result = new ArrayList<Byte>(data.length);
1160 for (int i = 0; i < data.length; i++) {
1161 result.add(data[i]);
1162 }
1163 return result;
1164 }
1165
1166 protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
1167 byte[] result = new byte[data.size()];
1168 for (int i = 0; i < data.size(); i++) {
1169 result[i] = data.get(i);
1170 }
1171 return result;
1172 }
1173
Rubin Xu3bf722a2016-12-15 16:07:38 +00001174 final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
1175 public static String bytesToHex(byte[] bytes) {
1176 if (bytes == null) {
1177 return "null";
1178 }
1179 char[] hexChars = new char[bytes.length * 2];
1180 for ( int j = 0; j < bytes.length; j++ ) {
1181 int v = bytes[j] & 0xFF;
1182 hexChars[j * 2] = hexArray[v >>> 4];
1183 hexChars[j * 2 + 1] = hexArray[v & 0x0F];
1184 }
1185 return new String(hexChars);
1186 }
1187}