SyntheticPasswordManager.java revision 16c823ebf398138add71ad8ff82053e3676f85c3
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;
227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.IWeaver;
237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverConfig;
247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverReadResponse;
257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverReadStatus;
267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverStatus;
277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.security.GateKeeper;
287374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roosimport android.os.RemoteException;
297374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roosimport android.os.UserManager;
303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.service.gatekeeper.GateKeeperResponse;
313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.service.gatekeeper.IGateKeeperService;
323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.util.ArrayMap;
333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.util.Log;
347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.util.Slog;
353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport com.android.internal.annotations.VisibleForTesting;
373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport com.android.internal.util.ArrayUtils;
387374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roosimport com.android.internal.widget.ICheckCredentialProgressCallback;
393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport com.android.internal.widget.LockPatternUtils;
403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport com.android.internal.widget.VerifyCredentialResponse;
417374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roosimport com.android.server.locksettings.LockSettingsStorage.PersistentData;
423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport libcore.util.HexEncoding;
443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.nio.ByteBuffer;
463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.security.NoSuchAlgorithmException;
473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.security.SecureRandom;
487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.ArrayList;
493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.util.Arrays;
503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.util.Collections;
517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.HashSet;
527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.List;
537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.Map;
547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.NoSuchElementException;
553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.util.Set;
563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu/**
593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * A class that maintains the wrapping of synthetic password by user credentials or escrow tokens.
603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * It's (mostly) a pure storage for synthetic passwords, providing APIs to creating and destroying
613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * synthetic password blobs which are wrapped by user credentials or escrow tokens.
623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *
633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * Here is the assumptions it makes:
643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   Each user has one single synthetic password at any time.
653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   The SP has an associated password handle, which binds to the SID for that user. The password
663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   handle is persisted by SyntheticPasswordManager internally.
673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD
687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu * Information persisted on disk:
707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *   for each user (stored under DEFAULT_HANDLE):
717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *     SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user
727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *                     credential exists, cleared when user clears their credential.
73128180b2d38d2ae4ba1f440ef10534777d643f00Rubin Xu *     SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combined with escrow
747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *                     tokens. Destroyed when escrow support is turned off for the given user.
757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *     for each SP blob under the user (stored under the corresponding handle):
777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *       SP_BLOB_NAME: The encrypted synthetic password. Always exists.
787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *       PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP.
79128180b2d38d2ae4ba1f440ef10534777d643f00Rubin Xu *       SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME for the
80128180b2d38d2ae4ba1f440ef10534777d643f00Rubin Xu *                            purpose of secure deletion. Exists if this is a non-weaver SP
817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *                            (both password and token based), or it's a token-based SP under weaver.
827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *       WEAVER_SLOT: Metadata about the weaver slot used. Only exists if this is a SP under weaver.
837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu */
863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xupublic class SyntheticPasswordManager {
873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_BLOB_NAME = "spblob";
883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_E0_NAME = "e0";
893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_P1_NAME = "p1";
903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_HANDLE_NAME = "handle";
913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SECDISCARDABLE_NAME = "secdis";
923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int SECDISCARDABLE_LENGTH = 16 * 1024;
933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String PASSWORD_DATA_NAME = "pwd";
947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final String WEAVER_SLOT_NAME = "weaver";
953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    public static final long DEFAULT_HANDLE = 0L;
973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String DEFAULT_PASSWORD = "default-password";
983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
997b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte WEAVER_VERSION = 1;
1007b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final int INVALID_WEAVER_SLOT = -1;
1017b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
1023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte SYNTHETIC_PASSWORD_VERSION = 1;
1033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
104f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
1053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // 256-bit synthetic password
1073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
1083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
109157bdddf4e1560519d513b61d6b3caf2d401c498Rubin Xu    private static final int PASSWORD_SCRYPT_N = 11;
1103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_SCRYPT_R = 3;
1113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_SCRYPT_P = 1;
1123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_SALT_LENGTH = 16;
1133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_TOKEN_LENGTH = 32;
1143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String TAG = "SyntheticPasswordManager";
1153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
1173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes();
1183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();
1193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
1203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
1213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
1223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
1237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
1247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
1257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
1263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class AuthenticationResult {
1283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public AuthenticationToken authToken;
1293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public VerifyCredentialResponse gkResponse;
13016c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu        public int credentialType;
1313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
1323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class AuthenticationToken {
1343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        /*
1353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * Here is the relationship between all three fields:
1363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
1373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * syntheticPassword = hash(P0 || P1)
1383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * E0 = P0 encrypted under syntheticPassword, stored on disk.
1393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         */
1403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @Nullable byte[] E0;
1413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @Nullable byte[] P1;
1423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @NonNull String syntheticPassword;
1433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public String deriveKeyStorePassword() {
1453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
1463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
1473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] deriveGkPassword() {
1503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
1513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    syntheticPassword.getBytes());
1523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] deriveDiskEncryptionKey() {
1553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
1563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    syntheticPassword.getBytes());
1573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
159f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        private void initialize(byte[] P0, byte[] P1) {
1603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.P1 = P1;
1613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.syntheticPassword = String.valueOf(HexEncoding.encode(
1623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    SyntheticPasswordCrypto.personalisedHash(
1633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                            PERSONALIZATION_SP_SPLIT, P0, P1)));
1643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.E0 = SyntheticPasswordCrypto.encrypt(this.syntheticPassword.getBytes(),
1653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    PERSONALIZATION_E0, P0);
1663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
168f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        public void recreate(byte[] secret) {
169f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            initialize(secret, this.P1);
170f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
171f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
1723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        protected static AuthenticationToken create() {
1733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            AuthenticationToken result = new AuthenticationToken();
1743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
1753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    secureRandom(SYNTHETIC_PASSWORD_LENGTH));
1763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
1773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] computeP0() {
1803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (E0 == null) {
1813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                return null;
1823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
1833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.decrypt(syntheticPassword.getBytes(), PERSONALIZATION_E0,
1843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    E0);
1853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
1873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class PasswordData {
1893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptN;
1903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptR;
1913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptP;
1923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public int passwordType;
1933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] salt;
1947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        // For GateKeeper-based credential, this is the password handle returned by GK,
1957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        // for weaver-based credential, this is empty.
1963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] passwordHandle;
1973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public static PasswordData create(int passwordType) {
1993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            PasswordData result = new PasswordData();
2003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptN = PASSWORD_SCRYPT_N;
2013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptR = PASSWORD_SCRYPT_R;
2023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptP = PASSWORD_SCRYPT_P;
2033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.passwordType = passwordType;
2043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.salt = secureRandom(PASSWORD_SALT_LENGTH);
2053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
2063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public static PasswordData fromBytes(byte[] data) {
2093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            PasswordData result = new PasswordData();
2103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            ByteBuffer buffer = ByteBuffer.allocate(data.length);
2113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(data, 0, data.length);
2123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.flip();
2133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.passwordType = buffer.getInt();
2143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptN = buffer.get();
2153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptR = buffer.get();
2163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptP = buffer.get();
2173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int saltLen = buffer.getInt();
2183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.salt = new byte[saltLen];
2193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.get(result.salt);
2203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int handleLen = buffer.getInt();
2217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (handleLen > 0) {
2227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.passwordHandle = new byte[handleLen];
2237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.get(result.passwordHandle);
2247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else {
2257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.passwordHandle = null;
2267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
2273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
2283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] toBytes() {
2317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
2337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    + Integer.BYTES + salt.length + Integer.BYTES +
2347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    (passwordHandle != null ? passwordHandle.length : 0));
2353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.putInt(passwordType);
2363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptN);
2373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptR);
2383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptP);
2393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.putInt(salt.length);
2403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(salt);
2417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (passwordHandle != null && passwordHandle.length > 0) {
2427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.putInt(passwordHandle.length);
2437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.put(passwordHandle);
2447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else {
2457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.putInt(0);
2467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
2473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return buffer.array();
2483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
2503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    static class TokenData {
2527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] secdiscardableOnDisk;
2537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] weaverSecret;
2547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] aggregatedSecret;
2557b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
2567b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private LockSettingsStorage mStorage;
2587b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private IWeaver mWeaver;
2597b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private WeaverConfig mWeaverConfig;
2603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2617374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    private final UserManager mUserManager;
2627374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
2637374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    public SyntheticPasswordManager(LockSettingsStorage storage, UserManager userManager) {
2643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        mStorage = storage;
2657374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        mUserManager = userManager;
2663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
2673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    @VisibleForTesting
2697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected IWeaver getWeaverService() throws RemoteException {
2707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        try {
2717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return IWeaver.getService();
2727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } catch (NoSuchElementException e) {
2737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Slog.i(TAG, "Device does not support weaver");
2747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return null;
2757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
2767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
2777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    public synchronized void initWeaverService() {
2797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (mWeaver != null) {
2807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return;
2817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
2827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        try {
2837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            mWeaverConfig = null;
2847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            mWeaver = getWeaverService();
2857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (mWeaver != null) {
2867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                mWeaver.getConfig((int status, WeaverConfig config) -> {
2877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (status == WeaverStatus.OK && config.slots > 0) {
2887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        mWeaverConfig = config;
2897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
2907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Slog.e(TAG, "Failed to get weaver config, status " + status
2917b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                                + " slots: " + config.slots);
2927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        mWeaver = null;
2937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
2947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                });
2957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
2967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } catch (RemoteException e) {
2977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Slog.e(TAG, "Failed to get weaver service", e);
2987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
2997b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3007b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3017b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private synchronized boolean isWeaverAvailable() {
3027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (mWeaver == null) {
3037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            //Re-initializing weaver in case there was a transient error preventing access to it.
3047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            initWeaverService();
3057b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3067b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return mWeaver != null && mWeaverConfig.slots > 0;
3077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3087b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    /**
3107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * Enroll the given key value pair into the specified weaver slot. if the given key is null,
3117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * a default all-zero key is used. If the value is not specified, a fresh random secret is
3127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * generated as the value.
3137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     *
3147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @return the value stored in the weaver slot
3157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @throws RemoteException
3167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     */
3177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
3187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throws RemoteException {
3197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
3207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid slot for weaver");
3217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key == null) {
3237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            key = new byte[mWeaverConfig.keySize];
3247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else if (key.length != mWeaverConfig.keySize) {
3257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid key size for weaver");
3267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (value == null) {
3287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            value = secureRandom(mWeaverConfig.valueSize);
3297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
3317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (writeStatus != WeaverStatus.OK) {
3327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
3337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return null;
3347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return value;
3367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    /**
3397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * Verify the supplied key against a weaver slot, returning a response indicating whether
3407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * the verification is successful, throttled or failed. If successful, the bound secret
3417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * is also returned.
3427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @throws RemoteException
3437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     */
3447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
3457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
3467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid slot for weaver");
3477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key == null) {
3497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            key = new byte[mWeaverConfig.keySize];
3507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else if (key.length != mWeaverConfig.keySize) {
3517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid key size for weaver");
3527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
3547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
3557b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            switch (status) {
3567b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.OK:
3577b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = new VerifyCredentialResponse(
3587b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            fromByteArrayList(readResponse.value));
3597b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3607b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.THROTTLE:
3617b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = new VerifyCredentialResponse(readResponse.timeout);
3627b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
3637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.INCORRECT_KEY:
3657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (readResponse.timeout == 0) {
3667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        response[0] = VerifyCredentialResponse.ERROR;
3677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
3687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
3697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        response[0] = new VerifyCredentialResponse(readResponse.timeout);
3707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
3717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
3727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.FAILED:
3747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = VerifyCredentialResponse.ERROR;
3757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
3767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu               default:
3787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   response[0] = VerifyCredentialResponse.ERROR;
3797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
3807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   break;
3817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
3827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        });
3837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return response[0];
3847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    public void removeUser(int userId) {
3877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
3887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            for (long handle : mStorage.listSyntheticPasswordHandlesForUser(WEAVER_SLOT_NAME,
3897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    userId)) {
3907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                destroyWeaverSlot(handle, userId);
3917b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
3927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
3953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public int getCredentialType(long handle, int userId) {
3963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
3973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (passwordData == null) {
3983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
3993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return LockPatternUtils.CREDENTIAL_TYPE_NONE;
4003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
4013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return PasswordData.fromBytes(passwordData).passwordType;
4023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
4053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Initializing a new Authentication token, possibly from an existing credential and hash.
4063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * The authentication token would bear a randomly-generated synthetic password.
4083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * This method has the side effect of rebinding the SID of the given user to the
4103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * newly-generated SP.
4113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * If the existing credential hash is non-null, the existing SID mill be migrated so
4133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * the synthetic password in the authentication token will produce the same SID
4143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
4157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * in a per-user data storage.)
4163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * If the existing credential hash is null, it means the given user should have no SID so
4183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
4193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * the supplied credential parameter is also ignored.
4203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Also saves the escrow information necessary to re-generate the synthetic password under
4223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
4233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * password escrow should be disabled completely on the given user.
4243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
4263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
4273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            byte[] hash, String credential, int userId) throws RemoteException {
4283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationToken result = AuthenticationToken.create();
4293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response;
4303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (hash != null) {
4313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            response = gatekeeper.enroll(userId, hash, credential.getBytes(),
4323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    result.deriveGkPassword());
4333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
4343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
4353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                clearSidForUser(userId);
4363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            } else {
4373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                saveSyntheticPasswordHandle(response.getPayload(), userId);
4383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
4393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else {
4403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            clearSidForUser(userId);
4413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
4423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveEscrowData(result, userId);
4433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
4443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
4473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
4483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Used when adding password to previously-unsecured devices.
4493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
4503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
4513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int userId) throws RemoteException {
4523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
4533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                authToken.deriveGkPassword());
4543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
4553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.e(TAG, "Fail to create new SID for user " + userId);
4563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return;
4573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
4583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveSyntheticPasswordHandle(response.getPayload(), userId);
4593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // Nuke the SP handle (and as a result, its SID) for the given user.
4623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void clearSidForUser(int userId) {
463aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
4643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public boolean hasSidForUser(int userId) {
4673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
4683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // if null, it means there is no SID associated with the user
4713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // This can happen if the user is migrated to SP but currently
4723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // do not have a lockscreen password.
4733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadSyntheticPasswordHandle(int userId) {
4743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
4753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
4783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
4793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
4823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
4833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
4843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return authToken.E0 != null && authToken.P1 != null;
4853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveEscrowData(AuthenticationToken authToken, int userId) {
4883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId);
4893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId);
4903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
492f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean hasEscrowData(int userId) {
493f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
494f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
495f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
496f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
4973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void destroyEscrowData(int userId) {
498aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId);
499aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId);
5003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
5013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
5027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private int loadWeaverSlot(long handle, int userId) {
5037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final int LENGTH = Byte.BYTES + Integer.BYTES;
5047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId);
5057b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (data == null || data.length != LENGTH) {
5067b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return INVALID_WEAVER_SLOT;
5077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5087b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ByteBuffer buffer = ByteBuffer.allocate(LENGTH);
5097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.put(data, 0, data.length);
5107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.flip();
5117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (buffer.get() != WEAVER_VERSION) {
5127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Log.e(TAG, "Invalid weaver slot version of handle " + handle);
5137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return INVALID_WEAVER_SLOT;
5147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return buffer.getInt();
5167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void saveWeaverSlot(int slot, long handle, int userId) {
5197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
5207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.put(WEAVER_VERSION);
5217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.putInt(slot);
5227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
5237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void destroyWeaverSlot(long handle, int userId) {
5267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int slot = loadWeaverSlot(handle, userId);
5277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot != INVALID_WEAVER_SLOT) {
5287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            try {
5297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                weaverEnroll(slot, null, null);
5307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } catch (RemoteException e) {
5317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.w(TAG, "Failed to destroy slot", e);
5327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
534aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(WEAVER_SLOT_NAME, handle, userId);
5357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private int getNextAvailableWeaverSlot() {
5387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
5397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                WEAVER_SLOT_NAME);
5407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        HashSet<Integer> slots = new HashSet<>();
5417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) {
5427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            for (Long handle : entry.getValue()) {
5437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                int slot = loadWeaverSlot(handle, entry.getKey());
5447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                slots.add(slot);
5457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < mWeaverConfig.slots; i++) {
5487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (!slots.contains(i)) {
5497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return i;
5507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        throw new RuntimeException("Run out of weaver slots.");
5537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
5563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Create a new password based SP blob based on the supplied authentication token, such that
5573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
5583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * in the same authentication token.
5593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
5603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * This method only creates SP blob wrapping around the given synthetic password and does not
5613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
5623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * is consistent with the device state by calling other APIs in this class.
5633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
5643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * @see #newSidForUser
5653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * @see #clearSidForUser
5663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
5673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
5687374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            String credential, int credentialType, AuthenticationToken authToken,
5697374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int requestedQuality, int userId)
5703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    throws RemoteException {
5713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
5723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
5733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credential = DEFAULT_PASSWORD;
5743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
5753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
5763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        long handle = generateHandle();
5773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        PasswordData pwd = PasswordData.create(credentialType);
5783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] pwdToken = computePasswordToken(credential, pwd);
5797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final long sid;
5807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] applicationId;
5817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
5837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // Weaver based user password
5847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int weaverSlot = getNextAvailableWeaverSlot();
5857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
5867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (weaverSecret == null) {
5877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Fail to enroll user password under weaver " + userId);
5887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return DEFAULT_HANDLE;
5897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            saveWeaverSlot(weaverSlot, handle, userId);
5917374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot);
5923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
5937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            pwd.passwordHandle = null;
5947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            sid = GateKeeper.INVALID_SECURE_USER_ID;
5957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret);
5967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
59754c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
59854c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            // to prevent them from accumulating and causing problems.
59954c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            gatekeeper.clearSecureUserId(fakeUid(userId));
6007b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // GateKeeper based user password
6017b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
6027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    passwordTokenToGkInput(pwdToken));
6037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
6047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
6057b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return DEFAULT_HANDLE;
6067b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
6077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            pwd.passwordHandle = response.getPayload();
6087b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            sid = sidFromPasswordHandle(pwd.passwordHandle);
6097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderSecdiscardable(pwdToken,
6107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    createSecdiscardable(handle, userId));
6117374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            synchronizeFrpPassword(pwd, requestedQuality, userId);
6123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
6133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
6143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
6163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                applicationId, sid, userId);
6173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return handle;
6183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
6193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6207374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
6217374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            String userCredential, int credentialType,
6227374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
6237374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        PersistentData persistentData = mStorage.readPersistentDataBlock();
6247374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        if (persistentData.type == PersistentData.TYPE_SP) {
6257374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
6267374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            byte[] pwdToken = computePasswordToken(userCredential, pwd);
6277374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
6287374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            GateKeeperResponse response = gatekeeper.verify(fakeUid(persistentData.userId),
6297374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                    pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
6307374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            return VerifyCredentialResponse.fromGateKeeperResponse(response);
6317374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
6327374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
6337374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            byte[] pwdToken = computePasswordToken(userCredential, pwd);
6347374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int weaverSlot = persistentData.userId;
6357374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
6367374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
6377374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        } else {
6387374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            Log.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
6397374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                    + persistentData.type);
6407374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            return VerifyCredentialResponse.ERROR;
6417374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        }
6427374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    }
6437374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
6447374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
6457374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    private void synchronizeFrpPassword(PasswordData pwd,
6467374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int requestedQuality, int userId) {
6477374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        if (mStorage.getPersistentDataBlock() != null
6487374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
6497374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
6507374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
6517374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                        pwd.toBytes());
6527374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            } else {
6537374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
6547374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            }
6557374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        }
6567374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    }
6577374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
6587374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
6597374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            int weaverSlot) {
6607374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        if (mStorage.getPersistentDataBlock() != null
6617374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
6627374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
6637374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
6647374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                        requestedQuality, pwd.toBytes());
6657374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            } else {
6667374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, 0, 0, null);
6677374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos            }
6687374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos        }
6697374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos    }
6707374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos
6717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
672f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
673f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public long createTokenBasedSyntheticPassword(byte[] token, int userId) {
674f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        long handle = generateHandle();
675f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
676f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            tokenMap.put(userId, new ArrayMap<>());
677f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
6787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        TokenData tokenData = new TokenData();
6797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
6807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
6817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
6827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
6837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            PERSONALISATION_WEAVER_TOKEN, secdiscardable);
6847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
6857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.secdiscardableOnDisk = secdiscardable;
6867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.weaverSecret = null;
6877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
6887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
6897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
6907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        tokenMap.get(userId).put(handle, tokenData);
691f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return handle;
692f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
693f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
694f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public Set<Long> getPendingTokensForUser(int userId) {
695f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
696f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return Collections.emptySet();
697f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
698f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return tokenMap.get(userId).keySet();
699f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
700f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
701f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean removePendingToken(long handle, int userId) {
702f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
703f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
704f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
705f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return tokenMap.get(userId).remove(handle) != null;
706f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
707f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
708f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
709f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            int userId) {
710f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
711f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
712f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
7137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        TokenData tokenData = tokenMap.get(userId).get(handle);
7147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (tokenData == null) {
715f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
716f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
717f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!loadEscrowData(authToken, userId)) {
718f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            Log.w(TAG, "User is not escrowable");
719f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
720f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
7217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
7227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int slot = getNextAvailableWeaverSlot();
7237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            try {
7247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                weaverEnroll(slot, null, tokenData.weaverSecret);
7257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } catch (RemoteException e) {
7267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
7277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return false;
7287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
7297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            saveWeaverSlot(slot, handle, userId);
7307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
7317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
732f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
7337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                tokenData.aggregatedSecret, 0L, userId);
734f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        tokenMap.get(userId).remove(handle);
735f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return true;
736f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
737f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
7383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
7393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            byte[] applicationId, long sid, int userId) {
740f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        final byte[] secret;
741f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
742f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            secret = authToken.computeP0();
743f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
744f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            secret = authToken.syntheticPassword.getBytes();
745f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
7463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
7473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] blob = new byte[content.length + 1 + 1];
7483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        blob[0] = SYNTHETIC_PASSWORD_VERSION;
7493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        blob[1] = type;
7503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(content, 0, blob, 2, content.length);
7513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_BLOB_NAME, blob, handle, userId);
7523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
7533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
7543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
7553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Decrypt a synthetic password by supplying the user credential and corresponding password
7563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
7573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * verification to referesh the SID & Auth token maintained by the system.
75816c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu     * Note: the credential type is not validated here since there are call sites where the type is
75916c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu     * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
7603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
7613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
7623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            long handle, String credential, int userId) throws RemoteException {
7633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (credential == null) {
7643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credential = DEFAULT_PASSWORD;
7653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
7663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationResult result = new AuthenticationResult();
7673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
76816c823ebf398138add71ad8ff82053e3676f85c3Rubin Xu        result.credentialType = pwd.passwordType;
7693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] pwdToken = computePasswordToken(credential, pwd);
7703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
7717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] applicationId;
7727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int weaverSlot = loadWeaverSlot(handle, userId);
7737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (weaverSlot != INVALID_WEAVER_SLOT) {
7747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // Weaver based user password
7757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (!isWeaverAvailable()) {
7767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "No weaver service to unwrap password based SP");
7777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
7787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
7797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
7807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
7817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
7827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
7837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
7847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
7857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
7867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
7877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
7887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    pwd.passwordHandle, gkPwdToken);
7897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int responseCode = response.getResponseCode();
7907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (responseCode == GateKeeperResponse.RESPONSE_OK) {
7917b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.OK;
7927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                if (response.getShouldReEnroll()) {
7937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
7947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            pwd.passwordHandle, gkPwdToken, gkPwdToken);
7957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
7967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        pwd.passwordHandle = reenrollResponse.getPayload();
7977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
7987374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                        synchronizeFrpPassword(pwd,
7997374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                pwd.passwordType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
8007374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
8017374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
8027374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                /* TODO(roosa): keep the same password quality */,
8037374d3a4bca6bfbf7da1ef5dbf0db9f35f0c8315Adrian Roos                                userId);
8047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
8057b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.w(TAG, "Fail to re-enroll user password for user " + userId);
8067b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        // continue the flow anyway
8077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
8083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                }
8097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
8107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
8117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else  {
8137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
8147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
8167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderSecdiscardable(pwdToken,
8177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    loadSecdiscardable(handle, userId));
8183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
8203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
8213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                applicationId, userId);
8223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
8233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
8243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
8253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
8263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
8273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
828f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    /**
829f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * Decrypt a synthetic password by supplying an escrow token and corresponding token
830f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
831f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * verification to referesh the SID & Auth token maintained by the system.
832f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     */
833f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
834f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
835f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                    throws RemoteException {
836f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        AuthenticationResult result = new AuthenticationResult();
8377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] secdiscardable = loadSecdiscardable(handle, userId);
8387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int slotId = loadWeaverSlot(handle, userId);
8397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slotId != INVALID_WEAVER_SLOT) {
8407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (!isWeaverAvailable()) {
8417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "No weaver service to unwrap token based SP");
8427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
8437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
8457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            VerifyCredentialResponse response = weaverVerify(slotId, null);
8467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
8477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response.getPayload() == null) {
8487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
8497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
8507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
8517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
8527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
8537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    PERSONALISATION_WEAVER_TOKEN, secdiscardable);
8547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
8557b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
856f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
857f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                applicationId, userId);
858f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (result.authToken != null) {
859f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
860f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            if (result.gkResponse == null) {
861f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                // The user currently has no password. return OK with null payload so null
862f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                // is propagated to unlockUser()
863f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                result.gkResponse = VerifyCredentialResponse.OK;
864f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            }
865f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
866f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.gkResponse = VerifyCredentialResponse.ERROR;
867f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
868f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return result;
869f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
870f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
8713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
8723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            byte[] applicationId, int userId) {
8733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
8743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (blob == null) {
8753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
8763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (blob[0] != SYNTHETIC_PASSWORD_VERSION) {
8783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            throw new RuntimeException("Unknown blob version");
8793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (blob[1] != type) {
8813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            throw new RuntimeException("Invalid blob type");
8823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] secret = decryptSPBlob(getHandleName(handle),
8843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                Arrays.copyOfRange(blob, 2, blob.length), applicationId);
8853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (secret == null) {
8863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.e(TAG, "Fail to decrypt SP for user " + userId);
8873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
8883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationToken result = new AuthenticationToken();
890f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
891f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            if (!loadEscrowData(result, userId)) {
892f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                Log.e(TAG, "User is not escrowable: " + userId);
893f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                return null;
894f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            }
895f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.recreate(secret);
896f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
897f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.syntheticPassword = new String(secret);
898f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
8993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
9003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
9033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
9043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * if required.
9053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
9063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Normally performing verifyChallenge with an AuthenticationToken should always return
9073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * RESPONSE_OK, since user authentication failures are detected earlier when trying to
9083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * decrypt SP.
9093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
9108b30ec3f49d4c8037bc6aa03ed6dd91aff3968adRubin Xu    public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
9113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
9123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] spHandle = loadSyntheticPasswordHandle(userId);
9133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (spHandle == null) {
9143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            // There is no password handle associated with the given user, i.e. the user is not
9153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            // secured by lockscreen and has no SID, so just return here;
9163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
9173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        VerifyCredentialResponse result;
9193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
9203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                spHandle, auth.deriveGkPassword());
9213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        int responseCode = response.getResponseCode();
9223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (responseCode == GateKeeperResponse.RESPONSE_OK) {
9233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = new VerifyCredentialResponse(response.getPayload());
9243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (response.getShouldReEnroll()) {
9253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                response = gatekeeper.enroll(userId, spHandle,
9263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                        spHandle, auth.deriveGkPassword());
9273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
9283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    spHandle = response.getPayload();
9293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    saveSyntheticPasswordHandle(spHandle, userId);
9303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    // Call self again to re-verify with updated handle
9313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    return verifyChallenge(gatekeeper, auth, challenge, userId);
9323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                } else {
9333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
9343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    // Fall through, return existing handle
9353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                }
9363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
9373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
9383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = new VerifyCredentialResponse(response.getTimeout());
9393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else {
9403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = VerifyCredentialResponse.ERROR;
9413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
9433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public boolean existsHandle(long handle, int userId) {
9463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return hasState(SP_BLOB_NAME, handle, userId);
9473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
949f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
950f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        destroySyntheticPassword(handle, userId);
951aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SECDISCARDABLE_NAME, handle, userId);
952f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
953f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
9543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
9553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroySyntheticPassword(handle, userId);
956aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SECDISCARDABLE_NAME, handle, userId);
957aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(PASSWORD_DATA_NAME, handle, userId);
9583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void destroySyntheticPassword(long handle, int userId) {
961aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        destroyState(SP_BLOB_NAME, handle, userId);
9623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroySPBlobKey(getHandleName(handle));
9637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
9647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            destroyWeaverSlot(handle, userId);
9657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
9667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
9677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
9687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
9697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash(
9707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                PERSONALISATION_WEAVER_PASSWORD, secret);
9717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] result = new byte[data.length + weaverSecret.length];
9727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        System.arraycopy(data, 0, result, 0, data.length);
9737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length);
9747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
9753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
9783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
9793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
9803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] result = new byte[data.length + secdiscardable.length];
9813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(data, 0, result, 0, data.length);
9823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
9833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
9843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] createSecdiscardable(long handle, int userId) {
9873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
9887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveSecdiscardable(handle, data, userId);
9893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return data;
9903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
9937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
9947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
9957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
9963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadSecdiscardable(long handle, int userId) {
9973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return loadState(SECDISCARDABLE_NAME, handle, userId);
9983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private boolean hasState(String stateName, long handle, int userId) {
10013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
10023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadState(String stateName, long handle, int userId) {
10053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return mStorage.readSyntheticPasswordState(userId, handle, stateName);
10063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveState(String stateName, byte[] data, long handle, int userId) {
10093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
10103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1012aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu    private void destroyState(String stateName, long handle, int userId) {
1013aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu        mStorage.deleteSyntheticPasswordState(userId, handle, stateName);
10143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
10173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
10183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
10213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
10223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected void destroySPBlobKey(String keyAlias) {
10253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
10263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public static long generateHandle() {
10293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        SecureRandom rng = new SecureRandom();
10303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        long result;
10313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        do {
10323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = rng.nextLong();
10333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } while (result == DEFAULT_HANDLE);
10343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
10353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private int fakeUid(int uid) {
10383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return 100000 + uid;
10393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected static byte[] secureRandom(int length) {
10423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        try {
10433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
10443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } catch (NoSuchAlgorithmException e) {
10453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            e.printStackTrace();
10463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
10473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
10483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private String getHandleName(long handle) {
10513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
10523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] computePasswordToken(String password, PasswordData data) {
10553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
10563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                PASSWORD_TOKEN_LENGTH);
10573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] passwordTokenToGkInput(byte[] token) {
10603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
10613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] passwordTokenToWeaverKey(byte[] token) {
10647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
10657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key.length < mWeaverConfig.keySize) {
10667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("weaver key length too small");
10677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
10687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return Arrays.copyOf(key, mWeaverConfig.keySize);
10697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
10707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
10713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected long sidFromPasswordHandle(byte[] handle) {
10723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return nativeSidFromPasswordHandle(handle);
10733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
10763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return nativeScrypt(password.getBytes(), salt, N, r, p, outLen);
10773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    native long nativeSidFromPasswordHandle(byte[] handle);
10803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen);
10813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected static ArrayList<Byte> toByteArrayList(byte[] data) {
10837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ArrayList<Byte> result = new ArrayList<Byte>(data.length);
10847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < data.length; i++) {
10857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result.add(data[i]);
10867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
10877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
10887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
10897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
10907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
10917b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] result = new byte[data.size()];
10927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < data.size(); i++) {
10937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result[i] = data.get(i);
10947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
10957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
10967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
10977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
10983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
10993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public static String bytesToHex(byte[] bytes) {
11003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (bytes == null) {
11013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return "null";
11023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
11033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        char[] hexChars = new char[bytes.length * 2];
11043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        for ( int j = 0; j < bytes.length; j++ ) {
11053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int v = bytes[j] & 0xFF;
11063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            hexChars[j * 2] = hexArray[v >>> 4];
11073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
11083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
11093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return new String(hexChars);
11103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
11113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu}
1112