SyntheticPasswordManager.java revision a339df080d7dfacbec7915f869115dfa91ce4b12
13bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu/*
23bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * Copyright (C) 2017 The Android Open Source Project
33bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *
43bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * Licensed under the Apache License, Version 2.0 (the "License");
53bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * you may not use this file except in compliance with the License.
63bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * You may obtain a copy of the License at
73bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *
83bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *      http://www.apache.org/licenses/LICENSE-2.0
93bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *
103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * Unless required by applicable law or agreed to in writing, software
113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * distributed under the License is distributed on an "AS IS" BASIS,
123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * See the License for the specific language governing permissions and
143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * limitations under the License.
153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu */
16507d11c9353666a75fee014565f900825a907691Andrew Scull
17507d11c9353666a75fee014565f900825a907691Andrew Scullpackage com.android.server.locksettings;
183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.annotation.NonNull;
203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.annotation.Nullable;
217374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roosimport android.app.admin.DevicePolicyManager;
222adc263ce97ae6c8291653490868879841d31a63Adrian Roosimport android.content.Context;
2360dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roosimport android.content.pm.UserInfo;
247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.IWeaver;
257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverConfig;
267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverReadResponse;
277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverReadStatus;
287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverStatus;
297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.security.GateKeeper;
307374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roosimport android.os.RemoteException;
317374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roosimport android.os.UserManager;
323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.service.gatekeeper.GateKeeperResponse;
333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.service.gatekeeper.IGateKeeperService;
343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.util.ArrayMap;
353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.util.Log;
367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.util.Slog;
373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport com.android.internal.annotations.VisibleForTesting;
393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport com.android.internal.util.ArrayUtils;
407374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roosimport com.android.internal.widget.ICheckCredentialProgressCallback;
413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport com.android.internal.widget.LockPatternUtils;
423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport com.android.internal.widget.VerifyCredentialResponse;
437374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roosimport com.android.server.locksettings.LockSettingsStorage.PersistentData;
443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport libcore.util.HexEncoding;
463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.nio.ByteBuffer;
483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.security.NoSuchAlgorithmException;
493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.security.SecureRandom;
507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.ArrayList;
513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.util.Arrays;
523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.util.Collections;
537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.HashSet;
547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.List;
557b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.Map;
567b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.NoSuchElementException;
573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.util.Set;
583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu/**
613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * A class that maintains the wrapping of synthetic password by user credentials or escrow tokens.
623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * It's (mostly) a pure storage for synthetic passwords, providing APIs to creating and destroying
633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * synthetic password blobs which are wrapped by user credentials or escrow tokens.
643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *
653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * Here is the assumptions it makes:
663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   Each user has one single synthetic password at any time.
673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   The SP has an associated password handle, which binds to the SID for that user. The password
683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   handle is persisted by SyntheticPasswordManager internally.
693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD
707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu * Information persisted on disk:
727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *   for each user (stored under DEFAULT_HANDLE):
737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *     SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user
747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *                     credential exists, cleared when user clears their credential.
75128180b2d38d2ae4ba1f440ef10534777d643f00Rubin Xu *     SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combined with escrow
767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *                     tokens. Destroyed when escrow support is turned off for the given user.
777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *     for each SP blob under the user (stored under the corresponding handle):
797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *       SP_BLOB_NAME: The encrypted synthetic password. Always exists.
807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *       PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP.
81128180b2d38d2ae4ba1f440ef10534777d643f00Rubin Xu *       SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME for the
82128180b2d38d2ae4ba1f440ef10534777d643f00Rubin Xu *                            purpose of secure deletion. Exists if this is a non-weaver SP
837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *                            (both password and token based), or it's a token-based SP under weaver.
847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *       WEAVER_SLOT: Metadata about the weaver slot used. Only exists if this is a SP under weaver.
857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu */
883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xupublic class SyntheticPasswordManager {
893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_BLOB_NAME = "spblob";
903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_E0_NAME = "e0";
913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_P1_NAME = "p1";
923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_HANDLE_NAME = "handle";
933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SECDISCARDABLE_NAME = "secdis";
943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int SECDISCARDABLE_LENGTH = 16 * 1024;
953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String PASSWORD_DATA_NAME = "pwd";
967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final String WEAVER_SLOT_NAME = "weaver";
973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    public static final long DEFAULT_HANDLE = 0L;
993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String DEFAULT_PASSWORD = "default-password";
1003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1017b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte WEAVER_VERSION = 1;
1027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final int INVALID_WEAVER_SLOT = -1;
1037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
1048c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu    private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1;
1058c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu    private static final byte SYNTHETIC_PASSWORD_VERSION = 2;
1063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
107f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
1083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // 256-bit synthetic password
1103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
1113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
112157bdddf4e1560519d513b61d6b3caf2d401c498Rubin Xu    private static final int PASSWORD_SCRYPT_N = 11;
1133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_SCRYPT_R = 3;
1143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_SCRYPT_P = 1;
1153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_SALT_LENGTH = 16;
1163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_TOKEN_LENGTH = 32;
1173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String TAG = "SyntheticPasswordManager";
1183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
1203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes();
1213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();
1223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
1233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
124e6527c1285cf38057d95c33f5fac4f4ea124e003Andrew Scull    private static final byte[] PERSONALIZATION_AUTHSECRET_KEY = "authsecret-hal".getBytes();
1253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
1263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
1277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
1287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
1297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
1303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class AuthenticationResult {
1323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public AuthenticationToken authToken;
1333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public VerifyCredentialResponse gkResponse;
13416c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu        public int credentialType;
1353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
1363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class AuthenticationToken {
1383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        /*
1393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * Here is the relationship between all three fields:
1403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
1413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * syntheticPassword = hash(P0 || P1)
1423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * E0 = P0 encrypted under syntheticPassword, stored on disk.
1433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         */
1443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @Nullable byte[] E0;
1453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @Nullable byte[] P1;
1463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @NonNull String syntheticPassword;
1473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public String deriveKeyStorePassword() {
1493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
1503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
1513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] deriveGkPassword() {
1543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
1553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    syntheticPassword.getBytes());
1563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] deriveDiskEncryptionKey() {
1593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
1603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    syntheticPassword.getBytes());
1613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
163e6527c1285cf38057d95c33f5fac4f4ea124e003Andrew Scull        public byte[] deriveVendorAuthSecret() {
164e6527c1285cf38057d95c33f5fac4f4ea124e003Andrew Scull            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_AUTHSECRET_KEY,
165e6527c1285cf38057d95c33f5fac4f4ea124e003Andrew Scull                    syntheticPassword.getBytes());
166e6527c1285cf38057d95c33f5fac4f4ea124e003Andrew Scull        }
167e6527c1285cf38057d95c33f5fac4f4ea124e003Andrew Scull
168f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        private void initialize(byte[] P0, byte[] P1) {
1693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.P1 = P1;
1703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.syntheticPassword = String.valueOf(HexEncoding.encode(
1713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    SyntheticPasswordCrypto.personalisedHash(
1723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                            PERSONALIZATION_SP_SPLIT, P0, P1)));
1733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.E0 = SyntheticPasswordCrypto.encrypt(this.syntheticPassword.getBytes(),
1743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    PERSONALIZATION_E0, P0);
1753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
177f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        public void recreate(byte[] secret) {
178f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            initialize(secret, this.P1);
179f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
180f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
1813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        protected static AuthenticationToken create() {
1823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            AuthenticationToken result = new AuthenticationToken();
1833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
1843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    secureRandom(SYNTHETIC_PASSWORD_LENGTH));
1853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
1863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] computeP0() {
1893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (E0 == null) {
1903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                return null;
1913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
1923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.decrypt(syntheticPassword.getBytes(), PERSONALIZATION_E0,
1933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    E0);
1943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
1963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class PasswordData {
1983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptN;
1993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptR;
2003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptP;
2013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public int passwordType;
2023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] salt;
2037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        // For GateKeeper-based credential, this is the password handle returned by GK,
2047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        // for weaver-based credential, this is empty.
2053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] passwordHandle;
2063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public static PasswordData create(int passwordType) {
2083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            PasswordData result = new PasswordData();
2093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptN = PASSWORD_SCRYPT_N;
2103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptR = PASSWORD_SCRYPT_R;
2113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptP = PASSWORD_SCRYPT_P;
2123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.passwordType = passwordType;
2133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.salt = secureRandom(PASSWORD_SALT_LENGTH);
2143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
2153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public static PasswordData fromBytes(byte[] data) {
2183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            PasswordData result = new PasswordData();
2193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            ByteBuffer buffer = ByteBuffer.allocate(data.length);
2203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(data, 0, data.length);
2213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.flip();
2223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.passwordType = buffer.getInt();
2233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptN = buffer.get();
2243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptR = buffer.get();
2253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptP = buffer.get();
2263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int saltLen = buffer.getInt();
2273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.salt = new byte[saltLen];
2283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.get(result.salt);
2293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int handleLen = buffer.getInt();
2307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (handleLen > 0) {
2317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.passwordHandle = new byte[handleLen];
2327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.get(result.passwordHandle);
2337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else {
2347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.passwordHandle = null;
2357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
2363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
2373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] toBytes() {
2407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
2427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    + Integer.BYTES + salt.length + Integer.BYTES +
2437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    (passwordHandle != null ? passwordHandle.length : 0));
2443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.putInt(passwordType);
2453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptN);
2463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptR);
2473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptP);
2483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.putInt(salt.length);
2493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(salt);
2507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (passwordHandle != null && passwordHandle.length > 0) {
2517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.putInt(passwordHandle.length);
2527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.put(passwordHandle);
2537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else {
2547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.putInt(0);
2557b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
2563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return buffer.array();
2573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
2593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2607b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    static class TokenData {
2617b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] secdiscardableOnDisk;
2627b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] weaverSecret;
2637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] aggregatedSecret;
2647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
2657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2662adc263ce97ae6c8291653490868879841d31a63Adrian Roos    private final Context mContext;
2673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private LockSettingsStorage mStorage;
2687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private IWeaver mWeaver;
2697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private WeaverConfig mWeaverConfig;
2703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2717374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    private final UserManager mUserManager;
2727374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
2732adc263ce97ae6c8291653490868879841d31a63Adrian Roos    public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
2742adc263ce97ae6c8291653490868879841d31a63Adrian Roos            UserManager userManager) {
2752adc263ce97ae6c8291653490868879841d31a63Adrian Roos        mContext = context;
2763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        mStorage = storage;
2777374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        mUserManager = userManager;
2783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
2793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    @VisibleForTesting
2817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected IWeaver getWeaverService() throws RemoteException {
2827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        try {
2837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return IWeaver.getService();
2847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } catch (NoSuchElementException e) {
2857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Slog.i(TAG, "Device does not support weaver");
2867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return null;
2877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
2887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
2897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    public synchronized void initWeaverService() {
2917b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (mWeaver != null) {
2927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return;
2937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
2947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        try {
2957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            mWeaverConfig = null;
2967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            mWeaver = getWeaverService();
2977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (mWeaver != null) {
2987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                mWeaver.getConfig((int status, WeaverConfig config) -> {
2997b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (status == WeaverStatus.OK && config.slots > 0) {
3007b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        mWeaverConfig = config;
3017b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
3027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Slog.e(TAG, "Failed to get weaver config, status " + status
3037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                                + " slots: " + config.slots);
3047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        mWeaver = null;
3057b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
3067b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                });
3077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
3087b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } catch (RemoteException e) {
3097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Slog.e(TAG, "Failed to get weaver service", e);
3107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private synchronized boolean isWeaverAvailable() {
3147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (mWeaver == null) {
3157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            //Re-initializing weaver in case there was a transient error preventing access to it.
3167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            initWeaverService();
3177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return mWeaver != null && mWeaverConfig.slots > 0;
3197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    /**
3227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * Enroll the given key value pair into the specified weaver slot. if the given key is null,
3237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * a default all-zero key is used. If the value is not specified, a fresh random secret is
3247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * generated as the value.
3257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     *
3267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @return the value stored in the weaver slot
3277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @throws RemoteException
3287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     */
3297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
3307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throws RemoteException {
3317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
3327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid slot for weaver");
3337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key == null) {
3357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            key = new byte[mWeaverConfig.keySize];
3367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else if (key.length != mWeaverConfig.keySize) {
3377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid key size for weaver");
3387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (value == null) {
3407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            value = secureRandom(mWeaverConfig.valueSize);
3417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
3437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (writeStatus != WeaverStatus.OK) {
3447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
3457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return null;
3467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return value;
3487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    /**
3517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * Verify the supplied key against a weaver slot, returning a response indicating whether
3527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * the verification is successful, throttled or failed. If successful, the bound secret
3537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * is also returned.
3547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @throws RemoteException
3557b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     */
3567b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
3577b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
3587b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid slot for weaver");
3597b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3607b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key == null) {
3617b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            key = new byte[mWeaverConfig.keySize];
3627b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else if (key.length != mWeaverConfig.keySize) {
3637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid key size for weaver");
3647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
3667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
3677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            switch (status) {
3687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.OK:
3697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = new VerifyCredentialResponse(
3707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            fromByteArrayList(readResponse.value));
3717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.THROTTLE:
3737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = new VerifyCredentialResponse(readResponse.timeout);
3747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
3757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.INCORRECT_KEY:
3777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (readResponse.timeout == 0) {
3787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        response[0] = VerifyCredentialResponse.ERROR;
3797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
3807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
3817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        response[0] = new VerifyCredentialResponse(readResponse.timeout);
3827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
3837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
3847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.FAILED:
3867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = VerifyCredentialResponse.ERROR;
3877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
3887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu               default:
3907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   response[0] = VerifyCredentialResponse.ERROR;
3917b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
3927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   break;
3937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
3947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        });
3957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return response[0];
3967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    public void removeUser(int userId) {
3992f22ee4d16c591b233423ef2940a657670ff3e65Rubin Xu        for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
4002f22ee4d16c591b233423ef2940a657670ff3e65Rubin Xu            destroyWeaverSlot(handle, userId);
4012f22ee4d16c591b233423ef2940a657670ff3e65Rubin Xu            destroySPBlobKey(getHandleName(handle));
4027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
4037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
4043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public int getCredentialType(long handle, int userId) {
4063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
4073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (passwordData == null) {
4083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
4093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return LockPatternUtils.CREDENTIAL_TYPE_NONE;
4103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
4113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return PasswordData.fromBytes(passwordData).passwordType;
4123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
4153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Initializing a new Authentication token, possibly from an existing credential and hash.
4163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * The authentication token would bear a randomly-generated synthetic password.
4183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * This method has the side effect of rebinding the SID of the given user to the
4203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * newly-generated SP.
4213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * If the existing credential hash is non-null, the existing SID mill be migrated so
4233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * the synthetic password in the authentication token will produce the same SID
4243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
4257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * in a per-user data storage.)
4263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * If the existing credential hash is null, it means the given user should have no SID so
4283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
4293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * the supplied credential parameter is also ignored.
4303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Also saves the escrow information necessary to re-generate the synthetic password under
4323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
4333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * password escrow should be disabled completely on the given user.
4343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
4363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
4373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            byte[] hash, String credential, int userId) throws RemoteException {
4383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationToken result = AuthenticationToken.create();
4393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response;
4403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (hash != null) {
4413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            response = gatekeeper.enroll(userId, hash, credential.getBytes(),
4423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    result.deriveGkPassword());
4433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
4443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
4453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                clearSidForUser(userId);
4463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            } else {
4473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                saveSyntheticPasswordHandle(response.getPayload(), userId);
4483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
4493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else {
4503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            clearSidForUser(userId);
4513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
4523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveEscrowData(result, userId);
4533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
4543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
4573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
4583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Used when adding password to previously-unsecured devices.
4593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
4603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
4613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int userId) throws RemoteException {
4623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
4633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                authToken.deriveGkPassword());
4643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
4653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.e(TAG, "Fail to create new SID for user " + userId);
4663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return;
4673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
4683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveSyntheticPasswordHandle(response.getPayload(), userId);
4693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // Nuke the SP handle (and as a result, its SID) for the given user.
4723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void clearSidForUser(int userId) {
473aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
4743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public boolean hasSidForUser(int userId) {
4773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
4783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // if null, it means there is no SID associated with the user
4813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // This can happen if the user is migrated to SP but currently
4823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // do not have a lockscreen password.
4833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadSyntheticPasswordHandle(int userId) {
4843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
4853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
4883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
4893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
4923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
4933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
4943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return authToken.E0 != null && authToken.P1 != null;
4953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveEscrowData(AuthenticationToken authToken, int userId) {
4983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId);
4993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId);
5003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
5013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
502f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean hasEscrowData(int userId) {
503f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
504f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
505f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
506f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
5073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void destroyEscrowData(int userId) {
508aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
509aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
5103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
5113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
5127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private int loadWeaverSlot(long handle, int userId) {
5137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final int LENGTH = Byte.BYTES + Integer.BYTES;
5147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId);
5157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (data == null || data.length != LENGTH) {
5167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return INVALID_WEAVER_SLOT;
5177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ByteBuffer buffer = ByteBuffer.allocate(LENGTH);
5197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.put(data, 0, data.length);
5207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.flip();
5217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (buffer.get() != WEAVER_VERSION) {
5227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Log.e(TAG, "Invalid weaver slot version of handle " + handle);
5237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return INVALID_WEAVER_SLOT;
5247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return buffer.getInt();
5267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void saveWeaverSlot(int slot, long handle, int userId) {
5297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
5307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.put(WEAVER_VERSION);
5317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.putInt(slot);
5327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
5337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void destroyWeaverSlot(long handle, int userId) {
5367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int slot = loadWeaverSlot(handle, userId);
537a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu        destroyState(WEAVER_SLOT_NAME, handle, userId);
5387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot != INVALID_WEAVER_SLOT) {
539a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu            Set<Integer> usedSlots = getUsedWeaverSlots();
540a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu            if (!usedSlots.contains(slot)) {
541a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
542a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                try {
543a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                    weaverEnroll(slot, null, null);
544a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                } catch (RemoteException e) {
545a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                    Log.w(TAG, "Failed to destroy slot", e);
546a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                }
547a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu            } else {
548a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
5497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
553a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu    /**
554a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * Return the set of weaver slots that are currently in use by all users on the device.
555a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * <p>
556a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * <em>Note:</em> Users who are in the process of being deleted are not tracked here
557a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * (due to them being marked as partial in UserManager so not visible from
558a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * {@link UserManager#getUsers}). As a result their weaver slots will not be considered
559a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * taken and can be reused by new users. Care should be taken when cleaning up the
560a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * deleted user in {@link #removeUser}, to prevent a reused slot from being erased
561a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * unintentionally.
562a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     */
563a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu    private Set<Integer> getUsedWeaverSlots() {
5647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
5657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                WEAVER_SLOT_NAME);
5667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        HashSet<Integer> slots = new HashSet<>();
5677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) {
5687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            for (Long handle : entry.getValue()) {
5697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                int slot = loadWeaverSlot(handle, entry.getKey());
5707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                slots.add(slot);
5717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
573a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu        return slots;
574a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu    }
575a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu
576a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu    private int getNextAvailableWeaverSlot() {
577a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu        Set<Integer> usedSlots = getUsedWeaverSlots();
5787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < mWeaverConfig.slots; i++) {
579a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu            if (!usedSlots.contains(i)) {
5807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return i;
5817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        throw new RuntimeException("Run out of weaver slots.");
5847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
5873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Create a new password based SP blob based on the supplied authentication token, such that
5883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
5893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * in the same authentication token.
5903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
5913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * This method only creates SP blob wrapping around the given synthetic password and does not
5923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
5933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * is consistent with the device state by calling other APIs in this class.
5943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
5953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * @see #newSidForUser
5963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * @see #clearSidForUser
5973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
5983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
5997374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            String credential, int credentialType, AuthenticationToken authToken,
6007374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int requestedQuality, int userId)
6013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    throws RemoteException {
6023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
6033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
6043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credential = DEFAULT_PASSWORD;
6053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
6063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        long handle = generateHandle();
6083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        PasswordData pwd = PasswordData.create(credentialType);
6093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] pwdToken = computePasswordToken(credential, pwd);
6107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final long sid;
6117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] applicationId;
6127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
6137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
6147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // Weaver based user password
6157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int weaverSlot = getNextAvailableWeaverSlot();
616a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu            Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
6177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
6187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (weaverSecret == null) {
6197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Fail to enroll user password under weaver " + userId);
6207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return DEFAULT_HANDLE;
6217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
6227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            saveWeaverSlot(weaverSlot, handle, userId);
6237374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot);
6243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            pwd.passwordHandle = null;
6267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            sid = GateKeeper.INVALID_SECURE_USER_ID;
6277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret);
6287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
62954c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
63054c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            // to prevent them from accumulating and causing problems.
63154c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            gatekeeper.clearSecureUserId(fakeUid(userId));
6327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // GateKeeper based user password
6337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
6347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    passwordTokenToGkInput(pwdToken));
6357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
6367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
6377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return DEFAULT_HANDLE;
6387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
6397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            pwd.passwordHandle = response.getPayload();
6407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            sid = sidFromPasswordHandle(pwd.passwordHandle);
6417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderSecdiscardable(pwdToken,
6427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    createSecdiscardable(handle, userId));
6437374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            synchronizeFrpPassword(pwd, requestedQuality, userId);
6443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
6453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
6463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
6483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                applicationId, sid, userId);
6493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return handle;
6503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
6513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6527374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
6537374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            String userCredential, int credentialType,
6547374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
6557374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        PersistentData persistentData = mStorage.readPersistentDataBlock();
6567374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        if (persistentData.type == PersistentData.TYPE_SP) {
6577374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
6587374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            byte[] pwdToken = computePasswordToken(userCredential, pwd);
6597374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
660b7d693217706fe6d40ad6665d5e9b6775812d10eAdrian Roos            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
661b7d693217706fe6d40ad6665d5e9b6775812d10eAdrian Roos                    0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
6627374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            return VerifyCredentialResponse.fromGateKeeperResponse(response);
6637374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
6647374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
6657374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            byte[] pwdToken = computePasswordToken(userCredential, pwd);
6667374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int weaverSlot = persistentData.userId;
6677374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
6687374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
6697374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        } else {
6707374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            Log.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
6717374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                    + persistentData.type);
6727374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            return VerifyCredentialResponse.ERROR;
6737374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        }
6747374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    }
6757374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
6767374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
67760dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos    public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) {
67860dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos        if (mStorage.getPersistentDataBlock() != null
6792adc263ce97ae6c8291653490868879841d31a63Adrian Roos                && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) {
68060dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos            PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle,
68160dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                    userInfo.id));
68260dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos            if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
68360dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                int weaverSlot = loadWeaverSlot(handle, userInfo.id);
68460dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                if (weaverSlot != INVALID_WEAVER_SLOT) {
68560dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                    synchronizeWeaverFrpPassword(pwd, requestedQuality, userInfo.id, weaverSlot);
68660dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                } else {
68760dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                    synchronizeFrpPassword(pwd, requestedQuality, userInfo.id);
68860dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                }
68960dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos            }
69060dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos        }
69160dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos    }
69260dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos
6937374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    private void synchronizeFrpPassword(PasswordData pwd,
6947374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int requestedQuality, int userId) {
6957374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        if (mStorage.getPersistentDataBlock() != null
6962adc263ce97ae6c8291653490868879841d31a63Adrian Roos                && LockPatternUtils.userOwnsFrpCredential(mContext,
6972adc263ce97ae6c8291653490868879841d31a63Adrian Roos                mUserManager.getUserInfo(userId))) {
6987374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
6997374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
7007374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                        pwd.toBytes());
7017374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            } else {
7027374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
7037374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            }
7047374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        }
7057374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    }
7067374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
7077374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
7087374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int weaverSlot) {
7097374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        if (mStorage.getPersistentDataBlock() != null
7102adc263ce97ae6c8291653490868879841d31a63Adrian Roos                && LockPatternUtils.userOwnsFrpCredential(mContext,
7112adc263ce97ae6c8291653490868879841d31a63Adrian Roos                mUserManager.getUserInfo(userId))) {
7127374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
7137374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
7147374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                        requestedQuality, pwd.toBytes());
7157374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            } else {
7167374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, 0, 0, null);
7177374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            }
7187374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        }
7197374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    }
7207374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
7217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
722f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
723f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public long createTokenBasedSyntheticPassword(byte[] token, int userId) {
724f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        long handle = generateHandle();
725f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
726f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            tokenMap.put(userId, new ArrayMap<>());
727f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
7287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        TokenData tokenData = new TokenData();
7297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
7307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
7317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
7327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
7337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            PERSONALISATION_WEAVER_TOKEN, secdiscardable);
7347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
7357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.secdiscardableOnDisk = secdiscardable;
7367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.weaverSecret = null;
7377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
7387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
7397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
7407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        tokenMap.get(userId).put(handle, tokenData);
741f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return handle;
742f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
743f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
744f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public Set<Long> getPendingTokensForUser(int userId) {
745f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
746f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return Collections.emptySet();
747f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
748f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return tokenMap.get(userId).keySet();
749f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
750f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
751f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean removePendingToken(long handle, int userId) {
752f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
753f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
754f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
755f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return tokenMap.get(userId).remove(handle) != null;
756f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
757f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
758f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
759f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            int userId) {
760f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
761f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
762f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
7637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        TokenData tokenData = tokenMap.get(userId).get(handle);
7647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (tokenData == null) {
765f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
766f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
767f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!loadEscrowData(authToken, userId)) {
768f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            Log.w(TAG, "User is not escrowable");
769f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
770f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
7717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
7727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int slot = getNextAvailableWeaverSlot();
7737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            try {
774a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
7757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                weaverEnroll(slot, null, tokenData.weaverSecret);
7767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } catch (RemoteException e) {
7777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
7787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return false;
7797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
7807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            saveWeaverSlot(slot, handle, userId);
7817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
7827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
783f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
7847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                tokenData.aggregatedSecret, 0L, userId);
785f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        tokenMap.get(userId).remove(handle);
786f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return true;
787f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
788f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
7893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
7903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            byte[] applicationId, long sid, int userId) {
791f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        final byte[] secret;
792f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
793f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            secret = authToken.computeP0();
794f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
795f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            secret = authToken.syntheticPassword.getBytes();
796f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
7973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
7983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] blob = new byte[content.length + 1 + 1];
7993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        blob[0] = SYNTHETIC_PASSWORD_VERSION;
8003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        blob[1] = type;
8013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(content, 0, blob, 2, content.length);
8023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_BLOB_NAME, blob, handle, userId);
8033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
8043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
8053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
8063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Decrypt a synthetic password by supplying the user credential and corresponding password
8073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
8083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * verification to referesh the SID & Auth token maintained by the system.
80916c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu     * Note: the credential type is not validated here since there are call sites where the type is
81016c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu     * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
8113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
8123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
813cf326f1882476d76a63f5b700a397088c312e050Rubin Xu            long handle, String credential, int userId,
814cf326f1882476d76a63f5b700a397088c312e050Rubin Xu            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
8153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (credential == null) {
8163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credential = DEFAULT_PASSWORD;
8173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationResult result = new AuthenticationResult();
8193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
82016c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu        result.credentialType = pwd.passwordType;
8213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] pwdToken = computePasswordToken(credential, pwd);
8223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
8237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] applicationId;
8248c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        final long sid;
8257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int weaverSlot = loadWeaverSlot(handle, userId);
8267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (weaverSlot != INVALID_WEAVER_SLOT) {
8277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // Weaver based user password
8287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (!isWeaverAvailable()) {
8297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "No weaver service to unwrap password based SP");
8307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
8317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
8337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
8347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
8357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
8378c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            sid = GateKeeper.INVALID_SECURE_USER_ID;
8387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
8397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
8407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
8417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
8427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    pwd.passwordHandle, gkPwdToken);
8437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int responseCode = response.getResponseCode();
8447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (responseCode == GateKeeperResponse.RESPONSE_OK) {
8457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.OK;
8467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                if (response.getShouldReEnroll()) {
8477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
8487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            pwd.passwordHandle, gkPwdToken, gkPwdToken);
8497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
8507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        pwd.passwordHandle = reenrollResponse.getPayload();
8517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
8527374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                        synchronizeFrpPassword(pwd,
8537374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                pwd.passwordType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
8547374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
8557374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
8567374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                /* TODO(roosa): keep the same password quality */,
8577374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                userId);
8587b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
8597b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.w(TAG, "Fail to re-enroll user password for user " + userId);
8607b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        // continue the flow anyway
8617b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
8623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                }
8637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
8647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
8657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else  {
8677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
8687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
8708c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            sid = sidFromPasswordHandle(pwd.passwordHandle);
8717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderSecdiscardable(pwdToken,
8727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    loadSecdiscardable(handle, userId));
8733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
874cf326f1882476d76a63f5b700a397088c312e050Rubin Xu        // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
875cf326f1882476d76a63f5b700a397088c312e050Rubin Xu        // Notify the callback so the keyguard UI can proceed immediately.
876cf326f1882476d76a63f5b700a397088c312e050Rubin Xu        if (progressCallback != null) {
877cf326f1882476d76a63f5b700a397088c312e050Rubin Xu            progressCallback.onCredentialVerified();
878cf326f1882476d76a63f5b700a397088c312e050Rubin Xu        }
8793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
8808c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu                applicationId, sid, userId);
8813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
8823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
8833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
8843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
8853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
8863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
887f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    /**
888f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * Decrypt a synthetic password by supplying an escrow token and corresponding token
889f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
890f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * verification to referesh the SID & Auth token maintained by the system.
891f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     */
892f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
893f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
894f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                    throws RemoteException {
895f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        AuthenticationResult result = new AuthenticationResult();
8967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] secdiscardable = loadSecdiscardable(handle, userId);
8977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int slotId = loadWeaverSlot(handle, userId);
8987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slotId != INVALID_WEAVER_SLOT) {
8997b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (!isWeaverAvailable()) {
9007b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "No weaver service to unwrap token based SP");
9017b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
9027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
9037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
9047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            VerifyCredentialResponse response = weaverVerify(slotId, null);
9057b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
9067b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response.getPayload() == null) {
9077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
9087b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
9097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
9107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
9117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
9127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    PERSONALISATION_WEAVER_TOKEN, secdiscardable);
9137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
9147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
915f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
9168c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu                applicationId, 0L, userId);
917f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (result.authToken != null) {
918f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
919f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            if (result.gkResponse == null) {
920f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                // The user currently has no password. return OK with null payload so null
921f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                // is propagated to unlockUser()
922f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                result.gkResponse = VerifyCredentialResponse.OK;
923f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            }
924f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
925f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.gkResponse = VerifyCredentialResponse.ERROR;
926f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
927f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return result;
928f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
929f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
9303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
9318c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            byte[] applicationId, long sid, int userId) {
9323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
9333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (blob == null) {
9343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
9353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9368c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        final byte version = blob[0];
9378c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        if (version != SYNTHETIC_PASSWORD_VERSION && version != SYNTHETIC_PASSWORD_VERSION_V1) {
9383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            throw new RuntimeException("Unknown blob version");
9393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (blob[1] != type) {
9413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            throw new RuntimeException("Invalid blob type");
9423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9438c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        final byte[] secret;
9448c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
9458c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
9468c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu                    Arrays.copyOfRange(blob, 2, blob.length), applicationId);
9478c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        } else {
9488c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            secret = decryptSPBlob(getHandleName(handle),
9493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                Arrays.copyOfRange(blob, 2, blob.length), applicationId);
9508c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        }
9513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (secret == null) {
9523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.e(TAG, "Fail to decrypt SP for user " + userId);
9533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
9543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationToken result = new AuthenticationToken();
956f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
957f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            if (!loadEscrowData(result, userId)) {
958f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                Log.e(TAG, "User is not escrowable: " + userId);
959f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                return null;
960f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            }
961f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.recreate(secret);
962f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
963f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.syntheticPassword = new String(secret);
964f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
9658c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
9668c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            Log.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
9678c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
9688c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        }
9693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
9703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
9733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
9743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * if required.
9753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
9763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Normally performing verifyChallenge with an AuthenticationToken should always return
9773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * RESPONSE_OK, since user authentication failures are detected earlier when trying to
9783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * decrypt SP.
9793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
9808b30ec3f49d4c8037bc6aa03ed6dd91aff3968adRubin Xu    public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
9813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
9823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] spHandle = loadSyntheticPasswordHandle(userId);
9833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (spHandle == null) {
9843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            // There is no password handle associated with the given user, i.e. the user is not
9853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            // secured by lockscreen and has no SID, so just return here;
9863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
9873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        VerifyCredentialResponse result;
9893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
9903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                spHandle, auth.deriveGkPassword());
9913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        int responseCode = response.getResponseCode();
9923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (responseCode == GateKeeperResponse.RESPONSE_OK) {
9933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = new VerifyCredentialResponse(response.getPayload());
9943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (response.getShouldReEnroll()) {
9953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                response = gatekeeper.enroll(userId, spHandle,
9963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                        spHandle, auth.deriveGkPassword());
9973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
9983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    spHandle = response.getPayload();
9993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    saveSyntheticPasswordHandle(spHandle, userId);
10003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    // Call self again to re-verify with updated handle
10013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    return verifyChallenge(gatekeeper, auth, challenge, userId);
10023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                } else {
10033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
10043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    // Fall through, return existing handle
10053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                }
10063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
10073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
10083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = new VerifyCredentialResponse(response.getTimeout());
10093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else {
10103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = VerifyCredentialResponse.ERROR;
10113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
10123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
10133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public boolean existsHandle(long handle, int userId) {
10163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return hasState(SP_BLOB_NAME, handle, userId);
10173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1019f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
1020f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        destroySyntheticPassword(handle, userId);
1021aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SECDISCARDABLE_NAME, handle, userId);
1022f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
1023f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
10243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
10253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroySyntheticPassword(handle, userId);
1026aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SECDISCARDABLE_NAME, handle, userId);
1027aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(PASSWORD_DATA_NAME, handle, userId);
10283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void destroySyntheticPassword(long handle, int userId) {
1031aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_BLOB_NAME, handle, userId);
10323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroySPBlobKey(getHandleName(handle));
10337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
10347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            destroyWeaverSlot(handle, userId);
10357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
10367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
10377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
10387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
10397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash(
10407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                PERSONALISATION_WEAVER_PASSWORD, secret);
10417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] result = new byte[data.length + weaverSecret.length];
10427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        System.arraycopy(data, 0, result, 0, data.length);
10437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length);
10447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
10453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
10483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
10493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
10503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] result = new byte[data.length + secdiscardable.length];
10513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(data, 0, result, 0, data.length);
10523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
10533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
10543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] createSecdiscardable(long handle, int userId) {
10573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
10587b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveSecdiscardable(handle, data, userId);
10593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return data;
10603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10627b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
10637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
10647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
10657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
10663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadSecdiscardable(long handle, int userId) {
10673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return loadState(SECDISCARDABLE_NAME, handle, userId);
10683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private boolean hasState(String stateName, long handle, int userId) {
10713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
10723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadState(String stateName, long handle, int userId) {
10753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return mStorage.readSyntheticPasswordState(userId, handle, stateName);
10763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveState(String stateName, byte[] data, long handle, int userId) {
10793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
10803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1082aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu    private void destroyState(String stateName, long handle, int userId) {
1083aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
10843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
10873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
10883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
10913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
10923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected void destroySPBlobKey(String keyAlias) {
10953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
10963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public static long generateHandle() {
10993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        SecureRandom rng = new SecureRandom();
11003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        long result;
11013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        do {
11023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = rng.nextLong();
11033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } while (result == DEFAULT_HANDLE);
11043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
11053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private int fakeUid(int uid) {
11083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return 100000 + uid;
11093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected static byte[] secureRandom(int length) {
11123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        try {
11133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
11143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } catch (NoSuchAlgorithmException e) {
11153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            e.printStackTrace();
11163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
11173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
11183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private String getHandleName(long handle) {
11213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
11223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] computePasswordToken(String password, PasswordData data) {
11253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
11263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                PASSWORD_TOKEN_LENGTH);
11273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] passwordTokenToGkInput(byte[] token) {
11303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
11313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] passwordTokenToWeaverKey(byte[] token) {
11347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
11357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key.length < mWeaverConfig.keySize) {
11367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("weaver key length too small");
11377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
11387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return Arrays.copyOf(key, mWeaverConfig.keySize);
11397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
11407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
11413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected long sidFromPasswordHandle(byte[] handle) {
11423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return nativeSidFromPasswordHandle(handle);
11433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
11463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return nativeScrypt(password.getBytes(), salt, N, r, p, outLen);
11473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    native long nativeSidFromPasswordHandle(byte[] handle);
11503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen);
11513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected static ArrayList<Byte> toByteArrayList(byte[] data) {
11537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ArrayList<Byte> result = new ArrayList<Byte>(data.length);
11547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < data.length; i++) {
11557b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result.add(data[i]);
11567b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
11577b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
11587b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
11597b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
11607b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
11617b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] result = new byte[data.size()];
11627b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < data.size(); i++) {
11637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result[i] = data.get(i);
11647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
11657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
11667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
11677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
11683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
11693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public static String bytesToHex(byte[] bytes) {
11703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (bytes == null) {
11713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return "null";
11723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
11733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        char[] hexChars = new char[bytes.length * 2];
11743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        for ( int j = 0; j < bytes.length; j++ ) {
11753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int v = bytes[j] & 0xFF;
11763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            hexChars[j * 2] = hexArray[v >>> 4];
11773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
11783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
11793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return new String(hexChars);
11803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu}
1182