SyntheticPasswordManager.java revision aa32d1530594db74e730e99d5ebbf8809bacd9d1
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) { 455aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu destroyState(SP_HANDLE_NAME, 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) { 490aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId); 491aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu destroyState(SP_P1_NAME, 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 } 526aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu destroyState(WEAVER_SLOT_NAME, 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); 880aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu destroyState(SECDISCARDABLE_NAME, handle, userId); 881f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu } 882f095f8366bac52ac1eeb2b3eb1a403294ceeb541Rubin Xu 8833bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu public void destroyPasswordBasedSyntheticPassword(long handle, int userId) { 8843bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu destroySyntheticPassword(handle, userId); 885aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu destroyState(SECDISCARDABLE_NAME, handle, userId); 886aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu destroyState(PASSWORD_DATA_NAME, handle, userId); 8873bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu } 8883bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu 8893bf722a8d54ca7192dfe07ee7b73eac7d25ccac5Rubin Xu private void destroySyntheticPassword(long handle, int userId) { 890aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu destroyState(SP_BLOB_NAME, 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 941aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu private void destroyState(String stateName, long handle, int userId) { 942aa32d1530594db74e730e99d5ebbf8809bacd9d1Rubin Xu mStorage.deleteSyntheticPasswordState(userId, handle, stateName); 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