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