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