1 /*
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  */
16 
17 package com.android.server.locksettings;
18 
19 import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.app.admin.PasswordMetrics;
24 import android.content.Context;
25 import android.content.pm.UserInfo;
26 import android.hardware.weaver.V1_0.IWeaver;
27 import android.hardware.weaver.V1_0.WeaverConfig;
28 import android.hardware.weaver.V1_0.WeaverReadResponse;
29 import android.hardware.weaver.V1_0.WeaverReadStatus;
30 import android.hardware.weaver.V1_0.WeaverStatus;
31 import android.os.RemoteException;
32 import android.os.UserManager;
33 import android.security.GateKeeper;
34 import android.security.Scrypt;
35 import android.service.gatekeeper.GateKeeperResponse;
36 import android.service.gatekeeper.IGateKeeperService;
37 import android.util.ArrayMap;
38 import android.util.Slog;
39 
40 import com.android.internal.annotations.VisibleForTesting;
41 import com.android.internal.util.ArrayUtils;
42 import com.android.internal.widget.ICheckCredentialProgressCallback;
43 import com.android.internal.widget.LockPatternUtils;
44 import com.android.internal.widget.LockscreenCredential;
45 import com.android.internal.widget.VerifyCredentialResponse;
46 import com.android.server.locksettings.LockSettingsStorage.PersistentData;
47 
48 import libcore.util.HexEncoding;
49 
50 import java.nio.ByteBuffer;
51 import java.security.NoSuchAlgorithmException;
52 import java.security.SecureRandom;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.Collections;
56 import java.util.HashSet;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.NoSuchElementException;
60 import java.util.Objects;
61 import java.util.Set;
62 
63 
64 /**
65  * A class that maintains the wrapping of synthetic password by user credentials or escrow tokens.
66  * It's (mostly) a pure storage for synthetic passwords, providing APIs to creating and destroying
67  * synthetic password blobs which are wrapped by user credentials or escrow tokens.
68  *
69  * Here is the assumptions it makes:
70  *   Each user has one single synthetic password at any time.
71  *   The SP has an associated password handle, which binds to the SID for that user. The password
72  *   handle is persisted by SyntheticPasswordManager internally.
73  *   If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD
74  *
75  * Information persisted on disk:
76  *   for each user (stored under DEFAULT_HANDLE):
77  *     SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user
78  *                     credential exists, cleared when user clears their credential.
79  *     SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combined with escrow
80  *                     tokens. Destroyed when escrow support is turned off for the given user.
81  *
82  *     for each SP blob under the user (stored under the corresponding handle):
83  *       SP_BLOB_NAME: The encrypted synthetic password. Always exists.
84  *       PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP.
85  *       SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME for the
86  *                            purpose of secure deletion. Exists if this is a non-weaver SP
87  *                            (both password and token based), or it's a token-based SP under weaver.
88  *       WEAVER_SLOT: Metadata about the weaver slot used. Only exists if this is a SP under weaver.
89  *
90  *
91  */
92 public class SyntheticPasswordManager {
93     private static final String SP_BLOB_NAME = "spblob";
94     private static final String SP_E0_NAME = "e0";
95     private static final String SP_P1_NAME = "p1";
96     private static final String SP_HANDLE_NAME = "handle";
97     private static final String SECDISCARDABLE_NAME = "secdis";
98     private static final int SECDISCARDABLE_LENGTH = 16 * 1024;
99     private static final String PASSWORD_DATA_NAME = "pwd";
100     private static final String WEAVER_SLOT_NAME = "weaver";
101     private static final String PASSWORD_METRICS_NAME = "metrics";
102 
103     public static final long DEFAULT_HANDLE = 0L;
104     private static final byte[] DEFAULT_PASSWORD = "default-password".getBytes();
105 
106     private static final byte WEAVER_VERSION = 1;
107     private static final int INVALID_WEAVER_SLOT = -1;
108 
109     private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1;
110     private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2;
111     private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3;
112     private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
113     private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
114 
115     // 256-bit synthetic password
116     private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
117 
118     private static final int PASSWORD_SCRYPT_N = 11;
119     private static final int PASSWORD_SCRYPT_R = 3;
120     private static final int PASSWORD_SCRYPT_P = 1;
121     private static final int PASSWORD_SALT_LENGTH = 16;
122     private static final int PASSWORD_TOKEN_LENGTH = 32;
123     private static final String TAG = "SyntheticPasswordManager";
124 
125     private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
126     private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes();
127     private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();
128     private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
129     private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
130     private static final byte[] PERSONALIZATION_AUTHSECRET_KEY = "authsecret-hal".getBytes();
131     private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
132     private static final byte[] PERSONALIZATION_PASSWORD_HASH = "pw-hash".getBytes();
133     private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
134     private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
135     private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
136     private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
137     private static final byte[] PERSONALIZATION_PASSWORD_METRICS = "password-metrics".getBytes();
138     private static final byte[] PERSONALISATION_CONTEXT =
139         "android-synthetic-password-personalization-context".getBytes();
140 
141     static class AuthenticationResult {
142         // Non-null if password/token passes verification, null otherwise
143         @Nullable public AuthenticationToken authToken;
144         // OK:    password / token passes verification, user has a lockscreen
145         // null:  user does not have a lockscreen (but password / token passes verification)
146         // ERROR: password / token fails verification
147         // RETRY: password / token verification is throttled at the moment.
148         @Nullable public VerifyCredentialResponse gkResponse;
149     }
150 
151     /**
152      * This class represents the master cryptographic secret for a given user (a.k.a synthietic
153      * password). This secret is derived from the user's lockscreen credential or password escrow
154      * token. All other cryptograhic keys related to the user, including disk encryption key,
155      * keystore encryption key, gatekeeper auth key, vendor auth secret and others are directly
156      * derived from this token.
157      * <p>
158      * The master secret associated with an authentication token is retrievable from
159      * {@link AuthenticationToken#getSyntheticPassword()} and the authentication token can be
160      * reconsturcted from the master secret later with
161      * {@link AuthenticationToken#recreateDirectly(byte[])}. The first time an authentication token
162      * is needed, it should be created with {@link AuthenticationToken#create()} so that the
163      * necessary escrow data ({@link #mEncryptedEscrowSplit0} and {@link #mEscrowSplit1}) is
164      * properly initialized. The caller can either persist the (non-secret) esscrow data if escrow
165      * is required, or discard it to cryptograhically disable escrow. To support escrow, the caller
166      * needs to securely store the secret returned from
167      * {@link AuthenticationToken#getEscrowSecret()}, and at the time of use, load the escrow data
168      * back with {@link AuthenticationToken#setEscrowData(byte[], byte[])} and then re-create the
169      * master secret from the escrow secret via
170      * {@link AuthenticationToken#recreateFromEscrow(byte[])}.
171      */
172     static class AuthenticationToken {
173         private final byte mVersion;
174         /**
175          * Here is the relationship between these fields:
176          * Generate two random block P0 and P1. P1 is recorded in mEscrowSplit1 but P0 is not.
177          * mSyntheticPassword = hash(P0 || P1)
178          * E0 = P0 encrypted under syntheticPassword, recoreded in mEncryptedEscrowSplit0.
179          */
180         private @NonNull byte[] mSyntheticPassword;
181         private @Nullable byte[] mEncryptedEscrowSplit0;
182         private @Nullable byte[] mEscrowSplit1;
183 
AuthenticationToken(byte version)184         AuthenticationToken(byte version) {
185             mVersion = version;
186         }
187 
derivePassword(byte[] personalization)188         private byte[] derivePassword(byte[] personalization) {
189             if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
190                 return (new SP800Derive(mSyntheticPassword))
191                     .withContext(personalization, PERSONALISATION_CONTEXT);
192             } else {
193                 return SyntheticPasswordCrypto.personalisedHash(personalization,
194                         mSyntheticPassword);
195             }
196         }
197 
deriveKeyStorePassword()198         public byte[] deriveKeyStorePassword() {
199             return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD));
200         }
201 
deriveGkPassword()202         public byte[] deriveGkPassword() {
203             return derivePassword(PERSONALIZATION_SP_GK_AUTH);
204         }
205 
deriveDiskEncryptionKey()206         public byte[] deriveDiskEncryptionKey() {
207             return derivePassword(PERSONALIZATION_FBE_KEY);
208         }
209 
deriveVendorAuthSecret()210         public byte[] deriveVendorAuthSecret() {
211             return derivePassword(PERSONALIZATION_AUTHSECRET_KEY);
212         }
213 
derivePasswordHashFactor()214         public byte[] derivePasswordHashFactor() {
215             return derivePassword(PERSONALIZATION_PASSWORD_HASH);
216         }
217 
218         /** Derives key used to encrypt password metrics */
deriveMetricsKey()219         public byte[] deriveMetricsKey() {
220             return derivePassword(PERSONALIZATION_PASSWORD_METRICS);
221         }
222 
223         /**
224          * Assign escrow data to this auth token. This is a prerequisite to call
225          * {@link AuthenticationToken#recreateFromEscrow}.
226          */
setEscrowData(@ullable byte[] encryptedEscrowSplit0, @Nullable byte[] escrowSplit1)227         public void setEscrowData(@Nullable byte[] encryptedEscrowSplit0,
228                 @Nullable byte[] escrowSplit1) {
229             mEncryptedEscrowSplit0 = encryptedEscrowSplit0;
230             mEscrowSplit1 = escrowSplit1;
231         }
232 
233         /**
234          * Re-creates authentication token from escrow secret (escrowSplit0, returned from
235          * {@link AuthenticationToken#getEscrowSecret}). Escrow data needs to be loaded
236          * by {@link #setEscrowData} before calling this.
237          */
recreateFromEscrow(byte[] escrowSplit0)238         public void recreateFromEscrow(byte[] escrowSplit0) {
239             Objects.requireNonNull(mEscrowSplit1);
240             Objects.requireNonNull(mEncryptedEscrowSplit0);
241             recreate(escrowSplit0, mEscrowSplit1);
242         }
243 
244         /**
245          * Re-creates authentication token from synthetic password directly.
246          */
recreateDirectly(byte[] syntheticPassword)247         public void recreateDirectly(byte[] syntheticPassword) {
248             this.mSyntheticPassword = Arrays.copyOf(syntheticPassword, syntheticPassword.length);
249         }
250 
251         /**
252          * Generates a new random synthetic password with escrow data.
253          */
create()254         static AuthenticationToken create() {
255             AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3);
256             byte[] escrowSplit0 = secureRandom(SYNTHETIC_PASSWORD_LENGTH);
257             byte[] escrowSplit1 = secureRandom(SYNTHETIC_PASSWORD_LENGTH);
258             result.recreate(escrowSplit0, escrowSplit1);
259             byte[] encrypteEscrowSplit0 = SyntheticPasswordCrypto.encrypt(result.mSyntheticPassword,
260                     PERSONALIZATION_E0, escrowSplit0);
261             result.setEscrowData(encrypteEscrowSplit0,  escrowSplit1);
262             return result;
263         }
264 
265         /**
266          * Re-creates synthetic password from both escrow splits. See javadoc for
267          * AuthenticationToken.mSyntheticPassword for details on what each block means.
268          */
recreate(byte[] escrowSplit0, byte[] escrowSplit1)269         private void recreate(byte[] escrowSplit0, byte[] escrowSplit1) {
270             mSyntheticPassword = String.valueOf(HexEncoding.encode(
271                     SyntheticPasswordCrypto.personalisedHash(
272                             PERSONALIZATION_SP_SPLIT, escrowSplit0, escrowSplit1))).getBytes();
273         }
274 
275         /**
276          * Returns the escrow secret that can be used later to reconstruct this authentication
277          * token from {@link #recreateFromEscrow(byte[])}. Only possible if escrow is not disabled
278          * (encryptedEscrowSplit0 known).
279          */
getEscrowSecret()280         public byte[] getEscrowSecret() {
281             if (mEncryptedEscrowSplit0 == null) {
282                 return null;
283             }
284             return SyntheticPasswordCrypto.decrypt(mSyntheticPassword, PERSONALIZATION_E0,
285                     mEncryptedEscrowSplit0);
286         }
287 
288         /**
289          * Returns the raw synthetic password that can be used later to reconstruct this
290          * authentication token from {@link #recreateDirectly(byte[])}
291          */
getSyntheticPassword()292         public byte[] getSyntheticPassword() {
293             return mSyntheticPassword;
294         }
295 
296         /**
297          * Returns the version of this AuthenticationToken for use with reconstructing
298          * this with a synthetic password version.
299          */
getVersion()300         public byte getVersion() {
301             return mVersion;
302         }
303     }
304 
305     static class PasswordData {
306         byte scryptN;
307         byte scryptR;
308         byte scryptP;
309         public int credentialType;
310         byte[] salt;
311         // For GateKeeper-based credential, this is the password handle returned by GK,
312         // for weaver-based credential, this is empty.
313         public byte[] passwordHandle;
314 
create(int passwordType)315         public static PasswordData create(int passwordType) {
316             PasswordData result = new PasswordData();
317             result.scryptN = PASSWORD_SCRYPT_N;
318             result.scryptR = PASSWORD_SCRYPT_R;
319             result.scryptP = PASSWORD_SCRYPT_P;
320             result.credentialType = passwordType;
321             result.salt = secureRandom(PASSWORD_SALT_LENGTH);
322             return result;
323         }
324 
fromBytes(byte[] data)325         public static PasswordData fromBytes(byte[] data) {
326             PasswordData result = new PasswordData();
327             ByteBuffer buffer = ByteBuffer.allocate(data.length);
328             buffer.put(data, 0, data.length);
329             buffer.flip();
330             result.credentialType = buffer.getInt();
331             result.scryptN = buffer.get();
332             result.scryptR = buffer.get();
333             result.scryptP = buffer.get();
334             int saltLen = buffer.getInt();
335             result.salt = new byte[saltLen];
336             buffer.get(result.salt);
337             int handleLen = buffer.getInt();
338             if (handleLen > 0) {
339                 result.passwordHandle = new byte[handleLen];
340                 buffer.get(result.passwordHandle);
341             } else {
342                 result.passwordHandle = null;
343             }
344             return result;
345         }
346 
toBytes()347         public byte[] toBytes() {
348 
349             ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
350                     + Integer.BYTES + salt.length + Integer.BYTES +
351                     (passwordHandle != null ? passwordHandle.length : 0));
352             buffer.putInt(credentialType);
353             buffer.put(scryptN);
354             buffer.put(scryptR);
355             buffer.put(scryptP);
356             buffer.putInt(salt.length);
357             buffer.put(salt);
358             if (passwordHandle != null && passwordHandle.length > 0) {
359                 buffer.putInt(passwordHandle.length);
360                 buffer.put(passwordHandle);
361             } else {
362                 buffer.putInt(0);
363             }
364             return buffer.array();
365         }
366     }
367 
368     static class TokenData {
369         byte[] secdiscardableOnDisk;
370         byte[] weaverSecret;
371         byte[] aggregatedSecret;
372         EscrowTokenStateChangeCallback mCallback;
373     }
374 
375     private final Context mContext;
376     private LockSettingsStorage mStorage;
377     private IWeaver mWeaver;
378     private WeaverConfig mWeaverConfig;
379     private PasswordSlotManager mPasswordSlotManager;
380 
381     private final UserManager mUserManager;
382 
SyntheticPasswordManager(Context context, LockSettingsStorage storage, UserManager userManager, PasswordSlotManager passwordSlotManager)383     public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
384             UserManager userManager, PasswordSlotManager passwordSlotManager) {
385         mContext = context;
386         mStorage = storage;
387         mUserManager = userManager;
388         mPasswordSlotManager = passwordSlotManager;
389     }
390 
391     @VisibleForTesting
getWeaverService()392     protected IWeaver getWeaverService() throws RemoteException {
393         try {
394             return IWeaver.getService(/* retry */ true);
395         } catch (NoSuchElementException e) {
396             Slog.i(TAG, "Device does not support weaver");
397             return null;
398         }
399     }
400 
initWeaverService()401     public synchronized void initWeaverService() {
402         if (mWeaver != null) {
403             return;
404         }
405         try {
406             mWeaverConfig = null;
407             mWeaver = getWeaverService();
408             if (mWeaver != null) {
409                 mWeaver.getConfig((int status, WeaverConfig config) -> {
410                     if (status == WeaverStatus.OK && config.slots > 0) {
411                         mWeaverConfig = config;
412                     } else {
413                         Slog.e(TAG, "Failed to get weaver config, status " + status
414                                 + " slots: " + config.slots);
415                         mWeaver = null;
416                     }
417                 });
418                 mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots());
419             }
420         } catch (RemoteException e) {
421             Slog.e(TAG, "Failed to get weaver service", e);
422         }
423     }
424 
isWeaverAvailable()425     private synchronized boolean isWeaverAvailable() {
426         if (mWeaver == null) {
427             //Re-initializing weaver in case there was a transient error preventing access to it.
428             initWeaverService();
429         }
430         return mWeaver != null && mWeaverConfig.slots > 0;
431     }
432 
433     /**
434      * Enroll the given key value pair into the specified weaver slot. if the given key is null,
435      * a default all-zero key is used. If the value is not specified, a fresh random secret is
436      * generated as the value.
437      *
438      * @return the value stored in the weaver slot, or null if the operation fails
439      */
weaverEnroll(int slot, byte[] key, @Nullable byte[] value)440     private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) {
441         if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
442             throw new IllegalArgumentException("Invalid slot for weaver");
443         }
444         if (key == null) {
445             key = new byte[mWeaverConfig.keySize];
446         } else if (key.length != mWeaverConfig.keySize) {
447             throw new IllegalArgumentException("Invalid key size for weaver");
448         }
449         if (value == null) {
450             value = secureRandom(mWeaverConfig.valueSize);
451         }
452         try {
453             int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
454             if (writeStatus != WeaverStatus.OK) {
455                 Slog.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
456                 return null;
457             }
458         } catch (RemoteException e) {
459             Slog.e(TAG, "weaver write failed", e);
460             return null;
461         }
462         return value;
463     }
464 
465     /**
466      * Verify the supplied key against a weaver slot, returning a response indicating whether
467      * the verification is successful, throttled or failed. If successful, the bound secret
468      * is also returned.
469      */
weaverVerify(int slot, byte[] key)470     private VerifyCredentialResponse weaverVerify(int slot, byte[] key) {
471         if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
472             throw new IllegalArgumentException("Invalid slot for weaver");
473         }
474         if (key == null) {
475             key = new byte[mWeaverConfig.keySize];
476         } else if (key.length != mWeaverConfig.keySize) {
477             throw new IllegalArgumentException("Invalid key size for weaver");
478         }
479         final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
480         try {
481             mWeaver.read(slot, toByteArrayList(key),
482                     (int status, WeaverReadResponse readResponse) -> {
483                     switch (status) {
484                         case WeaverReadStatus.OK:
485                             response[0] = new VerifyCredentialResponse(
486                                     fromByteArrayList(readResponse.value));
487                             break;
488                         case WeaverReadStatus.THROTTLE:
489                             response[0] = new VerifyCredentialResponse(readResponse.timeout);
490                             Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
491                             break;
492                         case WeaverReadStatus.INCORRECT_KEY:
493                             if (readResponse.timeout == 0) {
494                                 response[0] = VerifyCredentialResponse.ERROR;
495                                 Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
496                             } else {
497                                 response[0] = new VerifyCredentialResponse(readResponse.timeout);
498                                 Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: "
499                                         + slot);
500                             }
501                             break;
502                         case WeaverReadStatus.FAILED:
503                             response[0] = VerifyCredentialResponse.ERROR;
504                             Slog.e(TAG, "weaver read failed (FAILED), slot: " + slot);
505                             break;
506                         default:
507                             response[0] = VerifyCredentialResponse.ERROR;
508                             Slog.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
509                             break;
510                     }
511                 });
512         } catch (RemoteException e) {
513             response[0] = VerifyCredentialResponse.ERROR;
514             Slog.e(TAG, "weaver read failed, slot: " + slot, e);
515         }
516         return response[0];
517     }
518 
removeUser(int userId)519     public void removeUser(int userId) {
520         for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
521             destroyWeaverSlot(handle, userId);
522             destroySPBlobKey(getHandleName(handle));
523         }
524     }
525 
getCredentialType(long handle, int userId)526     int getCredentialType(long handle, int userId) {
527         byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
528         if (passwordData == null) {
529             Slog.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
530             return LockPatternUtils.CREDENTIAL_TYPE_NONE;
531         }
532         return PasswordData.fromBytes(passwordData).credentialType;
533     }
534 
getFrpCredentialType(byte[] payload)535     static int getFrpCredentialType(byte[] payload) {
536         if (payload == null) {
537             return LockPatternUtils.CREDENTIAL_TYPE_NONE;
538         }
539         return PasswordData.fromBytes(payload).credentialType;
540     }
541 
542     /**
543      * Initializing a new Authentication token, possibly from an existing credential and hash.
544      *
545      * The authentication token would bear a randomly-generated synthetic password.
546      *
547      * This method has the side effect of rebinding the SID of the given user to the
548      * newly-generated SP.
549      *
550      * If the existing credential hash is non-null, the existing SID mill be migrated so
551      * the synthetic password in the authentication token will produce the same SID
552      * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
553      * in a per-user data storage.)
554      *
555      * If the existing credential hash is null, it means the given user should have no SID so
556      * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
557      * the supplied credential parameter is also ignored.
558      *
559      * Also saves the escrow information necessary to re-generate the synthetic password under
560      * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
561      * password escrow should be disabled completely on the given user.
562      *
563      */
newSyntheticPasswordAndSid(IGateKeeperService gatekeeper, byte[] hash, LockscreenCredential credential, int userId)564     public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
565             byte[] hash, LockscreenCredential credential, int userId) {
566         AuthenticationToken result = AuthenticationToken.create();
567         GateKeeperResponse response;
568         if (hash != null) {
569             try {
570                 response = gatekeeper.enroll(userId, hash, credential.getCredential(),
571                         result.deriveGkPassword());
572             } catch (RemoteException e) {
573                 throw new IllegalStateException("Failed to enroll credential duing SP init", e);
574             }
575             if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
576                 Slog.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
577                 clearSidForUser(userId);
578             } else {
579                 saveSyntheticPasswordHandle(response.getPayload(), userId);
580             }
581         } else {
582             clearSidForUser(userId);
583         }
584         saveEscrowData(result, userId);
585         return result;
586     }
587 
588     /**
589      * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
590      * Used when adding password to previously-unsecured devices.
591      */
newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken, int userId)592     public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
593             int userId) {
594         GateKeeperResponse response;
595         try {
596             response = gatekeeper.enroll(userId, null, null, authToken.deriveGkPassword());
597         } catch (RemoteException e) {
598             throw new IllegalStateException("Failed to create new SID for user", e);
599         }
600         if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
601             throw new IllegalStateException("Fail to create new SID for user " + userId
602                     + " response: " + response.getResponseCode());
603         }
604         saveSyntheticPasswordHandle(response.getPayload(), userId);
605     }
606 
607     // Nuke the SP handle (and as a result, its SID) for the given user.
clearSidForUser(int userId)608     public void clearSidForUser(int userId) {
609         destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
610     }
611 
hasSidForUser(int userId)612     public boolean hasSidForUser(int userId) {
613         return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
614     }
615 
616     // if null, it means there is no SID associated with the user
617     // This can happen if the user is migrated to SP but currently
618     // do not have a lockscreen password.
loadSyntheticPasswordHandle(int userId)619     private byte[] loadSyntheticPasswordHandle(int userId) {
620         return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
621     }
622 
saveSyntheticPasswordHandle(byte[] spHandle, int userId)623     private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
624         saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
625     }
626 
loadEscrowData(AuthenticationToken authToken, int userId)627     private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
628         byte[] e0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
629         byte[] p1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
630         authToken.setEscrowData(e0,  p1);
631         return e0 != null && p1 != null;
632     }
633 
saveEscrowData(AuthenticationToken authToken, int userId)634     private void saveEscrowData(AuthenticationToken authToken, int userId) {
635         saveState(SP_E0_NAME, authToken.mEncryptedEscrowSplit0, DEFAULT_HANDLE, userId);
636         saveState(SP_P1_NAME, authToken.mEscrowSplit1, DEFAULT_HANDLE, userId);
637     }
638 
hasEscrowData(int userId)639     public boolean hasEscrowData(int userId) {
640         return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
641                 && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
642     }
643 
destroyEscrowData(int userId)644     public void destroyEscrowData(int userId) {
645         destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
646         destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
647     }
648 
loadWeaverSlot(long handle, int userId)649     private int loadWeaverSlot(long handle, int userId) {
650         final int LENGTH = Byte.BYTES + Integer.BYTES;
651         byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId);
652         if (data == null || data.length != LENGTH) {
653             return INVALID_WEAVER_SLOT;
654         }
655         ByteBuffer buffer = ByteBuffer.allocate(LENGTH);
656         buffer.put(data, 0, data.length);
657         buffer.flip();
658         if (buffer.get() != WEAVER_VERSION) {
659             Slog.e(TAG, "Invalid weaver slot version of handle " + handle);
660             return INVALID_WEAVER_SLOT;
661         }
662         return buffer.getInt();
663     }
664 
saveWeaverSlot(int slot, long handle, int userId)665     private void saveWeaverSlot(int slot, long handle, int userId) {
666         ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
667         buffer.put(WEAVER_VERSION);
668         buffer.putInt(slot);
669         saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
670     }
671 
destroyWeaverSlot(long handle, int userId)672     private void destroyWeaverSlot(long handle, int userId) {
673         int slot = loadWeaverSlot(handle, userId);
674         destroyState(WEAVER_SLOT_NAME, handle, userId);
675         if (slot != INVALID_WEAVER_SLOT) {
676             Set<Integer> usedSlots = getUsedWeaverSlots();
677             if (!usedSlots.contains(slot)) {
678                 Slog.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
679                 weaverEnroll(slot, null, null);
680                 mPasswordSlotManager.markSlotDeleted(slot);
681             } else {
682                 Slog.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
683             }
684         }
685     }
686 
687     /**
688      * Return the set of weaver slots that are currently in use by all users on the device.
689      * <p>
690      * <em>Note:</em> Users who are in the process of being deleted are not tracked here
691      * (due to them being marked as partial in UserManager so not visible from
692      * {@link UserManager#getUsers}). As a result their weaver slots will not be considered
693      * taken and can be reused by new users. Care should be taken when cleaning up the
694      * deleted user in {@link #removeUser}, to prevent a reused slot from being erased
695      * unintentionally.
696      */
getUsedWeaverSlots()697     private Set<Integer> getUsedWeaverSlots() {
698         Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
699                 WEAVER_SLOT_NAME);
700         HashSet<Integer> slots = new HashSet<>();
701         for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) {
702             for (Long handle : entry.getValue()) {
703                 int slot = loadWeaverSlot(handle, entry.getKey());
704                 slots.add(slot);
705             }
706         }
707         return slots;
708     }
709 
getNextAvailableWeaverSlot()710     private int getNextAvailableWeaverSlot() {
711         Set<Integer> usedSlots = getUsedWeaverSlots();
712         usedSlots.addAll(mPasswordSlotManager.getUsedSlots());
713         for (int i = 0; i < mWeaverConfig.slots; i++) {
714             if (!usedSlots.contains(i)) {
715                 return i;
716             }
717         }
718         throw new IllegalStateException("Run out of weaver slots.");
719     }
720 
721     /**
722      * Create a new password based SP blob based on the supplied authentication token, such that
723      * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
724      * in the same authentication token.
725      *
726      * This method only creates SP blob wrapping around the given synthetic password and does not
727      * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
728      * is consistent with the device state by calling other APIs in this class.
729      *
730      * @see #newSidForUser
731      * @see #clearSidForUser
732      * @return a new password handle for the wrapped SP blob
733      * @throw IllegalStateException if creation fails.
734      */
createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, LockscreenCredential credential, AuthenticationToken authToken, int userId)735     public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
736             LockscreenCredential credential, AuthenticationToken authToken, int userId) {
737         long handle = generateHandle();
738         PasswordData pwd = PasswordData.create(credential.getType());
739         byte[] pwdToken = computePasswordToken(credential, pwd);
740         final long sid;
741         final byte[] applicationId;
742 
743         if (isWeaverAvailable()) {
744             // Weaver based user password
745             int weaverSlot = getNextAvailableWeaverSlot();
746             Slog.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
747             byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken),
748                     null);
749             if (weaverSecret == null) {
750                 throw new IllegalStateException(
751                         "Fail to enroll user password under weaver " + userId);
752             }
753             saveWeaverSlot(weaverSlot, handle, userId);
754             mPasswordSlotManager.markSlotInUse(weaverSlot);
755             // No need to pass in quality since the credential type already encodes sufficient info
756             synchronizeWeaverFrpPassword(pwd, 0, userId, weaverSlot);
757 
758             pwd.passwordHandle = null;
759             sid = GateKeeper.INVALID_SECURE_USER_ID;
760             applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret);
761         } else {
762             // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
763             // to prevent them from accumulating and causing problems.
764             try {
765                 gatekeeper.clearSecureUserId(fakeUid(userId));
766             } catch (RemoteException ignore) {
767                 Slog.w(TAG, "Failed to clear SID from gatekeeper");
768             }
769             // GateKeeper based user password
770             GateKeeperResponse response;
771             try {
772                 response = gatekeeper.enroll(fakeUid(userId), null, null,
773                         passwordTokenToGkInput(pwdToken));
774             } catch (RemoteException e) {
775                 throw new IllegalStateException("Failed to enroll password for new SP blob", e);
776             }
777             if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
778                 throw new IllegalStateException(
779                         "Fail to enroll user password when creating SP for user " + userId);
780             }
781             pwd.passwordHandle = response.getPayload();
782             sid = sidFromPasswordHandle(pwd.passwordHandle);
783             applicationId = transformUnderSecdiscardable(pwdToken,
784                     createSecdiscardable(handle, userId));
785             // No need to pass in quality since the credential type already encodes sufficient info
786             synchronizeFrpPassword(pwd, 0, userId);
787         }
788         saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
789         savePasswordMetrics(credential, authToken, handle, userId);
790         createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
791                 applicationId, sid, userId);
792         return handle;
793     }
794 
verifyFrpCredential(IGateKeeperService gatekeeper, LockscreenCredential userCredential, ICheckCredentialProgressCallback progressCallback)795     public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
796             LockscreenCredential userCredential,
797             ICheckCredentialProgressCallback progressCallback) {
798         PersistentData persistentData = mStorage.readPersistentDataBlock();
799         if (persistentData.type == PersistentData.TYPE_SP) {
800             PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
801             byte[] pwdToken = computePasswordToken(userCredential, pwd);
802 
803             GateKeeperResponse response;
804             try {
805                 response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
806                         0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
807             } catch (RemoteException e) {
808                 Slog.e(TAG, "FRP verifyChallenge failed", e);
809                 return VerifyCredentialResponse.ERROR;
810             }
811             return VerifyCredentialResponse.fromGateKeeperResponse(response);
812         } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
813             if (!isWeaverAvailable()) {
814                 Slog.e(TAG, "No weaver service to verify SP-based FRP credential");
815                 return VerifyCredentialResponse.ERROR;
816             }
817             PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
818             byte[] pwdToken = computePasswordToken(userCredential, pwd);
819             int weaverSlot = persistentData.userId;
820 
821             return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
822         } else {
823             Slog.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
824                     + persistentData.type);
825             return VerifyCredentialResponse.ERROR;
826         }
827     }
828 
829 
migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality)830     public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) {
831         if (mStorage.getPersistentDataBlockManager() != null
832                 && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) {
833             PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle,
834                     userInfo.id));
835             if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
836                 int weaverSlot = loadWeaverSlot(handle, userInfo.id);
837                 if (weaverSlot != INVALID_WEAVER_SLOT) {
838                     synchronizeWeaverFrpPassword(pwd, requestedQuality, userInfo.id, weaverSlot);
839                 } else {
840                     synchronizeFrpPassword(pwd, requestedQuality, userInfo.id);
841                 }
842             }
843         }
844     }
845 
synchronizeFrpPassword(PasswordData pwd, int requestedQuality, int userId)846     private void synchronizeFrpPassword(PasswordData pwd,
847             int requestedQuality, int userId) {
848         if (mStorage.getPersistentDataBlockManager() != null
849                 && LockPatternUtils.userOwnsFrpCredential(mContext,
850                 mUserManager.getUserInfo(userId))) {
851             if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
852                 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
853                         pwd.toBytes());
854             } else {
855                 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
856             }
857         }
858     }
859 
synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId, int weaverSlot)860     private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
861             int weaverSlot) {
862         if (mStorage.getPersistentDataBlockManager() != null
863                 && LockPatternUtils.userOwnsFrpCredential(mContext,
864                 mUserManager.getUserInfo(userId))) {
865             if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
866                 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
867                         requestedQuality, pwd.toBytes());
868             } else {
869                 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, 0, 0, null);
870             }
871         }
872     }
873 
874     private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
875 
876     /**
877      * Create a token based Synthetic password for the given user.
878      * @return the handle of the token
879      */
createTokenBasedSyntheticPassword(byte[] token, int userId, @Nullable EscrowTokenStateChangeCallback changeCallback)880     public long createTokenBasedSyntheticPassword(byte[] token, int userId,
881             @Nullable EscrowTokenStateChangeCallback changeCallback) {
882         long handle = generateHandle();
883         if (!tokenMap.containsKey(userId)) {
884             tokenMap.put(userId, new ArrayMap<>());
885         }
886         TokenData tokenData = new TokenData();
887         final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
888         if (isWeaverAvailable()) {
889             tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
890             tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
891                             PERSONALISATION_WEAVER_TOKEN, secdiscardable);
892         } else {
893             tokenData.secdiscardableOnDisk = secdiscardable;
894             tokenData.weaverSecret = null;
895         }
896         tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
897         tokenData.mCallback = changeCallback;
898 
899         tokenMap.get(userId).put(handle, tokenData);
900         return handle;
901     }
902 
getPendingTokensForUser(int userId)903     public Set<Long> getPendingTokensForUser(int userId) {
904         if (!tokenMap.containsKey(userId)) {
905             return Collections.emptySet();
906         }
907         return tokenMap.get(userId).keySet();
908     }
909 
removePendingToken(long handle, int userId)910     public boolean removePendingToken(long handle, int userId) {
911         if (!tokenMap.containsKey(userId)) {
912             return false;
913         }
914         return tokenMap.get(userId).remove(handle) != null;
915     }
916 
activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken, int userId)917     public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
918             int userId) {
919         if (!tokenMap.containsKey(userId)) {
920             return false;
921         }
922         TokenData tokenData = tokenMap.get(userId).get(handle);
923         if (tokenData == null) {
924             return false;
925         }
926         if (!loadEscrowData(authToken, userId)) {
927             Slog.w(TAG, "User is not escrowable");
928             return false;
929         }
930         if (isWeaverAvailable()) {
931             int slot = getNextAvailableWeaverSlot();
932             Slog.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
933             if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) {
934                 Slog.e(TAG, "Failed to enroll weaver secret when activating token");
935                 return false;
936             }
937             saveWeaverSlot(slot, handle, userId);
938             mPasswordSlotManager.markSlotInUse(slot);
939         }
940         saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
941         createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
942                 tokenData.aggregatedSecret, 0L, userId);
943         tokenMap.get(userId).remove(handle);
944         if (tokenData.mCallback != null) {
945             tokenData.mCallback.onEscrowTokenActivated(handle, userId);
946         }
947         return true;
948     }
949 
createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken, byte[] applicationId, long sid, int userId)950     private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
951             byte[] applicationId, long sid, int userId) {
952         final byte[] secret;
953         if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
954             secret = authToken.getEscrowSecret();
955         } else {
956             secret = authToken.getSyntheticPassword();
957         }
958         byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
959         byte[] blob = new byte[content.length + 1 + 1];
960         /*
961          * We can upgrade from v1 to v2 because that's just a change in the way that
962          * the SP is stored. However, we can't upgrade to v3 because that is a change
963          * in the way that passwords are derived from the SP.
964          */
965         if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
966             blob[0] = SYNTHETIC_PASSWORD_VERSION_V3;
967         } else {
968             blob[0] = SYNTHETIC_PASSWORD_VERSION_V2;
969         }
970         blob[1] = type;
971         System.arraycopy(content, 0, blob, 2, content.length);
972         saveState(SP_BLOB_NAME, blob, handle, userId);
973     }
974 
975     /**
976      * Decrypt a synthetic password by supplying the user credential and corresponding password
977      * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
978      * verification to referesh the SID & Auth token maintained by the system.
979      */
unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, long handle, @NonNull LockscreenCredential credential, int userId, ICheckCredentialProgressCallback progressCallback)980     public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
981             long handle, @NonNull LockscreenCredential credential, int userId,
982             ICheckCredentialProgressCallback progressCallback) {
983         AuthenticationResult result = new AuthenticationResult();
984         PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
985 
986         if (!credential.checkAgainstStoredType(pwd.credentialType)) {
987             Slog.e(TAG, String.format("Credential type mismatch: expected %d actual %d",
988                     pwd.credentialType, credential.getType()));
989             result.gkResponse = VerifyCredentialResponse.ERROR;
990             return result;
991         }
992 
993         byte[] pwdToken = computePasswordToken(credential, pwd);
994 
995         final byte[] applicationId;
996         final long sid;
997         int weaverSlot = loadWeaverSlot(handle, userId);
998         if (weaverSlot != INVALID_WEAVER_SLOT) {
999             // Weaver based user password
1000             if (!isWeaverAvailable()) {
1001                 Slog.e(TAG, "No weaver service to unwrap password based SP");
1002                 result.gkResponse = VerifyCredentialResponse.ERROR;
1003                 return result;
1004             }
1005             result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
1006             if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
1007                 return result;
1008             }
1009             sid = GateKeeper.INVALID_SECURE_USER_ID;
1010             applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
1011         } else {
1012             byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
1013             GateKeeperResponse response;
1014             try {
1015                 response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
1016                         pwd.passwordHandle, gkPwdToken);
1017             } catch (RemoteException e) {
1018                 Slog.e(TAG, "gatekeeper verify failed", e);
1019                 result.gkResponse = VerifyCredentialResponse.ERROR;
1020                 return result;
1021             }
1022             int responseCode = response.getResponseCode();
1023             if (responseCode == GateKeeperResponse.RESPONSE_OK) {
1024                 result.gkResponse = VerifyCredentialResponse.OK;
1025                 if (response.getShouldReEnroll()) {
1026                     GateKeeperResponse reenrollResponse;
1027                     try {
1028                         reenrollResponse = gatekeeper.enroll(fakeUid(userId),
1029                                 pwd.passwordHandle, gkPwdToken, gkPwdToken);
1030                     } catch (RemoteException e) {
1031                         Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e);
1032                         reenrollResponse = GateKeeperResponse.ERROR;
1033                         // continue the flow anyway
1034                     }
1035                     if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
1036                         pwd.passwordHandle = reenrollResponse.getPayload();
1037                         // Use the reenrollment opportunity to update credential type
1038                         // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN)
1039                         pwd.credentialType = credential.getType();
1040                         saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
1041                         synchronizeFrpPassword(pwd, 0, userId);
1042                     } else {
1043                         Slog.w(TAG, "Fail to re-enroll user password for user " + userId);
1044                         // continue the flow anyway
1045                     }
1046                 }
1047             } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
1048                 result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
1049                 return result;
1050             } else  {
1051                 result.gkResponse = VerifyCredentialResponse.ERROR;
1052                 return result;
1053             }
1054             sid = sidFromPasswordHandle(pwd.passwordHandle);
1055             applicationId = transformUnderSecdiscardable(pwdToken,
1056                     loadSecdiscardable(handle, userId));
1057         }
1058         // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
1059         // Notify the callback so the keyguard UI can proceed immediately.
1060         if (progressCallback != null) {
1061             try {
1062                 progressCallback.onCredentialVerified();
1063             } catch (RemoteException e) {
1064                 Slog.w(TAG, "progressCallback throws exception", e);
1065             }
1066         }
1067         result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
1068                 applicationId, sid, userId);
1069 
1070         // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
1071         result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
1072 
1073         // Upgrade case: store the metrics if the device did not have stored metrics before, should
1074         // only happen once on old synthetic password blobs.
1075         if (result.authToken != null && !hasPasswordMetrics(handle, userId)) {
1076             savePasswordMetrics(credential, result.authToken, handle, userId);
1077         }
1078         return result;
1079     }
1080 
1081     /**
1082      * Decrypt a synthetic password by supplying an escrow token and corresponding token
1083      * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
1084      * verification to referesh the SID & Auth token maintained by the system.
1085      */
unwrapTokenBasedSyntheticPassword( IGateKeeperService gatekeeper, long handle, byte[] token, int userId)1086     public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
1087             IGateKeeperService gatekeeper, long handle, byte[] token, int userId) {
1088         AuthenticationResult result = new AuthenticationResult();
1089         byte[] secdiscardable = loadSecdiscardable(handle, userId);
1090         int slotId = loadWeaverSlot(handle, userId);
1091         if (slotId != INVALID_WEAVER_SLOT) {
1092             if (!isWeaverAvailable()) {
1093                 Slog.e(TAG, "No weaver service to unwrap token based SP");
1094                 result.gkResponse = VerifyCredentialResponse.ERROR;
1095                 return result;
1096             }
1097             VerifyCredentialResponse response = weaverVerify(slotId, null);
1098             if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
1099                     response.getPayload() == null) {
1100                 Slog.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
1101                 result.gkResponse = VerifyCredentialResponse.ERROR;
1102                 return result;
1103             }
1104             secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
1105                     PERSONALISATION_WEAVER_TOKEN, secdiscardable);
1106         }
1107         byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
1108         result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
1109                 applicationId, 0L, userId);
1110         if (result.authToken != null) {
1111             result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
1112             if (result.gkResponse == null) {
1113                 // The user currently has no password. return OK with null payload so null
1114                 // is propagated to unlockUser()
1115                 result.gkResponse = VerifyCredentialResponse.OK;
1116             }
1117         } else {
1118             result.gkResponse = VerifyCredentialResponse.ERROR;
1119         }
1120         return result;
1121     }
1122 
unwrapSyntheticPasswordBlob(long handle, byte type, byte[] applicationId, long sid, int userId)1123     private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
1124             byte[] applicationId, long sid, int userId) {
1125         byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
1126         if (blob == null) {
1127             return null;
1128         }
1129         final byte version = blob[0];
1130         if (version != SYNTHETIC_PASSWORD_VERSION_V3
1131                 && version != SYNTHETIC_PASSWORD_VERSION_V2
1132                 && version != SYNTHETIC_PASSWORD_VERSION_V1) {
1133             throw new IllegalArgumentException("Unknown blob version");
1134         }
1135         if (blob[1] != type) {
1136             throw new IllegalArgumentException("Invalid blob type");
1137         }
1138         final byte[] secret;
1139         if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
1140             secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
1141                     Arrays.copyOfRange(blob, 2, blob.length), applicationId);
1142         } else {
1143             secret = decryptSPBlob(getHandleName(handle),
1144                 Arrays.copyOfRange(blob, 2, blob.length), applicationId);
1145         }
1146         if (secret == null) {
1147             Slog.e(TAG, "Fail to decrypt SP for user " + userId);
1148             return null;
1149         }
1150         AuthenticationToken result = new AuthenticationToken(version);
1151         if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
1152             if (!loadEscrowData(result, userId)) {
1153                 Slog.e(TAG, "User is not escrowable: " + userId);
1154                 return null;
1155             }
1156             result.recreateFromEscrow(secret);
1157         } else {
1158             result.recreateDirectly(secret);
1159         }
1160         if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
1161             Slog.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
1162             createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
1163         }
1164         return result;
1165     }
1166 
1167     /**
1168      * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
1169      * if required.
1170      *
1171      * Normally performing verifyChallenge with an AuthenticationToken should always return
1172      * RESPONSE_OK, since user authentication failures are detected earlier when trying to
1173      * decrypt SP.
1174      */
verifyChallenge(IGateKeeperService gatekeeper, @NonNull AuthenticationToken auth, long challenge, int userId)1175     public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
1176             @NonNull AuthenticationToken auth, long challenge, int userId) {
1177         byte[] spHandle = loadSyntheticPasswordHandle(userId);
1178         if (spHandle == null) {
1179             // There is no password handle associated with the given user, i.e. the user is not
1180             // secured by lockscreen and has no SID, so just return here;
1181             return null;
1182         }
1183         GateKeeperResponse response;
1184         try {
1185             response = gatekeeper.verifyChallenge(userId, challenge,
1186                     spHandle, auth.deriveGkPassword());
1187         } catch (RemoteException e) {
1188             Slog.e(TAG, "Fail to verify with gatekeeper " + userId, e);
1189             return VerifyCredentialResponse.ERROR;
1190         }
1191         int responseCode = response.getResponseCode();
1192         if (responseCode == GateKeeperResponse.RESPONSE_OK) {
1193             VerifyCredentialResponse result = new VerifyCredentialResponse(response.getPayload());
1194             if (response.getShouldReEnroll()) {
1195                 try {
1196                     response = gatekeeper.enroll(userId, spHandle, spHandle,
1197                             auth.deriveGkPassword());
1198                 } catch (RemoteException e) {
1199                     Slog.e(TAG, "Failed to invoke gatekeeper.enroll", e);
1200                     response = GateKeeperResponse.ERROR;
1201                 }
1202                 if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
1203                     spHandle = response.getPayload();
1204                     saveSyntheticPasswordHandle(spHandle, userId);
1205                     // Call self again to re-verify with updated handle
1206                     return verifyChallenge(gatekeeper, auth, challenge, userId);
1207                 } else {
1208                     // Fall through, return result from the previous verification attempt.
1209                     Slog.w(TAG, "Fail to re-enroll SP handle for user " + userId);
1210                 }
1211             }
1212             return result;
1213         } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
1214             return new VerifyCredentialResponse(response.getTimeout());
1215         } else {
1216             return VerifyCredentialResponse.ERROR;
1217         }
1218     }
1219 
existsHandle(long handle, int userId)1220     public boolean existsHandle(long handle, int userId) {
1221         return hasState(SP_BLOB_NAME, handle, userId);
1222     }
1223 
destroyTokenBasedSyntheticPassword(long handle, int userId)1224     public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
1225         destroySyntheticPassword(handle, userId);
1226         destroyState(SECDISCARDABLE_NAME, handle, userId);
1227     }
1228 
destroyPasswordBasedSyntheticPassword(long handle, int userId)1229     public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
1230         destroySyntheticPassword(handle, userId);
1231         destroyState(SECDISCARDABLE_NAME, handle, userId);
1232         destroyState(PASSWORD_DATA_NAME, handle, userId);
1233         destroyState(PASSWORD_METRICS_NAME, handle, userId);
1234     }
1235 
destroySyntheticPassword(long handle, int userId)1236     private void destroySyntheticPassword(long handle, int userId) {
1237         destroyState(SP_BLOB_NAME, handle, userId);
1238         destroySPBlobKey(getHandleName(handle));
1239         if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
1240             destroyWeaverSlot(handle, userId);
1241         }
1242     }
1243 
transformUnderWeaverSecret(byte[] data, byte[] secret)1244     private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
1245         byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash(
1246                 PERSONALISATION_WEAVER_PASSWORD, secret);
1247         byte[] result = new byte[data.length + weaverSecret.length];
1248         System.arraycopy(data, 0, result, 0, data.length);
1249         System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length);
1250         return result;
1251     }
1252 
transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable)1253     private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
1254         byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
1255                 PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
1256         byte[] result = new byte[data.length + secdiscardable.length];
1257         System.arraycopy(data, 0, result, 0, data.length);
1258         System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
1259         return result;
1260     }
1261 
createSecdiscardable(long handle, int userId)1262     private byte[] createSecdiscardable(long handle, int userId) {
1263         byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
1264         saveSecdiscardable(handle, data, userId);
1265         return data;
1266     }
1267 
saveSecdiscardable(long handle, byte[] secdiscardable, int userId)1268     private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
1269         saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
1270     }
1271 
loadSecdiscardable(long handle, int userId)1272     private byte[] loadSecdiscardable(long handle, int userId) {
1273         return loadState(SECDISCARDABLE_NAME, handle, userId);
1274     }
1275 
1276     /**
1277      * Retrieves the saved password metrics associated with a SP handle. Only meaningful to be
1278      * called on the handle of a password-based synthetic password. A valid AuthenticationToken for
1279      * the target user is required in order to be able to decrypt the encrypted password metrics on
1280      * disk.
1281      */
getPasswordMetrics(AuthenticationToken authToken, long handle, int userId)1282     public @Nullable PasswordMetrics getPasswordMetrics(AuthenticationToken authToken, long handle,
1283             int userId) {
1284         final byte[] encrypted = loadState(PASSWORD_METRICS_NAME, handle, userId);
1285         if (encrypted == null) return null;
1286         final byte[] decrypted = SyntheticPasswordCrypto.decrypt(authToken.deriveMetricsKey(),
1287                 /* personalization= */ new byte[0], encrypted);
1288         if (decrypted == null) return null;
1289         return VersionedPasswordMetrics.deserialize(decrypted).getMetrics();
1290     }
1291 
savePasswordMetrics(LockscreenCredential credential, AuthenticationToken authToken, long handle, int userId)1292     private void savePasswordMetrics(LockscreenCredential credential, AuthenticationToken authToken,
1293             long handle, int userId) {
1294         final byte[] encrypted = SyntheticPasswordCrypto.encrypt(authToken.deriveMetricsKey(),
1295                 /* personalization= */ new byte[0],
1296                 new VersionedPasswordMetrics(credential).serialize());
1297         saveState(PASSWORD_METRICS_NAME, encrypted, handle, userId);
1298     }
1299 
hasPasswordMetrics(long handle, int userId)1300     private boolean hasPasswordMetrics(long handle, int userId) {
1301         return hasState(PASSWORD_METRICS_NAME, handle, userId);
1302     }
1303 
hasState(String stateName, long handle, int userId)1304     private boolean hasState(String stateName, long handle, int userId) {
1305         return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
1306     }
1307 
loadState(String stateName, long handle, int userId)1308     private byte[] loadState(String stateName, long handle, int userId) {
1309         return mStorage.readSyntheticPasswordState(userId, handle, stateName);
1310     }
1311 
saveState(String stateName, byte[] data, long handle, int userId)1312     private void saveState(String stateName, byte[] data, long handle, int userId) {
1313         mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
1314     }
1315 
destroyState(String stateName, long handle, int userId)1316     private void destroyState(String stateName, long handle, int userId) {
1317         mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
1318     }
1319 
decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId)1320     protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
1321         return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
1322     }
1323 
createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid)1324     protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
1325         return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
1326     }
1327 
destroySPBlobKey(String keyAlias)1328     protected void destroySPBlobKey(String keyAlias) {
1329         SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
1330     }
1331 
generateHandle()1332     public static long generateHandle() {
1333         SecureRandom rng = new SecureRandom();
1334         long result;
1335         do {
1336             result = rng.nextLong();
1337         } while (result == DEFAULT_HANDLE);
1338         return result;
1339     }
1340 
fakeUid(int uid)1341     private int fakeUid(int uid) {
1342         return 100000 + uid;
1343     }
1344 
secureRandom(int length)1345     protected static byte[] secureRandom(int length) {
1346         try {
1347             return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
1348         } catch (NoSuchAlgorithmException e) {
1349             e.printStackTrace();
1350             return null;
1351         }
1352     }
1353 
getHandleName(long handle)1354     private String getHandleName(long handle) {
1355         return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
1356     }
1357 
computePasswordToken(LockscreenCredential credential, PasswordData data)1358     private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) {
1359         final byte[] password = credential.isNone() ? DEFAULT_PASSWORD : credential.getCredential();
1360         return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
1361                 PASSWORD_TOKEN_LENGTH);
1362     }
1363 
passwordTokenToGkInput(byte[] token)1364     private byte[] passwordTokenToGkInput(byte[] token) {
1365         return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
1366     }
1367 
passwordTokenToWeaverKey(byte[] token)1368     private byte[] passwordTokenToWeaverKey(byte[] token) {
1369         byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
1370         if (key.length < mWeaverConfig.keySize) {
1371             throw new IllegalArgumentException("weaver key length too small");
1372         }
1373         return Arrays.copyOf(key, mWeaverConfig.keySize);
1374     }
1375 
sidFromPasswordHandle(byte[] handle)1376     protected long sidFromPasswordHandle(byte[] handle) {
1377         return nativeSidFromPasswordHandle(handle);
1378     }
1379 
scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen)1380     protected byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) {
1381         return new Scrypt().scrypt(password, salt, n, r, p, outLen);
1382     }
1383 
nativeSidFromPasswordHandle(byte[] handle)1384     native long nativeSidFromPasswordHandle(byte[] handle);
1385 
toByteArrayList(byte[] data)1386     protected static ArrayList<Byte> toByteArrayList(byte[] data) {
1387         ArrayList<Byte> result = new ArrayList<Byte>(data.length);
1388         for (int i = 0; i < data.length; i++) {
1389             result.add(data[i]);
1390         }
1391         return result;
1392     }
1393 
fromByteArrayList(ArrayList<Byte> data)1394     protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
1395         byte[] result = new byte[data.size()];
1396         for (int i = 0; i < data.size(); i++) {
1397             result[i] = data.get(i);
1398         }
1399         return result;
1400     }
1401 
1402     protected static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes();
bytesToHex(byte[] bytes)1403     private static byte[] bytesToHex(byte[] bytes) {
1404         if (bytes == null) {
1405             return "null".getBytes();
1406         }
1407         byte[] hexBytes = new byte[bytes.length * 2];
1408         for ( int j = 0; j < bytes.length; j++ ) {
1409             int v = bytes[j] & 0xFF;
1410             hexBytes[j * 2] = HEX_ARRAY[v >>> 4];
1411             hexBytes[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
1412         }
1413         return hexBytes;
1414     }
1415 }
1416