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