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_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 
    149         public String deriveKeyStorePassword() {
    150             return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
    151                     PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
    152         }
    153 
    154         public byte[] deriveGkPassword() {
    155             return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
    156                     syntheticPassword.getBytes());
    157         }
    158 
    159         public byte[] deriveDiskEncryptionKey() {
    160             return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
    161                     syntheticPassword.getBytes());
    162         }
    163 
    164         public byte[] deriveVendorAuthSecret() {
    165             return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_AUTHSECRET_KEY,
    166                     syntheticPassword.getBytes());
    167         }
    168 
    169         public byte[] derivePasswordHashFactor() {
    170             return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_PASSWORD_HASH,
    171                     syntheticPassword.getBytes());
    172         }
    173 
    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 
    183         public void recreate(byte[] secret) {
    184             initialize(secret, this.P1);
    185         }
    186 
    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 
    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 
    213         public static PasswordData create(int passwordType) {
    214             PasswordData result = new PasswordData();
    215             result.scryptN = PASSWORD_SCRYPT_N;
    216             result.scryptR = PASSWORD_SCRYPT_R;
    217             result.scryptP = PASSWORD_SCRYPT_P;
    218             result.passwordType = passwordType;
    219             result.salt = secureRandom(PASSWORD_SALT_LENGTH);
    220             return result;
    221         }
    222 
    223         public static PasswordData fromBytes(byte[] data) {
    224             PasswordData result = new PasswordData();
    225             ByteBuffer buffer = ByteBuffer.allocate(data.length);
    226             buffer.put(data, 0, data.length);
    227             buffer.flip();
    228             result.passwordType = buffer.getInt();
    229             result.scryptN = buffer.get();
    230             result.scryptR = buffer.get();
    231             result.scryptP = buffer.get();
    232             int saltLen = buffer.getInt();
    233             result.salt = new byte[saltLen];
    234             buffer.get(result.salt);
    235             int handleLen = buffer.getInt();
    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 
    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 
    279     public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
    280             UserManager userManager) {
    281         mContext = context;
    282         mStorage = storage;
    283         mUserManager = userManager;
    284     }
    285 
    286     @VisibleForTesting
    287     protected IWeaver getWeaverService() throws RemoteException {
    288         try {
    289             return IWeaver.getService();
    290         } catch (NoSuchElementException e) {
    291             Slog.i(TAG, "Device does not support weaver");
    292             return null;
    293         }
    294     }
    295 
    296     public synchronized void initWeaverService() {
    297         if (mWeaver != null) {
    298             return;
    299         }
    300         try {
    301             mWeaverConfig = null;
    302             mWeaver = getWeaverService();
    303             if (mWeaver != null) {
    304                 mWeaver.getConfig((int status, WeaverConfig config) -> {
    305                     if (status == WeaverStatus.OK && config.slots > 0) {
    306                         mWeaverConfig = config;
    307                     } else {
    308                         Slog.e(TAG, "Failed to get weaver config, status " + status
    309                                 + " slots: " + config.slots);
    310                         mWeaver = null;
    311                     }
    312                 });
    313             }
    314         } catch (RemoteException e) {
    315             Slog.e(TAG, "Failed to get weaver service", e);
    316         }
    317     }
    318 
    319     private synchronized boolean isWeaverAvailable() {
    320         if (mWeaver == null) {
    321             //Re-initializing weaver in case there was a transient error preventing access to it.
    322             initWeaverService();
    323         }
    324         return mWeaver != null && mWeaverConfig.slots > 0;
    325     }
    326 
    327     /**
    328      * Enroll the given key value pair into the specified weaver slot. if the given key is null,
    329      * a default all-zero key is used. If the value is not specified, a fresh random secret is
    330      * generated as the value.
    331      *
    332      * @return the value stored in the weaver slot
    333      * @throws RemoteException
    334      */
    335     private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
    336             throws RemoteException {
    337         if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
    338             throw new RuntimeException("Invalid slot for weaver");
    339         }
    340         if (key == null) {
    341             key = new byte[mWeaverConfig.keySize];
    342         } else if (key.length != mWeaverConfig.keySize) {
    343             throw new RuntimeException("Invalid key size for weaver");
    344         }
    345         if (value == null) {
    346             value = secureRandom(mWeaverConfig.valueSize);
    347         }
    348         int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
    349         if (writeStatus != WeaverStatus.OK) {
    350             Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
    351             return null;
    352         }
    353         return value;
    354     }
    355 
    356     /**
    357      * Verify the supplied key against a weaver slot, returning a response indicating whether
    358      * the verification is successful, throttled or failed. If successful, the bound secret
    359      * is also returned.
    360      * @throws RemoteException
    361      */
    362     private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
    363         if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
    364             throw new RuntimeException("Invalid slot for weaver");
    365         }
    366         if (key == null) {
    367             key = new byte[mWeaverConfig.keySize];
    368         } else if (key.length != mWeaverConfig.keySize) {
    369             throw new RuntimeException("Invalid key size for weaver");
    370         }
    371         final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
    372         mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
    373             switch (status) {
    374                 case WeaverReadStatus.OK:
    375                     response[0] = new VerifyCredentialResponse(
    376                             fromByteArrayList(readResponse.value));
    377                     break;
    378                 case WeaverReadStatus.THROTTLE:
    379                     response[0] = new VerifyCredentialResponse(readResponse.timeout);
    380                     Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
    381                     break;
    382                 case WeaverReadStatus.INCORRECT_KEY:
    383                     if (readResponse.timeout == 0) {
    384                         response[0] = VerifyCredentialResponse.ERROR;
    385                         Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
    386                     } else {
    387                         response[0] = new VerifyCredentialResponse(readResponse.timeout);
    388                         Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
    389                     }
    390                     break;
    391                 case WeaverReadStatus.FAILED:
    392                     response[0] = VerifyCredentialResponse.ERROR;
    393                     Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
    394                     break;
    395                default:
    396                    response[0] = VerifyCredentialResponse.ERROR;
    397                    Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
    398                    break;
    399             }
    400         });
    401         return response[0];
    402     }
    403 
    404     public void removeUser(int userId) {
    405         for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
    406             destroyWeaverSlot(handle, userId);
    407             destroySPBlobKey(getHandleName(handle));
    408         }
    409     }
    410 
    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      */
    442     public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
    443             byte[] hash, String credential, int userId) throws RemoteException {
    444         AuthenticationToken result = AuthenticationToken.create();
    445         GateKeeperResponse response;
    446         if (hash != null) {
    447             response = gatekeeper.enroll(userId, hash, credential.getBytes(),
    448                     result.deriveGkPassword());
    449             if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
    450                 Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
    451                 clearSidForUser(userId);
    452             } else {
    453                 saveSyntheticPasswordHandle(response.getPayload(), userId);
    454             }
    455         } else {
    456             clearSidForUser(userId);
    457         }
    458         saveEscrowData(result, userId);
    459         return result;
    460     }
    461 
    462     /**
    463      * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
    464      * Used when adding password to previously-unsecured devices.
    465      */
    466     public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
    467             int userId) throws RemoteException {
    468         GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
    469                 authToken.deriveGkPassword());
    470         if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
    471             Log.e(TAG, "Fail to create new SID for user " + userId);
    472             return;
    473         }
    474         saveSyntheticPasswordHandle(response.getPayload(), userId);
    475     }
    476 
    477     // Nuke the SP handle (and as a result, its SID) for the given user.
    478     public void clearSidForUser(int userId) {
    479         destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
    480     }
    481 
    482     public boolean hasSidForUser(int userId) {
    483         return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
    484     }
    485 
    486     // if null, it means there is no SID associated with the user
    487     // This can happen if the user is migrated to SP but currently
    488     // do not have a lockscreen password.
    489     private byte[] loadSyntheticPasswordHandle(int userId) {
    490         return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
    491     }
    492 
    493     private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
    494         saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
    495     }
    496 
    497     private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
    498         authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
    499         authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
    500         return authToken.E0 != null && authToken.P1 != null;
    501     }
    502 
    503     private void saveEscrowData(AuthenticationToken authToken, int userId) {
    504         saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId);
    505         saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId);
    506     }
    507 
    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 
    513     public void destroyEscrowData(int userId) {
    514         destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
    515         destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
    516     }
    517 
    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 
    534     private void saveWeaverSlot(int slot, long handle, int userId) {
    535         ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
    536         buffer.put(WEAVER_VERSION);
    537         buffer.putInt(slot);
    538         saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
    539     }
    540 
    541     private void destroyWeaverSlot(long handle, int userId) {
    542         int slot = loadWeaverSlot(handle, userId);
    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      */
    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 
    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      */
    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 
    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 
    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 
    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 
    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 
    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 
    750     public Set<Long> getPendingTokensForUser(int userId) {
    751         if (!tokenMap.containsKey(userId)) {
    752             return Collections.emptySet();
    753         }
    754         return tokenMap.get(userId).keySet();
    755     }
    756 
    757     public boolean removePendingToken(long handle, int userId) {
    758         if (!tokenMap.containsKey(userId)) {
    759             return false;
    760         }
    761         return tokenMap.get(userId).remove(handle) != null;
    762     }
    763 
    764     public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
    765             int userId) {
    766         if (!tokenMap.containsKey(userId)) {
    767             return false;
    768         }
    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 
    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      */
    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      */
    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 
    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      */
    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 
   1021     public boolean existsHandle(long handle, int userId) {
   1022         return hasState(SP_BLOB_NAME, handle, userId);
   1023     }
   1024 
   1025     public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
   1026         destroySyntheticPassword(handle, userId);
   1027         destroyState(SECDISCARDABLE_NAME, handle, userId);
   1028     }
   1029 
   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 
   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 
   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 
   1053     private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
   1054         byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
   1055                 PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
   1056         byte[] result = new byte[data.length + secdiscardable.length];
   1057         System.arraycopy(data, 0, result, 0, data.length);
   1058         System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
   1059         return result;
   1060     }
   1061 
   1062     private byte[] createSecdiscardable(long handle, int userId) {
   1063         byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
   1064         saveSecdiscardable(handle, data, userId);
   1065         return data;
   1066     }
   1067 
   1068     private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
   1069         saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
   1070     }
   1071 
   1072     private byte[] loadSecdiscardable(long handle, int userId) {
   1073         return loadState(SECDISCARDABLE_NAME, handle, userId);
   1074     }
   1075 
   1076     private boolean hasState(String stateName, long handle, int userId) {
   1077         return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
   1078     }
   1079 
   1080     private byte[] loadState(String stateName, long handle, int userId) {
   1081         return mStorage.readSyntheticPasswordState(userId, handle, stateName);
   1082     }
   1083 
   1084     private void saveState(String stateName, byte[] data, long handle, int userId) {
   1085         mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
   1086     }
   1087 
   1088     private void destroyState(String stateName, long handle, int userId) {
   1089         mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
   1090     }
   1091 
   1092     protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
   1093         return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
   1094     }
   1095 
   1096     protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
   1097         return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
   1098     }
   1099 
   1100     protected void destroySPBlobKey(String keyAlias) {
   1101         SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
   1102     }
   1103 
   1104     public static long generateHandle() {
   1105         SecureRandom rng = new SecureRandom();
   1106         long result;
   1107         do {
   1108             result = rng.nextLong();
   1109         } while (result == DEFAULT_HANDLE);
   1110         return result;
   1111     }
   1112 
   1113     private int fakeUid(int uid) {
   1114         return 100000 + uid;
   1115     }
   1116 
   1117     protected static byte[] secureRandom(int length) {
   1118         try {
   1119             return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
   1120         } catch (NoSuchAlgorithmException e) {
   1121             e.printStackTrace();
   1122             return null;
   1123         }
   1124     }
   1125 
   1126     private String getHandleName(long handle) {
   1127         return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
   1128     }
   1129 
   1130     private byte[] computePasswordToken(String password, PasswordData data) {
   1131         return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
   1132                 PASSWORD_TOKEN_LENGTH);
   1133     }
   1134 
   1135     private byte[] passwordTokenToGkInput(byte[] token) {
   1136         return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
   1137     }
   1138 
   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 
   1147     protected long sidFromPasswordHandle(byte[] handle) {
   1148         return nativeSidFromPasswordHandle(handle);
   1149     }
   1150 
   1151     protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
   1152         return nativeScrypt(password.getBytes(), salt, N, r, p, outLen);
   1153     }
   1154 
   1155     native long nativeSidFromPasswordHandle(byte[] handle);
   1156     native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen);
   1157 
   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 
   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();
   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