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