SyntheticPasswordManager.java revision 507d11c9353666a75fee014565f900825a907691
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;
217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.IWeaver;
227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverConfig;
237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverReadResponse;
247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverReadStatus;
257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.hardware.weaver.V1_0.WeaverStatus;
263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.os.RemoteException;
277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.security.GateKeeper;
283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.service.gatekeeper.GateKeeperResponse;
293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.service.gatekeeper.IGateKeeperService;
303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.util.ArrayMap;
313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport android.util.Log;
327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport android.util.Slog;
333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport com.android.internal.annotations.VisibleForTesting;
353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport com.android.internal.util.ArrayUtils;
363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport com.android.internal.widget.LockPatternUtils;
373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport com.android.internal.widget.VerifyCredentialResponse;
383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport libcore.util.HexEncoding;
403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.nio.ByteBuffer;
423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.security.NoSuchAlgorithmException;
433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.security.SecureRandom;
447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.ArrayList;
453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.util.Arrays;
463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.util.Collections;
477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.HashSet;
487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.List;
497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.Map;
507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xuimport java.util.NoSuchElementException;
513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xuimport java.util.Set;
523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu/**
553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * A class that maintains the wrapping of synthetic password by user credentials or escrow tokens.
563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * It's (mostly) a pure storage for synthetic passwords, providing APIs to creating and destroying
573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * synthetic password blobs which are wrapped by user credentials or escrow tokens.
583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *
593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu * Here is the assumptions it makes:
603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   Each user has one single synthetic password at any time.
613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   The SP has an associated password handle, which binds to the SID for that user. The password
623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   handle is persisted by SyntheticPasswordManager internally.
633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu *   If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD
647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu * Information persisted on disk:
667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *   for each user (stored under DEFAULT_HANDLE):
677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *     SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user
687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *                     credential exists, cleared when user clears their credential.
69128180b2d38d2ae4ba1f440ef10534777d643f00Rubin Xu *     SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combined with escrow
707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *                     tokens. Destroyed when escrow support is turned off for the given user.
717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *     for each SP blob under the user (stored under the corresponding handle):
737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *       SP_BLOB_NAME: The encrypted synthetic password. Always exists.
747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *       PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP.
75128180b2d38d2ae4ba1f440ef10534777d643f00Rubin Xu *       SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME for the
76128180b2d38d2ae4ba1f440ef10534777d643f00Rubin Xu *                            purpose of secure deletion. Exists if this is a non-weaver SP
777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *                            (both password and token based), or it's a token-based SP under weaver.
787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *       WEAVER_SLOT: Metadata about the weaver slot used. Only exists if this is a SP under weaver.
797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu *
813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu */
823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xupublic class SyntheticPasswordManager {
833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_BLOB_NAME = "spblob";
843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_E0_NAME = "e0";
853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_P1_NAME = "p1";
863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SP_HANDLE_NAME = "handle";
873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String SECDISCARDABLE_NAME = "secdis";
883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int SECDISCARDABLE_LENGTH = 16 * 1024;
893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String PASSWORD_DATA_NAME = "pwd";
907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final String WEAVER_SLOT_NAME = "weaver";
913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    public static final long DEFAULT_HANDLE = 0L;
933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String DEFAULT_PASSWORD = "default-password";
943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte WEAVER_VERSION = 1;
967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final int INVALID_WEAVER_SLOT = -1;
977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte SYNTHETIC_PASSWORD_VERSION = 1;
993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
100f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
1013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // 256-bit synthetic password
1033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8;
1043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
105157bdddf4e1560519d513b61d6b3caf2d401c498Rubin Xu    private static final int PASSWORD_SCRYPT_N = 11;
1063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_SCRYPT_R = 3;
1073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_SCRYPT_P = 1;
1083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_SALT_LENGTH = 16;
1093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final int PASSWORD_TOKEN_LENGTH = 32;
1103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final String TAG = "SyntheticPasswordManager";
1113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
1133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes();
1143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();
1153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
1163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
1173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
1183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
1197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
1207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
1217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
1223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class AuthenticationResult {
1243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public AuthenticationToken authToken;
1253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public VerifyCredentialResponse gkResponse;
1263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
1273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class AuthenticationToken {
1293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        /*
1303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * Here is the relationship between all three fields:
1313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
1323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * syntheticPassword = hash(P0 || P1)
1333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         * E0 = P0 encrypted under syntheticPassword, stored on disk.
1343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu         */
1353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @Nullable byte[] E0;
1363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @Nullable byte[] P1;
1373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        private @NonNull String syntheticPassword;
1383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public String deriveKeyStorePassword() {
1403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
1413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
1423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] deriveGkPassword() {
1453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
1463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    syntheticPassword.getBytes());
1473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] deriveDiskEncryptionKey() {
1503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
1513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    syntheticPassword.getBytes());
1523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
154f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        private void initialize(byte[] P0, byte[] P1) {
1553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.P1 = P1;
1563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.syntheticPassword = String.valueOf(HexEncoding.encode(
1573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    SyntheticPasswordCrypto.personalisedHash(
1583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                            PERSONALIZATION_SP_SPLIT, P0, P1)));
1593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            this.E0 = SyntheticPasswordCrypto.encrypt(this.syntheticPassword.getBytes(),
1603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    PERSONALIZATION_E0, P0);
1613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
163f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        public void recreate(byte[] secret) {
164f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            initialize(secret, this.P1);
165f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
166f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
1673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        protected static AuthenticationToken create() {
1683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            AuthenticationToken result = new AuthenticationToken();
1693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
1703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    secureRandom(SYNTHETIC_PASSWORD_LENGTH));
1713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
1723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] computeP0() {
1753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (E0 == null) {
1763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                return null;
1773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
1783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SyntheticPasswordCrypto.decrypt(syntheticPassword.getBytes(), PERSONALIZATION_E0,
1793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    E0);
1803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
1813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
1823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    static class PasswordData {
1843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptN;
1853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptR;
1863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte scryptP;
1873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public int passwordType;
1883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] salt;
1897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        // For GateKeeper-based credential, this is the password handle returned by GK,
1907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        // for weaver-based credential, this is empty.
1913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] passwordHandle;
1923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
1933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public static PasswordData create(int passwordType) {
1943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            PasswordData result = new PasswordData();
1953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptN = PASSWORD_SCRYPT_N;
1963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptR = PASSWORD_SCRYPT_R;
1973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptP = PASSWORD_SCRYPT_P;
1983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.passwordType = passwordType;
1993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.salt = secureRandom(PASSWORD_SALT_LENGTH);
2003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
2013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public static PasswordData fromBytes(byte[] data) {
2043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            PasswordData result = new PasswordData();
2053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            ByteBuffer buffer = ByteBuffer.allocate(data.length);
2063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(data, 0, data.length);
2073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.flip();
2083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.passwordType = buffer.getInt();
2093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptN = buffer.get();
2103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptR = buffer.get();
2113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.scryptP = buffer.get();
2123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int saltLen = buffer.getInt();
2133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result.salt = new byte[saltLen];
2143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.get(result.salt);
2153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int handleLen = buffer.getInt();
2167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (handleLen > 0) {
2177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.passwordHandle = new byte[handleLen];
2187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.get(result.passwordHandle);
2197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else {
2207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.passwordHandle = null;
2217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
2223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return result;
2233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        public byte[] toBytes() {
2267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
2287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    + Integer.BYTES + salt.length + Integer.BYTES +
2297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    (passwordHandle != null ? passwordHandle.length : 0));
2303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.putInt(passwordType);
2313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptN);
2323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptR);
2333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(scryptP);
2343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.putInt(salt.length);
2353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            buffer.put(salt);
2367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (passwordHandle != null && passwordHandle.length > 0) {
2377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.putInt(passwordHandle.length);
2387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.put(passwordHandle);
2397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else {
2407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                buffer.putInt(0);
2417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
2423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return buffer.array();
2433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
2443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
2453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    static class TokenData {
2477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] secdiscardableOnDisk;
2487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] weaverSecret;
2497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] aggregatedSecret;
2507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
2517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private LockSettingsStorage mStorage;
2537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private IWeaver mWeaver;
2547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private WeaverConfig mWeaverConfig;
2553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public SyntheticPasswordManager(LockSettingsStorage storage) {
2573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        mStorage = storage;
2583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
2593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
2607b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    @VisibleForTesting
2617b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected IWeaver getWeaverService() throws RemoteException {
2627b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        try {
2637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return IWeaver.getService();
2647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } catch (NoSuchElementException e) {
2657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Slog.i(TAG, "Device does not support weaver");
2667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return null;
2677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
2687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
2697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    public synchronized void initWeaverService() {
2717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (mWeaver != null) {
2727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return;
2737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
2747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        try {
2757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            mWeaverConfig = null;
2767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            mWeaver = getWeaverService();
2777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (mWeaver != null) {
2787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                mWeaver.getConfig((int status, WeaverConfig config) -> {
2797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (status == WeaverStatus.OK && config.slots > 0) {
2807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        mWeaverConfig = config;
2817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
2827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Slog.e(TAG, "Failed to get weaver config, status " + status
2837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                                + " slots: " + config.slots);
2847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        mWeaver = null;
2857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
2867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                });
2877b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
2887b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } catch (RemoteException e) {
2897b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Slog.e(TAG, "Failed to get weaver service", e);
2907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
2917b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
2927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
2937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private synchronized boolean isWeaverAvailable() {
2947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (mWeaver == null) {
2957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            //Re-initializing weaver in case there was a transient error preventing access to it.
2967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            initWeaverService();
2977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
2987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return mWeaver != null && mWeaverConfig.slots > 0;
2997b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3007b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3017b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    /**
3027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * Enroll the given key value pair into the specified weaver slot. if the given key is null,
3037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * a default all-zero key is used. If the value is not specified, a fresh random secret is
3047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * generated as the value.
3057b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     *
3067b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @return the value stored in the weaver slot
3077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @throws RemoteException
3087b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     */
3097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
3107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throws RemoteException {
3117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
3127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid slot for weaver");
3137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key == null) {
3157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            key = new byte[mWeaverConfig.keySize];
3167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else if (key.length != mWeaverConfig.keySize) {
3177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid key size for weaver");
3187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (value == null) {
3207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            value = secureRandom(mWeaverConfig.valueSize);
3217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
3237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (writeStatus != WeaverStatus.OK) {
3247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
3257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return null;
3267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return value;
3287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    /**
3317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * Verify the supplied key against a weaver slot, returning a response indicating whether
3327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * the verification is successful, throttled or failed. If successful, the bound secret
3337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * is also returned.
3347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * @throws RemoteException
3357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     */
3367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
3377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
3387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid slot for weaver");
3397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key == null) {
3417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            key = new byte[mWeaverConfig.keySize];
3427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else if (key.length != mWeaverConfig.keySize) {
3437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("Invalid key size for weaver");
3447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
3467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
3477b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            switch (status) {
3487b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.OK:
3497b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = new VerifyCredentialResponse(
3507b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            fromByteArrayList(readResponse.value));
3517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.THROTTLE:
3537b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = new VerifyCredentialResponse(readResponse.timeout);
3547b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
3557b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3567b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.INCORRECT_KEY:
3577b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (readResponse.timeout == 0) {
3587b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        response[0] = VerifyCredentialResponse.ERROR;
3597b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
3607b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
3617b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        response[0] = new VerifyCredentialResponse(readResponse.timeout);
3627b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
3637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
3647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                case WeaverReadStatus.FAILED:
3667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response[0] = VerifyCredentialResponse.ERROR;
3677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
3687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    break;
3697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu               default:
3707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   response[0] = VerifyCredentialResponse.ERROR;
3717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
3727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                   break;
3737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
3747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        });
3757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return response[0];
3767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
3787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    public void removeUser(int userId) {
3797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
3807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            for (long handle : mStorage.listSyntheticPasswordHandlesForUser(WEAVER_SLOT_NAME,
3817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    userId)) {
3827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                destroyWeaverSlot(handle, userId);
3837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
3847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
3857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
3863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
3873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public int getCredentialType(long handle, int userId) {
3883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
3893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (passwordData == null) {
3903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
3913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return LockPatternUtils.CREDENTIAL_TYPE_NONE;
3923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
3933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return PasswordData.fromBytes(passwordData).passwordType;
3943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
3953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
3963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
3973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Initializing a new Authentication token, possibly from an existing credential and hash.
3983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
3993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * The authentication token would bear a randomly-generated synthetic password.
4003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * This method has the side effect of rebinding the SID of the given user to the
4023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * newly-generated SP.
4033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * If the existing credential hash is non-null, the existing SID mill be migrated so
4053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * the synthetic password in the authentication token will produce the same SID
4063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
4077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu     * in a per-user data storage.)
4083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * If the existing credential hash is null, it means the given user should have no SID so
4103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
4113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * the supplied credential parameter is also ignored.
4123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Also saves the escrow information necessary to re-generate the synthetic password under
4143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
4153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * password escrow should be disabled completely on the given user.
4163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
4173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
4183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
4193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            byte[] hash, String credential, int userId) throws RemoteException {
4203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationToken result = AuthenticationToken.create();
4213bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response;
4223bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (hash != null) {
4233bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            response = gatekeeper.enroll(userId, hash, credential.getBytes(),
4243bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    result.deriveGkPassword());
4253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
4263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
4273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                clearSidForUser(userId);
4283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            } else {
4293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                saveSyntheticPasswordHandle(response.getPayload(), userId);
4303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
4313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else {
4323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            clearSidForUser(userId);
4333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
4343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveEscrowData(result, userId);
4353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
4363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
4393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Enroll a new password handle and SID for the given synthetic password and persist it on disk.
4403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Used when adding password to previously-unsecured devices.
4413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
4423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
4433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int userId) throws RemoteException {
4443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
4453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                authToken.deriveGkPassword());
4463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
4473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.e(TAG, "Fail to create new SID for user " + userId);
4483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return;
4493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
4503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveSyntheticPasswordHandle(response.getPayload(), userId);
4513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // Nuke the SP handle (and as a result, its SID) for the given user.
4543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void clearSidForUser(int userId) {
4553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroyState(SP_HANDLE_NAME, true, DEFAULT_HANDLE, userId);
4563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public boolean hasSidForUser(int userId) {
4593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
4603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // if null, it means there is no SID associated with the user
4633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // This can happen if the user is migrated to SP but currently
4643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    // do not have a lockscreen password.
4653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadSyntheticPasswordHandle(int userId) {
4663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId);
4673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) {
4703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId);
4713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private boolean loadEscrowData(AuthenticationToken authToken, int userId) {
4743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId);
4753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId);
4763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return authToken.E0 != null && authToken.P1 != null;
4773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveEscrowData(AuthenticationToken authToken, int userId) {
4803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId);
4813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId);
4823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
484f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean hasEscrowData(int userId) {
485f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId)
486f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId);
487f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
488f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
4893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void destroyEscrowData(int userId) {
4903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroyState(SP_E0_NAME, true, DEFAULT_HANDLE, userId);
4913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroyState(SP_P1_NAME, true, DEFAULT_HANDLE, userId);
4923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
4933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
4947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private int loadWeaverSlot(long handle, int userId) {
4957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final int LENGTH = Byte.BYTES + Integer.BYTES;
4967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId);
4977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (data == null || data.length != LENGTH) {
4987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return INVALID_WEAVER_SLOT;
4997b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5007b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ByteBuffer buffer = ByteBuffer.allocate(LENGTH);
5017b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.put(data, 0, data.length);
5027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.flip();
5037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (buffer.get() != WEAVER_VERSION) {
5047b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            Log.e(TAG, "Invalid weaver slot version of handle " + handle);
5057b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            return INVALID_WEAVER_SLOT;
5067b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return buffer.getInt();
5087b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void saveWeaverSlot(int slot, long handle, int userId) {
5117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES);
5127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.put(WEAVER_VERSION);
5137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        buffer.putInt(slot);
5147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId);
5157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void destroyWeaverSlot(long handle, int userId) {
5187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int slot = loadWeaverSlot(handle, userId);
5197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slot != INVALID_WEAVER_SLOT) {
5207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            try {
5217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                weaverEnroll(slot, null, null);
5227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } catch (RemoteException e) {
5237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.w(TAG, "Failed to destroy slot", e);
5247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        destroyState(WEAVER_SLOT_NAME, true, handle, userId);
5277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private int getNextAvailableWeaverSlot() {
5307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
5317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                WEAVER_SLOT_NAME);
5327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        HashSet<Integer> slots = new HashSet<>();
5337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) {
5347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            for (Long handle : entry.getValue()) {
5357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                int slot = loadWeaverSlot(handle, entry.getKey());
5367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                slots.add(slot);
5377b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < mWeaverConfig.slots; i++) {
5407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (!slots.contains(i)) {
5417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return i;
5427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
5447b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        throw new RuntimeException("Run out of weaver slots.");
5457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
5467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
5483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Create a new password based SP blob based on the supplied authentication token, such that
5493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result
5503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * in the same authentication token.
5513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
5523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * This method only creates SP blob wrapping around the given synthetic password and does not
5533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * handle logic around SID or SP handle. The caller should separately ensure that the user's SID
5543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * is consistent with the device state by calling other APIs in this class.
5553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
5563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * @see #newSidForUser
5573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * @see #clearSidForUser
5583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
5593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
5603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            String credential, int credentialType, AuthenticationToken authToken, int userId)
5613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    throws RemoteException {
5623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
5633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
5643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credential = DEFAULT_PASSWORD;
5653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
5663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
5673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        long handle = generateHandle();
5683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        PasswordData pwd = PasswordData.create(credentialType);
5693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] pwdToken = computePasswordToken(credential, pwd);
5707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final long sid;
5717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] applicationId;
5727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
5737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
5747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // Weaver based user password
5757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int weaverSlot = getNextAvailableWeaverSlot();
5767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
5777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (weaverSecret == null) {
5787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Fail to enroll user password under weaver " + userId);
5797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return DEFAULT_HANDLE;
5807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            saveWeaverSlot(weaverSlot, handle, userId);
5823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
5837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            pwd.passwordHandle = null;
5847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            sid = GateKeeper.INVALID_SECURE_USER_ID;
5857b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret);
5867b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
58754c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
58854c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            // to prevent them from accumulating and causing problems.
58954c19b62052172cdfc6a36546687c4a56a0d2a5cRubin Xu            gatekeeper.clearSecureUserId(fakeUid(userId));
5907b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // GateKeeper based user password
5917b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
5927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    passwordTokenToGkInput(pwdToken));
5937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
5947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
5957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return DEFAULT_HANDLE;
5967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
5977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            pwd.passwordHandle = response.getPayload();
5987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            sid = sidFromPasswordHandle(pwd.passwordHandle);
5997b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderSecdiscardable(pwdToken,
6007b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    createSecdiscardable(handle, userId));
6013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
6023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
6033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
6053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                applicationId, sid, userId);
6063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return handle;
6073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
6083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
610f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
611f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public long createTokenBasedSyntheticPassword(byte[] token, int userId) {
612f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        long handle = generateHandle();
613f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
614f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            tokenMap.put(userId, new ArrayMap<>());
615f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
6167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        TokenData tokenData = new TokenData();
6177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH);
6187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
6197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize);
6207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret,
6217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            PERSONALISATION_WEAVER_TOKEN, secdiscardable);
6227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
6237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.secdiscardableOnDisk = secdiscardable;
6247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            tokenData.weaverSecret = null;
6257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
6267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable);
6277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
6287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        tokenMap.get(userId).put(handle, tokenData);
629f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return handle;
630f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
631f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
632f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public Set<Long> getPendingTokensForUser(int userId) {
633f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
634f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return Collections.emptySet();
635f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
636f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return tokenMap.get(userId).keySet();
637f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
638f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
639f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean removePendingToken(long handle, int userId) {
640f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
641f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
642f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
643f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return tokenMap.get(userId).remove(handle) != null;
644f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
645f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
646f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken,
647f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            int userId) {
648f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!tokenMap.containsKey(userId)) {
649f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
650f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
6517b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        TokenData tokenData = tokenMap.get(userId).get(handle);
6527b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (tokenData == null) {
653f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
654f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
655f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (!loadEscrowData(authToken, userId)) {
656f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            Log.w(TAG, "User is not escrowable");
657f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            return false;
658f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
6597b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (isWeaverAvailable()) {
6607b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int slot = getNextAvailableWeaverSlot();
6617b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            try {
6627b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                weaverEnroll(slot, null, tokenData.weaverSecret);
6637b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } catch (RemoteException e) {
6647b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
6657b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return false;
6667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
6677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            saveWeaverSlot(slot, handle, userId);
6687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
6697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId);
670f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken,
6717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                tokenData.aggregatedSecret, 0L, userId);
672f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        tokenMap.get(userId).remove(handle);
673f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return true;
674f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
675f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
6763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken,
6773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            byte[] applicationId, long sid, int userId) {
678f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        final byte[] secret;
679f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
680f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            secret = authToken.computeP0();
681f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
682f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            secret = authToken.syntheticPassword.getBytes();
683f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
6843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
6853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] blob = new byte[content.length + 1 + 1];
6863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        blob[0] = SYNTHETIC_PASSWORD_VERSION;
6873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        blob[1] = type;
6883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(content, 0, blob, 2, content.length);
6893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        saveState(SP_BLOB_NAME, blob, handle, userId);
6903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
6913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
6923bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
6933bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Decrypt a synthetic password by supplying the user credential and corresponding password
6943bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
6953bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * verification to referesh the SID & Auth token maintained by the system.
6963bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
6973bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
6983bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            long handle, String credential, int userId) throws RemoteException {
6993bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (credential == null) {
7003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            credential = DEFAULT_PASSWORD;
7013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
7023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationResult result = new AuthenticationResult();
7033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
7043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] pwdToken = computePasswordToken(credential, pwd);
7053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
7067b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        final byte[] applicationId;
7077b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int weaverSlot = loadWeaverSlot(handle, userId);
7087b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (weaverSlot != INVALID_WEAVER_SLOT) {
7097b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            // Weaver based user password
7107b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (!isWeaverAvailable()) {
7117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "No weaver service to unwrap password based SP");
7127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
7137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
7147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
7157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
7167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
7177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
7187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
7197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
7207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        } else {
7217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
7227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
7237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    pwd.passwordHandle, gkPwdToken);
7247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            int responseCode = response.getResponseCode();
7257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (responseCode == GateKeeperResponse.RESPONSE_OK) {
7267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.OK;
7277b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                if (response.getShouldReEnroll()) {
7287b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
7297b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                            pwd.passwordHandle, gkPwdToken, gkPwdToken);
7307b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
7317b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        pwd.passwordHandle = reenrollResponse.getPayload();
7327b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
7337b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    } else {
7347b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        Log.w(TAG, "Fail to re-enroll user password for user " + userId);
7357b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                        // continue the flow anyway
7367b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    }
7373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                }
7387b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
7397b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = new VerifyCredentialResponse(response.getTimeout());
7407b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
7417b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            } else  {
7427b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
7437b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
7443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
7457b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            applicationId = transformUnderSecdiscardable(pwdToken,
7467b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    loadSecdiscardable(handle, userId));
7473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
7483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
7493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
7503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                applicationId, userId);
7513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
7523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        // Perform verifyChallenge to refresh auth tokens for GK if user password exists.
7533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
7543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
7553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
7563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
757f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    /**
758f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * Decrypt a synthetic password by supplying an escrow token and corresponding token
759f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * blob handle generated previously. If the decryption is successful, initiate a GateKeeper
760f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     * verification to referesh the SID & Auth token maintained by the system.
761f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu     */
762f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
763f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
764f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                    throws RemoteException {
765f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        AuthenticationResult result = new AuthenticationResult();
7667b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] secdiscardable = loadSecdiscardable(handle, userId);
7677b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        int slotId = loadWeaverSlot(handle, userId);
7687b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (slotId != INVALID_WEAVER_SLOT) {
7697b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (!isWeaverAvailable()) {
7707b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "No weaver service to unwrap token based SP");
7717b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
7727b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
7737b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
7747b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            VerifyCredentialResponse response = weaverVerify(slotId, null);
7757b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
7767b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    response.getPayload() == null) {
7777b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                Log.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
7787b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                result.gkResponse = VerifyCredentialResponse.ERROR;
7797b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                return result;
7807b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            }
7817b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            secdiscardable = SyntheticPasswordCrypto.decrypt(response.getPayload(),
7827b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                    PERSONALISATION_WEAVER_TOKEN, secdiscardable);
7837b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
7847b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
785f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
786f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                applicationId, userId);
787f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (result.authToken != null) {
788f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
789f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            if (result.gkResponse == null) {
790f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                // The user currently has no password. return OK with null payload so null
791f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                // is propagated to unlockUser()
792f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                result.gkResponse = VerifyCredentialResponse.OK;
793f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            }
794f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
795f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.gkResponse = VerifyCredentialResponse.ERROR;
796f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
797f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        return result;
798f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
799f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
8003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
8013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            byte[] applicationId, int userId) {
8023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
8033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (blob == null) {
8043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
8053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (blob[0] != SYNTHETIC_PASSWORD_VERSION) {
8073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            throw new RuntimeException("Unknown blob version");
8083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (blob[1] != type) {
8103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            throw new RuntimeException("Invalid blob type");
8113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] secret = decryptSPBlob(getHandleName(handle),
8133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                Arrays.copyOfRange(blob, 2, blob.length), applicationId);
8143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (secret == null) {
8153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            Log.e(TAG, "Fail to decrypt SP for user " + userId);
8163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
8173bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        AuthenticationToken result = new AuthenticationToken();
819f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
820f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            if (!loadEscrowData(result, userId)) {
821f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                Log.e(TAG, "User is not escrowable: " + userId);
822f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu                return null;
823f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            }
824f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.recreate(secret);
825f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        } else {
826f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu            result.syntheticPassword = new String(secret);
827f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        }
8283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
8293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
8303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
8313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    /**
8323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle
8333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * if required.
8343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     *
8353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * Normally performing verifyChallenge with an AuthenticationToken should always return
8363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * RESPONSE_OK, since user authentication failures are detected earlier when trying to
8373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     * decrypt SP.
8383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu     */
8398b30ec3f49d4c8037bc6aa03ed6dd91aff3968adRubin Xu    public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
8403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
8413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] spHandle = loadSyntheticPasswordHandle(userId);
8423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (spHandle == null) {
8433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            // There is no password handle associated with the given user, i.e. the user is not
8443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            // secured by lockscreen and has no SID, so just return here;
8453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
8463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        VerifyCredentialResponse result;
8483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
8493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                spHandle, auth.deriveGkPassword());
8503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        int responseCode = response.getResponseCode();
8513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (responseCode == GateKeeperResponse.RESPONSE_OK) {
8523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = new VerifyCredentialResponse(response.getPayload());
8533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            if (response.getShouldReEnroll()) {
8543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                response = gatekeeper.enroll(userId, spHandle,
8553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                        spHandle, auth.deriveGkPassword());
8563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
8573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    spHandle = response.getPayload();
8583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    saveSyntheticPasswordHandle(spHandle, userId);
8593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    // Call self again to re-verify with updated handle
8603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    return verifyChallenge(gatekeeper, auth, challenge, userId);
8613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                } else {
8623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
8633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                    // Fall through, return existing handle
8643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                }
8653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            }
8663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
8673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = new VerifyCredentialResponse(response.getTimeout());
8683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } else {
8693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = VerifyCredentialResponse.ERROR;
8703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
8713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
8723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
8733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
8743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public boolean existsHandle(long handle, int userId) {
8753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return hasState(SP_BLOB_NAME, handle, userId);
8763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
8773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
878f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    public void destroyTokenBasedSyntheticPassword(long handle, int userId) {
879f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        destroySyntheticPassword(handle, userId);
880f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu        destroyState(SECDISCARDABLE_NAME, true, handle, userId);
881f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu    }
882f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu
8833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public void destroyPasswordBasedSyntheticPassword(long handle, int userId) {
8843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroySyntheticPassword(handle, userId);
8853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroyState(SECDISCARDABLE_NAME, true, handle, userId);
8863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroyState(PASSWORD_DATA_NAME, true, handle, userId);
8873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
8883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
8893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void destroySyntheticPassword(long handle, int userId) {
8903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroyState(SP_BLOB_NAME, true, handle, userId);
8913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        destroySPBlobKey(getHandleName(handle));
8927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (hasState(WEAVER_SLOT_NAME, handle, userId)) {
8937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            destroyWeaverSlot(handle, userId);
8947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
8957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
8967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
8977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) {
8987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash(
8997b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu                PERSONALISATION_WEAVER_PASSWORD, secret);
9007b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] result = new byte[data.length + weaverSecret.length];
9017b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        System.arraycopy(data, 0, result, 0, data.length);
9027b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length);
9037b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
9043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) {
9073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash(
9083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                PERSONALISATION_SECDISCARDABLE, rawSecdiscardable);
9093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] result = new byte[data.length + secdiscardable.length];
9103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(data, 0, result, 0, data.length);
9113bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length);
9123bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
9133bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9143bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9153bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] createSecdiscardable(long handle, int userId) {
9163bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        byte[] data = secureRandom(SECDISCARDABLE_LENGTH);
9177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveSecdiscardable(handle, data, userId);
9183bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return data;
9193bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9203bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) {
9227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId);
9237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
9247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
9253bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadSecdiscardable(long handle, int userId) {
9263bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return loadState(SECDISCARDABLE_NAME, handle, userId);
9273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private boolean hasState(String stateName, long handle, int userId) {
9303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
9313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] loadState(String stateName, long handle, int userId) {
9343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return mStorage.readSyntheticPasswordState(userId, handle, stateName);
9353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void saveState(String stateName, byte[] data, long handle, int userId) {
9383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        mStorage.writeSyntheticPasswordState(userId, handle, stateName, data);
9393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9413bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private void destroyState(String stateName, boolean secure, long handle, int userId) {
9423bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        mStorage.deleteSyntheticPasswordState(userId, handle, stateName, secure);
9433bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9443bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9453bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
9463bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId);
9473bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9483bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9493bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
9503bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid);
9513bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9523bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9533bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected void destroySPBlobKey(String keyAlias) {
9543bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
9553bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9563bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9573bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public static long generateHandle() {
9583bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        SecureRandom rng = new SecureRandom();
9593bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        long result;
9603bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        do {
9613bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            result = rng.nextLong();
9623bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } while (result == DEFAULT_HANDLE);
9633bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return result;
9643bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9653bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9663bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private int fakeUid(int uid) {
9673bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return 100000 + uid;
9683bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9693bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9703bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected static byte[] secureRandom(int length) {
9713bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        try {
9723bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return SecureRandom.getInstance("SHA1PRNG").generateSeed(length);
9733bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        } catch (NoSuchAlgorithmException e) {
9743bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            e.printStackTrace();
9753bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return null;
9763bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
9773bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9783bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9793bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private String getHandleName(long handle) {
9803bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
9813bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9823bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] computePasswordToken(String password, PasswordData data) {
9843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
9853bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu                PASSWORD_TOKEN_LENGTH);
9863bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    private byte[] passwordTokenToGkInput(byte[] token) {
9893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token);
9903bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
9913bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
9927b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    private byte[] passwordTokenToWeaverKey(byte[] token) {
9937b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
9947b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        if (key.length < mWeaverConfig.keySize) {
9957b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            throw new RuntimeException("weaver key length too small");
9967b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
9977b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return Arrays.copyOf(key, mWeaverConfig.keySize);
9987b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
9997b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
10003bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected long sidFromPasswordHandle(byte[] handle) {
10013bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return nativeSidFromPasswordHandle(handle);
10023bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10033bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10043bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
10053bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return nativeScrypt(password.getBytes(), salt, N, r, p, outLen);
10063bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10073bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10083bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    native long nativeSidFromPasswordHandle(byte[] handle);
10093bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen);
10103bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu
10117b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected static ArrayList<Byte> toByteArrayList(byte[] data) {
10127b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        ArrayList<Byte> result = new ArrayList<Byte>(data.length);
10137b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < data.length; i++) {
10147b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result.add(data[i]);
10157b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
10167b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
10177b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
10187b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
10197b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
10207b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        byte[] result = new byte[data.size()];
10217b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        for (int i = 0; i < data.size(); i++) {
10227b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu            result[i] = data.get(i);
10237b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        }
10247b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu        return result;
10257b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu    }
10267b7424b0b7c5b6be09ed6fb0ec70272574dfe718Rubin Xu
10273bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
10283bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    public static String bytesToHex(byte[] bytes) {
10293bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        if (bytes == null) {
10303bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            return "null";
10313bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
10323bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        char[] hexChars = new char[bytes.length * 2];
10333bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        for ( int j = 0; j < bytes.length; j++ ) {
10343bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            int v = bytes[j] & 0xFF;
10353bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            hexChars[j * 2] = hexArray[v >>> 4];
10363bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
10373bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        }
10383bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu        return new String(hexChars);
10393bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu    }
10403bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu}
1041