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();
126f01e90789eb27bc538df13374b6e67991c0ea829Rubin Xu    private static final byte[] PERSONALIZATION_PASSWORD_HASH = "pw-hash".getBytes();
1273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
1287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
1297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
1307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
1313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class AuthenticationResult {
1333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public AuthenticationToken authToken;
1343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public VerifyCredentialResponse gkResponse;
13516c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu        public int credentialType;
1363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
1373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class AuthenticationToken {
1393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        /*
1403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * Here is the relationship between all three fields:
1413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
1423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * syntheticPassword = hash(P0 || P1)
1433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * E0 = P0 encrypted under syntheticPassword, stored on disk.
1443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         */
1453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @Nullable byte[] E0;
1463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @Nullable byte[] P1;
1473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @NonNull String syntheticPassword;
1483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public String deriveKeyStorePassword() {
1503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
1513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
1523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] deriveGkPassword() {
1553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
1563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    syntheticPassword.getBytes());
1573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] deriveDiskEncryptionKey() {
1603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
1613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    syntheticPassword.getBytes());
1623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
164e6527c1285cf38057d95c33f5fac4f4ea124e003Andrew Scull        public byte[] deriveVendorAuthSecret() {
165e6527c1285cf38057d95c33f5fac4f4ea124e003Andrew Scull            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_AUTHSECRET_KEY,
166e6527c1285cf38057d95c33f5fac4f4ea124e003Andrew Scull                    syntheticPassword.getBytes());
167e6527c1285cf38057d95c33f5fac4f4ea124e003Andrew Scull        }
168e6527c1285cf38057d95c33f5fac4f4ea124e003Andrew Scull
169f01e90789eb27bc538df13374b6e67991c0ea829Rubin Xu        public byte[] derivePasswordHashFactor() {
170f01e90789eb27bc538df13374b6e67991c0ea829Rubin Xu            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_PASSWORD_HASH,
171f01e90789eb27bc538df13374b6e67991c0ea829Rubin Xu                    syntheticPassword.getBytes());
172f01e90789eb27bc538df13374b6e67991c0ea829Rubin Xu        }
173f01e90789eb27bc538df13374b6e67991c0ea829Rubin Xu
174f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        private void initialize(byte[] P0, byte[] P1) {
1753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.P1 = P1;
1763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.syntheticPassword = String.valueOf(HexEncoding.encode(
1773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    SyntheticPasswordCrypto.personalisedHash(
1783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                            PERSONALIZATION_SP_SPLIT, P0, P1)));
1793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.E0 = SyntheticPasswordCrypto.encrypt(this.syntheticPassword.getBytes(),
1803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    PERSONALIZATION_E0, P0);
1813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
183f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        public void recreate(byte[] secret) {
184f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            initialize(secret, this.P1);
185f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
186f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
1873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        protected static AuthenticationToken create() {
1883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            AuthenticationToken result = new AuthenticationToken();
1893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
1903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    secureRandom(SYNTHETIC_PASSWORD_LENGTH));
1913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
1923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] computeP0() {
1953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (E0 == null) {
1963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                return null;
1973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
1983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.decrypt(syntheticPassword.getBytes(), PERSONALIZATION_E0,
1993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    E0);
2003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
2023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class PasswordData {
2043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptN;
2053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptR;
2063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptP;
2073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public int passwordType;
2083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] salt;
2097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        // For GateKeeper-based credential, this is the password handle returned by GK,
2107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        // for weaver-based credential, this is empty.
2113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] passwordHandle;
2123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public static PasswordData create(int passwordType) {
2143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            PasswordData result = new PasswordData();
2153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptN = PASSWORD_SCRYPT_N;
2163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptR = PASSWORD_SCRYPT_R;
2173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptP = PASSWORD_SCRYPT_P;
2183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.passwordType = passwordType;
2193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.salt = secureRandom(PASSWORD_SALT_LENGTH);
2203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
2213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public static PasswordData fromBytes(byte[] data) {
2243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            PasswordData result = new PasswordData();
2253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            ByteBuffer buffer = ByteBuffer.allocate(data.length);
2263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(data, 0, data.length);
2273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.flip();
2283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.passwordType = buffer.getInt();
2293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptN = buffer.get();
2303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptR = buffer.get();
2313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptP = buffer.get();
2323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int saltLen = buffer.getInt();
2333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.salt = new byte[saltLen];
2343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.get(result.salt);
2353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int handleLen = buffer.getInt();
2367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (handleLen > 0) {
2377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.passwordHandle = new byte[handleLen];
2387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.get(result.passwordHandle);
2397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else {
2407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.passwordHandle = null;
2417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
2423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
2433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] toBytes() {
2467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
2487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    + Integer.BYTES + salt.length + Integer.BYTES +
2497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    (passwordHandle != null ? passwordHandle.length : 0));
2503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.putInt(passwordType);
2513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptN);
2523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptR);
2533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptP);
2543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.putInt(salt.length);
2553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(salt);
2567b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (passwordHandle != null && passwordHandle.length > 0) {
2577b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.putInt(passwordHandle.length);
2587b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.put(passwordHandle);
2597b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else {
2607b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.putInt(0);
2617b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
2623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return buffer.array();
2633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
2653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    static class TokenData {
2677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] secdiscardableOnDisk;
2687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] weaverSecret;
2697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] aggregatedSecret;
2707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
2717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2722adc263ce97ae6c8291653490868879841d31a63Adrian Roos    private final Context mContext;
2733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private LockSettingsStorage mStorage;
2747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private IWeaver mWeaver;
2757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private WeaverConfig mWeaverConfig;
2763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2777374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    private final UserManager mUserManager;
2787374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
2792adc263ce97ae6c8291653490868879841d31a63Adrian Roos    public SyntheticPasswordManager(Context context, LockSettingsStorage storage,
2802adc263ce97ae6c8291653490868879841d31a63Adrian Roos            UserManager userManager) {
2812adc263ce97ae6c8291653490868879841d31a63Adrian Roos        mContext = context;
2823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        mStorage = storage;
2837374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        mUserManager = userManager;
2843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
2853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    @VisibleForTesting
2877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected IWeaver getWeaverService() throws RemoteException {
2887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        try {
2897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return IWeaver.getService();
2907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } catch (NoSuchElementException e) {
2917b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Slog.i(TAG, "Device does not support weaver");
2927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return null;
2937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
2947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
2957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    public synchronized void initWeaverService() {
2977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (mWeaver != null) {
2987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return;
2997b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3007b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        try {
3017b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            mWeaverConfig = null;
3027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            mWeaver = getWeaverService();
3037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (mWeaver != null) {
3047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                mWeaver.getConfig((int status, WeaverConfig config) -> {
3057b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (status == WeaverStatus.OK && config.slots > 0) {
3067b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        mWeaverConfig = config;
3077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
3087b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Slog.e(TAG, "Failed to get weaver config, status " + status
3097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                                + " slots: " + config.slots);
3107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        mWeaver = null;
3117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
3127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                });
3137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
3147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } catch (RemoteException e) {
3157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Slog.e(TAG, "Failed to get weaver service", e);
3167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private synchronized boolean isWeaverAvailable() {
3207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (mWeaver == null) {
3217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            //Re-initializing weaver in case there was a transient error preventing access to it.
3227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            initWeaverService();
3237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return mWeaver != null && mWeaverConfig.slots > 0;
3257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    /**
3287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * Enroll the given key value pair into the specified weaver slot. if the given key is null,
3297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * a default all-zero key is used. If the value is not specified, a fresh random secret is
3307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * generated as the value.
3317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     *
3327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @return the value stored in the weaver slot
3337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @throws RemoteException
3347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     */
3357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
3367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throws RemoteException {
3377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
3387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid slot for weaver");
3397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key == null) {
3417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            key = new byte[mWeaverConfig.keySize];
3427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else if (key.length != mWeaverConfig.keySize) {
3437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid key size for weaver");
3447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (value == null) {
3467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            value = secureRandom(mWeaverConfig.valueSize);
3477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
3497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (writeStatus != WeaverStatus.OK) {
3507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
3517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return null;
3527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return value;
3547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3557b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3567b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    /**
3577b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * Verify the supplied key against a weaver slot, returning a response indicating whether
3587b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * the verification is successful, throttled or failed. If successful, the bound secret
3597b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * is also returned.
3607b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @throws RemoteException
3617b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     */
3627b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
3637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
3647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid slot for weaver");
3657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key == null) {
3677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            key = new byte[mWeaverConfig.keySize];
3687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else if (key.length != mWeaverConfig.keySize) {
3697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid key size for weaver");
3707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
3727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
3737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            switch (status) {
3747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.OK:
3757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = new VerifyCredentialResponse(
3767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            fromByteArrayList(readResponse.value));
3777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.THROTTLE:
3797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = new VerifyCredentialResponse(readResponse.timeout);
3807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
3817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.INCORRECT_KEY:
3837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (readResponse.timeout == 0) {
3847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        response[0] = VerifyCredentialResponse.ERROR;
3857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
3867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
3877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        response[0] = new VerifyCredentialResponse(readResponse.timeout);
3887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
3897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
3907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3917b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.FAILED:
3927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = VerifyCredentialResponse.ERROR;
3937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
3947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu               default:
3967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   response[0] = VerifyCredentialResponse.ERROR;
3977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
3987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   break;
3997b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
4007b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        });
4017b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return response[0];
4027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
4037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
4047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    public void removeUser(int userId) {
4052f22ee4d16c591b233423ef2940a657670ff3e65Rubin Xu        for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
4062f22ee4d16c591b233423ef2940a657670ff3e65Rubin Xu            destroyWeaverSlot(handle, userId);
4072f22ee4d16c591b233423ef2940a657670ff3e65Rubin Xu            destroySPBlobKey(getHandleName(handle));
4087b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
4097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
4103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public int getCredentialType(long handle, int userId) {
4123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
4133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (passwordData == null) {
4143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
4153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return LockPatternUtils.CREDENTIAL_TYPE_NONE;
4163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
4173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return PasswordData.fromBytes(passwordData).passwordType;
4183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
4213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Initializing a new Authentication token, possibly from an existing credential and hash.
4223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * The authentication token would bear a randomly-generated synthetic password.
4243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * This method has the side effect of rebinding the SID of the given user to the
4263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * newly-generated SP.
4273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * If the existing credential hash is non-null, the existing SID mill be migrated so
4293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * the synthetic password in the authentication token will produce the same SID
4303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
4317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * in a per-user data storage.)
4323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * If the existing credential hash is null, it means the given user should have no SID so
4343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
4353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * the supplied credential parameter is also ignored.
4363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Also saves the escrow information necessary to re-generate the synthetic password under
4383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
4393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * password escrow should be disabled completely on the given user.
4403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
4423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
4433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            byte[] hash, String credential, int userId) throws RemoteException {
4443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationToken result = AuthenticationToken.create();
4453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response;
4463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (hash != null) {
4473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            response = gatekeeper.enroll(userId, hash, credential.getBytes(),
4483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    result.deriveGkPassword());
4493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
4503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
4513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                clearSidForUser(userId);
4523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            } else {
4533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                saveSyntheticPasswordHandle(response.getPayload(), userId);
4543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
4553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else {
4563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            clearSidForUser(userId);
4573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
4583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveEscrowData(result, userId);
4593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
4603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
4633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
4643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Used when adding password to previously-unsecured devices.
4653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
4663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
4673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int userId) throws RemoteException {
4683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
4693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                authToken.deriveGkPassword());
4703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
4713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.e(TAG, "Fail to create new SID for user " + userId);
4723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return;
4733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
4743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveSyntheticPasswordHandle(response.getPayload(), userId);
4753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // Nuke the SP handle (and as a result, its SID) for the given user.
4783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void clearSidForUser(int userId) {
479aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
4803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public boolean hasSidForUser(int userId) {
4833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
4843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // if null, it means there is no SID associated with the user
4873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // This can happen if the user is migrated to SP but currently
4883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // do not have a lockscreen password.
4893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadSyntheticPasswordHandle(int userId) {
4903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
4913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
4943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
4953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
4983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
4993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
5003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return authToken.E0 != null && authToken.P1 != null;
5013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
5023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
5033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveEscrowData(AuthenticationToken authToken, int userId) {
5043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId);
5053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId);
5063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
5073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
508f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean hasEscrowData(int userId) {
509f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
510f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
511f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
512f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
5133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void destroyEscrowData(int userId) {
514aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
515aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
5163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
5173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
5187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private int loadWeaverSlot(long handle, int userId) {
5197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final int LENGTH = Byte.BYTES + Integer.BYTES;
5207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId);
5217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (data == null || data.length != LENGTH) {
5227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return INVALID_WEAVER_SLOT;
5237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ByteBuffer buffer = ByteBuffer.allocate(LENGTH);
5257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.put(data, 0, data.length);
5267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.flip();
5277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (buffer.get() != WEAVER_VERSION) {
5287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Log.e(TAG, "Invalid weaver slot version of handle " + handle);
5297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return INVALID_WEAVER_SLOT;
5307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return buffer.getInt();
5327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void saveWeaverSlot(int slot, long handle, int userId) {
5357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
5367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.put(WEAVER_VERSION);
5377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.putInt(slot);
5387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
5397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void destroyWeaverSlot(long handle, int userId) {
5427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int slot = loadWeaverSlot(handle, userId);
543a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu        destroyState(WEAVER_SLOT_NAME, handle, userId);
5447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot != INVALID_WEAVER_SLOT) {
545a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu            Set<Integer> usedSlots = getUsedWeaverSlots();
546a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu            if (!usedSlots.contains(slot)) {
547a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
548a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                try {
549a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                    weaverEnroll(slot, null, null);
550a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                } catch (RemoteException e) {
551a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                    Log.w(TAG, "Failed to destroy slot", e);
552a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                }
553a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu            } else {
554a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
5557b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5567b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5577b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5587b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
559a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu    /**
560a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * Return the set of weaver slots that are currently in use by all users on the device.
561a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * <p>
562a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * <em>Note:</em> Users who are in the process of being deleted are not tracked here
563a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * (due to them being marked as partial in UserManager so not visible from
564a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * {@link UserManager#getUsers}). As a result their weaver slots will not be considered
565a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * taken and can be reused by new users. Care should be taken when cleaning up the
566a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * deleted user in {@link #removeUser}, to prevent a reused slot from being erased
567a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     * unintentionally.
568a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu     */
569a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu    private Set<Integer> getUsedWeaverSlots() {
5707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
5717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                WEAVER_SLOT_NAME);
5727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        HashSet<Integer> slots = new HashSet<>();
5737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) {
5747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            for (Long handle : entry.getValue()) {
5757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                int slot = loadWeaverSlot(handle, entry.getKey());
5767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                slots.add(slot);
5777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
579a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu        return slots;
580a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu    }
581a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu
582a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu    private int getNextAvailableWeaverSlot() {
583a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu        Set<Integer> usedSlots = getUsedWeaverSlots();
5847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < mWeaverConfig.slots; i++) {
585a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu            if (!usedSlots.contains(i)) {
5867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return i;
5877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        throw new RuntimeException("Run out of weaver slots.");
5907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5917b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
5933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Create a new password based SP blob based on the supplied authentication token, such that
5943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
5953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * in the same authentication token.
5963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
5973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * This method only creates SP blob wrapping around the given synthetic password and does not
5983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
5993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * is consistent with the device state by calling other APIs in this class.
6003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
6013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * @see #newSidForUser
6023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * @see #clearSidForUser
6033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
6043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
6057374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            String credential, int credentialType, AuthenticationToken authToken,
6067374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int requestedQuality, int userId)
6073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    throws RemoteException {
6083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
6093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
6103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credential = DEFAULT_PASSWORD;
6113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
6123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        long handle = generateHandle();
6143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        PasswordData pwd = PasswordData.create(credentialType);
6153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] pwdToken = computePasswordToken(credential, pwd);
6167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final long sid;
6177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] applicationId;
6187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
6197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
6207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // Weaver based user password
6217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int weaverSlot = getNextAvailableWeaverSlot();
622a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu            Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
6237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
6247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (weaverSecret == null) {
6257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Fail to enroll user password under weaver " + userId);
6267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return DEFAULT_HANDLE;
6277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
6287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            saveWeaverSlot(weaverSlot, handle, userId);
6297374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot);
6303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            pwd.passwordHandle = null;
6327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            sid = GateKeeper.INVALID_SECURE_USER_ID;
6337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret);
6347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
63554c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
63654c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            // to prevent them from accumulating and causing problems.
63754c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            gatekeeper.clearSecureUserId(fakeUid(userId));
6387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // GateKeeper based user password
6397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
6407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    passwordTokenToGkInput(pwdToken));
6417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
6427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
6437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return DEFAULT_HANDLE;
6447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
6457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            pwd.passwordHandle = response.getPayload();
6467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            sid = sidFromPasswordHandle(pwd.passwordHandle);
6477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderSecdiscardable(pwdToken,
6487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    createSecdiscardable(handle, userId));
6497374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            synchronizeFrpPassword(pwd, requestedQuality, userId);
6503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
6513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
6523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
6543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                applicationId, sid, userId);
6553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return handle;
6563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
6573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6587374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
6597374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            String userCredential, int credentialType,
6607374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
6617374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        PersistentData persistentData = mStorage.readPersistentDataBlock();
6627374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        if (persistentData.type == PersistentData.TYPE_SP) {
6637374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
6647374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            byte[] pwdToken = computePasswordToken(userCredential, pwd);
6657374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
666b7d693217706fe6d40ad6665d5e9b6775812d10eAdrian Roos            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
667b7d693217706fe6d40ad6665d5e9b6775812d10eAdrian Roos                    0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
6687374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            return VerifyCredentialResponse.fromGateKeeperResponse(response);
6697374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
6707374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
6717374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            byte[] pwdToken = computePasswordToken(userCredential, pwd);
6727374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int weaverSlot = persistentData.userId;
6737374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
6747374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
6757374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        } else {
6767374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            Log.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
6777374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                    + persistentData.type);
6787374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            return VerifyCredentialResponse.ERROR;
6797374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        }
6807374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    }
6817374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
6827374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
68360dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos    public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) {
68460dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos        if (mStorage.getPersistentDataBlock() != null
6852adc263ce97ae6c8291653490868879841d31a63Adrian Roos                && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) {
68660dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos            PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle,
68760dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                    userInfo.id));
68860dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos            if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
68960dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                int weaverSlot = loadWeaverSlot(handle, userInfo.id);
69060dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                if (weaverSlot != INVALID_WEAVER_SLOT) {
69160dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                    synchronizeWeaverFrpPassword(pwd, requestedQuality, userInfo.id, weaverSlot);
69260dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                } else {
69360dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                    synchronizeFrpPassword(pwd, requestedQuality, userInfo.id);
69460dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos                }
69560dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos            }
69660dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos        }
69760dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos    }
69860dcbbf9232dc4b19c39c0a131b9819ea86dbda2Adrian Roos
6997374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    private void synchronizeFrpPassword(PasswordData pwd,
7007374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int requestedQuality, int userId) {
7017374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        if (mStorage.getPersistentDataBlock() != null
7022adc263ce97ae6c8291653490868879841d31a63Adrian Roos                && LockPatternUtils.userOwnsFrpCredential(mContext,
7032adc263ce97ae6c8291653490868879841d31a63Adrian Roos                mUserManager.getUserInfo(userId))) {
7047374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
7057374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
7067374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                        pwd.toBytes());
7077374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            } else {
7087374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
7097374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            }
7107374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        }
7117374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    }
7127374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
7137374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
7147374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int weaverSlot) {
7157374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        if (mStorage.getPersistentDataBlock() != null
7162adc263ce97ae6c8291653490868879841d31a63Adrian Roos                && LockPatternUtils.userOwnsFrpCredential(mContext,
7172adc263ce97ae6c8291653490868879841d31a63Adrian Roos                mUserManager.getUserInfo(userId))) {
7187374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
7197374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
7207374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                        requestedQuality, pwd.toBytes());
7217374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            } else {
7227374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, 0, 0, null);
7237374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            }
7247374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        }
7257374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    }
7267374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
7277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
728f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
729f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public long createTokenBasedSyntheticPassword(byte[] token, int userId) {
730f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        long handle = generateHandle();
731f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
732f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            tokenMap.put(userId, new ArrayMap<>());
733f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
7347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        TokenData tokenData = new TokenData();
7357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
7367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
7377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
7387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
7397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            PERSONALISATION_WEAVER_TOKEN, secdiscardable);
7407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
7417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.secdiscardableOnDisk = secdiscardable;
7427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.weaverSecret = null;
7437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
7447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
7457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
7467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        tokenMap.get(userId).put(handle, tokenData);
747f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return handle;
748f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
749f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
750f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public Set<Long> getPendingTokensForUser(int userId) {
751f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
752f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return Collections.emptySet();
753f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
754f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return tokenMap.get(userId).keySet();
755f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
756f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
757f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean removePendingToken(long handle, int userId) {
758f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
759f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
760f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
761f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return tokenMap.get(userId).remove(handle) != null;
762f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
763f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
764f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
765f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            int userId) {
766f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
767f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
768f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
7697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        TokenData tokenData = tokenMap.get(userId).get(handle);
7707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (tokenData == null) {
771f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
772f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
773f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!loadEscrowData(authToken, userId)) {
774f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            Log.w(TAG, "User is not escrowable");
775f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
776f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
7777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
7787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int slot = getNextAvailableWeaverSlot();
7797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            try {
780a339df080d7dfacbec7915f869115dfa91ce4b12Rubin Xu                Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
7817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                weaverEnroll(slot, null, tokenData.weaverSecret);
7827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } catch (RemoteException e) {
7837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
7847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return false;
7857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
7867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            saveWeaverSlot(slot, handle, userId);
7877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
7887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
789f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
7907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                tokenData.aggregatedSecret, 0L, userId);
791f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        tokenMap.get(userId).remove(handle);
792f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return true;
793f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
794f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
7953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
7963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            byte[] applicationId, long sid, int userId) {
797f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        final byte[] secret;
798f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
799f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            secret = authToken.computeP0();
800f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
801f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            secret = authToken.syntheticPassword.getBytes();
802f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
8033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
8043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] blob = new byte[content.length + 1 + 1];
8053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        blob[0] = SYNTHETIC_PASSWORD_VERSION;
8063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        blob[1] = type;
8073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(content, 0, blob, 2, content.length);
8083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_BLOB_NAME, blob, handle, userId);
8093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
8103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
8113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
8123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Decrypt a synthetic password by supplying the user credential and corresponding password
8133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
8143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * verification to referesh the SID & Auth token maintained by the system.
81516c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu     * Note: the credential type is not validated here since there are call sites where the type is
81616c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu     * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
8173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
8183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
819cf326f1882476d76a63f5b700a397088c312e050Rubin Xu            long handle, String credential, int userId,
820cf326f1882476d76a63f5b700a397088c312e050Rubin Xu            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
8213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (credential == null) {
8223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credential = DEFAULT_PASSWORD;
8233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationResult result = new AuthenticationResult();
8253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
82616c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu        result.credentialType = pwd.passwordType;
8273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] pwdToken = computePasswordToken(credential, pwd);
8283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
8297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] applicationId;
8308c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        final long sid;
8317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int weaverSlot = loadWeaverSlot(handle, userId);
8327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (weaverSlot != INVALID_WEAVER_SLOT) {
8337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // Weaver based user password
8347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (!isWeaverAvailable()) {
8357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "No weaver service to unwrap password based SP");
8367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
8377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
8397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
8407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
8417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
8438c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            sid = GateKeeper.INVALID_SECURE_USER_ID;
8447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
8457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
8467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
8477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
8487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    pwd.passwordHandle, gkPwdToken);
8497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int responseCode = response.getResponseCode();
8507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (responseCode == GateKeeperResponse.RESPONSE_OK) {
8517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.OK;
8527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                if (response.getShouldReEnroll()) {
8537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
8547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            pwd.passwordHandle, gkPwdToken, gkPwdToken);
8557b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
8567b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        pwd.passwordHandle = reenrollResponse.getPayload();
8577b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
8587374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                        synchronizeFrpPassword(pwd,
8597374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                pwd.passwordType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
8607374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
8617374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
8627374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                /* TODO(roosa): keep the same password quality */,
8637374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                userId);
8647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
8657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.w(TAG, "Fail to re-enroll user password for user " + userId);
8667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        // continue the flow anyway
8677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
8683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                }
8697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
8707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
8717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else  {
8737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
8747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
8768c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            sid = sidFromPasswordHandle(pwd.passwordHandle);
8777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderSecdiscardable(pwdToken,
8787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    loadSecdiscardable(handle, userId));
8793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
880cf326f1882476d76a63f5b700a397088c312e050Rubin Xu        // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
881cf326f1882476d76a63f5b700a397088c312e050Rubin Xu        // Notify the callback so the keyguard UI can proceed immediately.
882cf326f1882476d76a63f5b700a397088c312e050Rubin Xu        if (progressCallback != null) {
883cf326f1882476d76a63f5b700a397088c312e050Rubin Xu            progressCallback.onCredentialVerified();
884cf326f1882476d76a63f5b700a397088c312e050Rubin Xu        }
8853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
8868c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu                applicationId, sid, userId);
8873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
8883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
8893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
8903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
8913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
8923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
893f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    /**
894f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * Decrypt a synthetic password by supplying an escrow token and corresponding token
895f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
896f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * verification to referesh the SID & Auth token maintained by the system.
897f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     */
898f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
899f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
900f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                    throws RemoteException {
901f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        AuthenticationResult result = new AuthenticationResult();
9027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] secdiscardable = loadSecdiscardable(handle, userId);
9037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int slotId = loadWeaverSlot(handle, userId);
9047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slotId != INVALID_WEAVER_SLOT) {
9057b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (!isWeaverAvailable()) {
9067b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "No weaver service to unwrap token based SP");
9077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
9087b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
9097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
9107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            VerifyCredentialResponse response = weaverVerify(slotId, null);
9117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
9127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response.getPayload() == null) {
9137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
9147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
9157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
9167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
9177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
9187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    PERSONALISATION_WEAVER_TOKEN, secdiscardable);
9197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
9207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
921f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
9228c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu                applicationId, 0L, userId);
923f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (result.authToken != null) {
924f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
925f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            if (result.gkResponse == null) {
926f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                // The user currently has no password. return OK with null payload so null
927f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                // is propagated to unlockUser()
928f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                result.gkResponse = VerifyCredentialResponse.OK;
929f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            }
930f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
931f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.gkResponse = VerifyCredentialResponse.ERROR;
932f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
933f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return result;
934f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
935f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
9363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
9378c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            byte[] applicationId, long sid, int userId) {
9383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
9393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (blob == null) {
9403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
9413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9428c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        final byte version = blob[0];
9438c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        if (version != SYNTHETIC_PASSWORD_VERSION && version != SYNTHETIC_PASSWORD_VERSION_V1) {
9443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            throw new RuntimeException("Unknown blob version");
9453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (blob[1] != type) {
9473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            throw new RuntimeException("Invalid blob type");
9483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9498c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        final byte[] secret;
9508c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
9518c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
9528c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu                    Arrays.copyOfRange(blob, 2, blob.length), applicationId);
9538c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        } else {
9548c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            secret = decryptSPBlob(getHandleName(handle),
9553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                Arrays.copyOfRange(blob, 2, blob.length), applicationId);
9568c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        }
9573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (secret == null) {
9583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.e(TAG, "Fail to decrypt SP for user " + userId);
9593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
9603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationToken result = new AuthenticationToken();
962f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
963f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            if (!loadEscrowData(result, userId)) {
964f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                Log.e(TAG, "User is not escrowable: " + userId);
965f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                return null;
966f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            }
967f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.recreate(secret);
968f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
969f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.syntheticPassword = new String(secret);
970f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
9718c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
9728c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            Log.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
9738c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu            createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
9748c52865a61e3d01c821d884b9814d70a72272bf1Rubin Xu        }
9753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
9763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
9793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
9803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * if required.
9813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
9823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Normally performing verifyChallenge with an AuthenticationToken should always return
9833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * RESPONSE_OK, since user authentication failures are detected earlier when trying to
9843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * decrypt SP.
9853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
9868b30ec3f49d4c8037bc6aa03ed6dd91aff3968adRubin Xu    public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
9873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
9883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] spHandle = loadSyntheticPasswordHandle(userId);
9893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (spHandle == null) {
9903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            // There is no password handle associated with the given user, i.e. the user is not
9913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            // secured by lockscreen and has no SID, so just return here;
9923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
9933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        VerifyCredentialResponse result;
9953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
9963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                spHandle, auth.deriveGkPassword());
9973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        int responseCode = response.getResponseCode();
9983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (responseCode == GateKeeperResponse.RESPONSE_OK) {
9993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = new VerifyCredentialResponse(response.getPayload());
10003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (response.getShouldReEnroll()) {
10013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                response = gatekeeper.enroll(userId, spHandle,
10023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                        spHandle, auth.deriveGkPassword());
10033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
10043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    spHandle = response.getPayload();
10053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    saveSyntheticPasswordHandle(spHandle, userId);
10063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    // Call self again to re-verify with updated handle
10073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    return verifyChallenge(gatekeeper, auth, challenge, userId);
10083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                } else {
10093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
10103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    // Fall through, return existing handle
10113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                }
10123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
10133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
10143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = new VerifyCredentialResponse(response.getTimeout());
10153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else {
10163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = VerifyCredentialResponse.ERROR;
10173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
10183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
10193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public boolean existsHandle(long handle, int userId) {
10223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return hasState(SP_BLOB_NAME, handle, userId);
10233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1025f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
1026f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        destroySyntheticPassword(handle, userId);
1027aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SECDISCARDABLE_NAME, handle, userId);
1028f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
1029f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
10303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
10313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroySyntheticPassword(handle, userId);
1032aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SECDISCARDABLE_NAME, handle, userId);
1033aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(PASSWORD_DATA_NAME, handle, userId);
10343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void destroySyntheticPassword(long handle, int userId) {
1037aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_BLOB_NAME, handle, userId);
10383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroySPBlobKey(getHandleName(handle));
10397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
10407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            destroyWeaverSlot(handle, userId);
10417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
10427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
10437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
10447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
10457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash(
10467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                PERSONALISATION_WEAVER_PASSWORD, secret);
10477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] result = new byte[data.length + weaverSecret.length];
10487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        System.arraycopy(data, 0, result, 0, data.length);
10497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length);
10507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
10513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
10543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
10553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
10563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] result = new byte[data.length + secdiscardable.length];
10573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(data, 0, result, 0, data.length);
10583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
10593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
10603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] createSecdiscardable(long handle, int userId) {
10633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
10647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveSecdiscardable(handle, data, userId);
10653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return data;
10663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
10697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
10707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
10717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
10723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadSecdiscardable(long handle, int userId) {
10733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return loadState(SECDISCARDABLE_NAME, handle, userId);
10743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private boolean hasState(String stateName, long handle, int userId) {
10773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
10783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadState(String stateName, long handle, int userId) {
10813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return mStorage.readSyntheticPasswordState(userId, handle, stateName);
10823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveState(String stateName, byte[] data, long handle, int userId) {
10853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
10863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1088aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu    private void destroyState(String stateName, long handle, int userId) {
1089aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
10903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
10933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
10943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
10973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
10983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected void destroySPBlobKey(String keyAlias) {
11013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
11023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public static long generateHandle() {
11053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        SecureRandom rng = new SecureRandom();
11063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        long result;
11073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        do {
11083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = rng.nextLong();
11093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } while (result == DEFAULT_HANDLE);
11103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
11113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private int fakeUid(int uid) {
11143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return 100000 + uid;
11153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected static byte[] secureRandom(int length) {
11183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        try {
11193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
11203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } catch (NoSuchAlgorithmException e) {
11213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            e.printStackTrace();
11223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
11233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
11243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private String getHandleName(long handle) {
11273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
11283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] computePasswordToken(String password, PasswordData data) {
11313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
11323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                PASSWORD_TOKEN_LENGTH);
11333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] passwordTokenToGkInput(byte[] token) {
11363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
11373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] passwordTokenToWeaverKey(byte[] token) {
11407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
11417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key.length < mWeaverConfig.keySize) {
11427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("weaver key length too small");
11437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
11447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return Arrays.copyOf(key, mWeaverConfig.keySize);
11457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
11467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
11473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected long sidFromPasswordHandle(byte[] handle) {
11483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return nativeSidFromPasswordHandle(handle);
11493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
11523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return nativeScrypt(password.getBytes(), salt, N, r, p, outLen);
11533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    native long nativeSidFromPasswordHandle(byte[] handle);
11563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen);
11573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
11587b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected static ArrayList<Byte> toByteArrayList(byte[] data) {
11597b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ArrayList<Byte> result = new ArrayList<Byte>(data.length);
11607b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < data.length; i++) {
11617b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result.add(data[i]);
11627b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
11637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
11647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
11657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
11667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
11677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] result = new byte[data.size()];
11687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < data.size(); i++) {
11697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result[i] = data.get(i);
11707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
11717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
11727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
11737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
11743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
11753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public static String bytesToHex(byte[] bytes) {
11763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (bytes == null) {
11773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return "null";
11783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
11793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        char[] hexChars = new char[bytes.length * 2];
11803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        for ( int j = 0; j < bytes.length; j++ ) {
11813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int v = bytes[j] & 0xFF;
11823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            hexChars[j * 2] = hexArray[v >>> 4];
11833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
11843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
11853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return new String(hexChars);
11863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu}
1188