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