1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.locksettings; 18 19 import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.app.admin.PasswordMetrics; 25 import android.content.Context; 26 import android.content.pm.UserInfo; 27 import android.hardware.weaver.V1_0.IWeaver; 28 import android.hardware.weaver.V1_0.WeaverConfig; 29 import android.hardware.weaver.V1_0.WeaverReadResponse; 30 import android.hardware.weaver.V1_0.WeaverReadStatus; 31 import android.hardware.weaver.V1_0.WeaverStatus; 32 import android.os.RemoteCallbackList; 33 import android.os.RemoteException; 34 import android.os.UserManager; 35 import android.security.GateKeeper; 36 import android.security.Scrypt; 37 import android.service.gatekeeper.GateKeeperResponse; 38 import android.service.gatekeeper.IGateKeeperService; 39 import android.util.ArrayMap; 40 import android.util.ArraySet; 41 import android.util.Slog; 42 43 import com.android.internal.annotations.VisibleForTesting; 44 import com.android.internal.util.ArrayUtils; 45 import com.android.internal.widget.ICheckCredentialProgressCallback; 46 import com.android.internal.widget.IWeakEscrowTokenRemovedListener; 47 import com.android.internal.widget.LockPatternUtils; 48 import com.android.internal.widget.LockscreenCredential; 49 import com.android.internal.widget.VerifyCredentialResponse; 50 import com.android.server.locksettings.LockSettingsStorage.PersistentData; 51 52 import libcore.util.HexEncoding; 53 54 import java.lang.annotation.Retention; 55 import java.lang.annotation.RetentionPolicy; 56 import java.nio.ByteBuffer; 57 import java.security.NoSuchAlgorithmException; 58 import java.security.SecureRandom; 59 import java.util.ArrayList; 60 import java.util.Arrays; 61 import java.util.Collections; 62 import java.util.HashSet; 63 import java.util.List; 64 import java.util.Map; 65 import java.util.NoSuchElementException; 66 import java.util.Objects; 67 import java.util.Set; 68 69 70 /** 71 * A class that maintains the wrapping of synthetic password by user credentials or escrow tokens. 72 * It's (mostly) a pure storage for synthetic passwords, providing APIs to creating and destroying 73 * synthetic password blobs which are wrapped by user credentials or escrow tokens. 74 * 75 * Here is the assumptions it makes: 76 * Each user has one single synthetic password at any time. 77 * The SP has an associated password handle, which binds to the SID for that user. The password 78 * handle is persisted by SyntheticPasswordManager internally. 79 * If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD 80 * 81 * Information persisted on disk: 82 * for each user (stored under DEFAULT_HANDLE): 83 * SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user 84 * credential exists, cleared when user clears their credential. 85 * SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combined with escrow 86 * tokens. Destroyed when escrow support is turned off for the given user. 87 * 88 * for each SP blob under the user (stored under the corresponding handle): 89 * SP_BLOB_NAME: The encrypted synthetic password. Always exists. 90 * PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP. 91 * SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME for the 92 * purpose of secure deletion. Exists if this is a non-weaver SP 93 * (both password and token based), or it's a token-based SP under weaver. 94 * WEAVER_SLOT: Metadata about the weaver slot used. Only exists if this is a SP under weaver. 95 * 96 * 97 */ 98 public class SyntheticPasswordManager { 99 private static final String SP_BLOB_NAME = "spblob"; 100 private static final String SP_E0_NAME = "e0"; 101 private static final String SP_P1_NAME = "p1"; 102 private static final String SP_HANDLE_NAME = "handle"; 103 private static final String SECDISCARDABLE_NAME = "secdis"; 104 private static final int SECDISCARDABLE_LENGTH = 16 * 1024; 105 private static final String PASSWORD_DATA_NAME = "pwd"; 106 private static final String WEAVER_SLOT_NAME = "weaver"; 107 private static final String PASSWORD_METRICS_NAME = "metrics"; 108 109 public static final long DEFAULT_HANDLE = 0L; 110 private static final byte[] DEFAULT_PASSWORD = "default-password".getBytes(); 111 112 private static final byte WEAVER_VERSION = 1; 113 private static final int INVALID_WEAVER_SLOT = -1; 114 115 private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1; 116 private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2; 117 private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3; 118 private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0; 119 private static final byte SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED = 1; 120 private static final byte SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED = 2; 121 122 // 256-bit synthetic password 123 private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8; 124 125 private static final int PASSWORD_SCRYPT_N = 11; 126 private static final int PASSWORD_SCRYPT_R = 3; 127 private static final int PASSWORD_SCRYPT_P = 1; 128 private static final int PASSWORD_SALT_LENGTH = 16; 129 private static final int PASSWORD_TOKEN_LENGTH = 32; 130 private static final String TAG = "SyntheticPasswordManager"; 131 132 private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes(); 133 private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes(); 134 private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes(); 135 private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes(); 136 private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes(); 137 private static final byte[] PERSONALIZATION_AUTHSECRET_KEY = "authsecret-hal".getBytes(); 138 private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes(); 139 private static final byte[] PERSONALIZATION_PASSWORD_HASH = "pw-hash".getBytes(); 140 private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes(); 141 private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes(); 142 private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes(); 143 private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes(); 144 private static final byte[] PERSONALIZATION_PASSWORD_METRICS = "password-metrics".getBytes(); 145 private static final byte[] PERSONALISATION_CONTEXT = 146 "android-synthetic-password-personalization-context".getBytes(); 147 148 static class AuthenticationResult { 149 // Non-null if password/token passes verification, null otherwise 150 @Nullable public AuthenticationToken authToken; 151 // OK: password / token passes verification, user has a lockscreen 152 // null: user does not have a lockscreen (but password / token passes verification) 153 // ERROR: password / token fails verification 154 // RETRY: password / token verification is throttled at the moment. 155 @Nullable public VerifyCredentialResponse gkResponse; 156 } 157 158 /** 159 * This class represents the main cryptographic secret for a given user (a.k.a synthietic 160 * password). This secret is derived from the user's lockscreen credential or password escrow 161 * token. All other cryptograhic keys related to the user, including disk encryption key, 162 * keystore encryption key, gatekeeper auth key, vendor auth secret and others are directly 163 * derived from this token. 164 * <p> 165 * The main secret associated with an authentication token is retrievable from 166 * {@link AuthenticationToken#getSyntheticPassword()} and the authentication token can be 167 * reconsturcted from the main secret later with 168 * {@link AuthenticationToken#recreateDirectly(byte[])}. The first time an authentication token 169 * is needed, it should be created with {@link AuthenticationToken#create()} so that the 170 * necessary escrow data ({@link #mEncryptedEscrowSplit0} and {@link #mEscrowSplit1}) is 171 * properly initialized. The caller can either persist the (non-secret) esscrow data if escrow 172 * is required, or discard it to cryptograhically disable escrow. To support escrow, the caller 173 * needs to securely store the secret returned from 174 * {@link AuthenticationToken#getEscrowSecret()}, and at the time of use, load the escrow data 175 * back with {@link AuthenticationToken#setEscrowData(byte[], byte[])} and then re-create the 176 * main secret from the escrow secret via 177 * {@link AuthenticationToken#recreateFromEscrow(byte[])}. 178 */ 179 static class AuthenticationToken { 180 private final byte mVersion; 181 /** 182 * Here is the relationship between these fields: 183 * Generate two random block P0 and P1. P1 is recorded in mEscrowSplit1 but P0 is not. 184 * mSyntheticPassword = hash(P0 || P1) 185 * E0 = P0 encrypted under syntheticPassword, recoreded in mEncryptedEscrowSplit0. 186 */ 187 private @NonNull byte[] mSyntheticPassword; 188 private @Nullable byte[] mEncryptedEscrowSplit0; 189 private @Nullable byte[] mEscrowSplit1; 190 AuthenticationToken(byte version)191 AuthenticationToken(byte version) { 192 mVersion = version; 193 } 194 derivePassword(byte[] personalization)195 private byte[] derivePassword(byte[] personalization) { 196 if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) { 197 return (new SP800Derive(mSyntheticPassword)) 198 .withContext(personalization, PERSONALISATION_CONTEXT); 199 } else { 200 return SyntheticPasswordCrypto.personalisedHash(personalization, 201 mSyntheticPassword); 202 } 203 } 204 deriveKeyStorePassword()205 public byte[] deriveKeyStorePassword() { 206 return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD)); 207 } 208 deriveGkPassword()209 public byte[] deriveGkPassword() { 210 return derivePassword(PERSONALIZATION_SP_GK_AUTH); 211 } 212 deriveDiskEncryptionKey()213 public byte[] deriveDiskEncryptionKey() { 214 return derivePassword(PERSONALIZATION_FBE_KEY); 215 } 216 deriveVendorAuthSecret()217 public byte[] deriveVendorAuthSecret() { 218 return derivePassword(PERSONALIZATION_AUTHSECRET_KEY); 219 } 220 derivePasswordHashFactor()221 public byte[] derivePasswordHashFactor() { 222 return derivePassword(PERSONALIZATION_PASSWORD_HASH); 223 } 224 225 /** Derives key used to encrypt password metrics */ deriveMetricsKey()226 public byte[] deriveMetricsKey() { 227 return derivePassword(PERSONALIZATION_PASSWORD_METRICS); 228 } 229 230 /** 231 * Assign escrow data to this auth token. This is a prerequisite to call 232 * {@link AuthenticationToken#recreateFromEscrow}. 233 */ setEscrowData(@ullable byte[] encryptedEscrowSplit0, @Nullable byte[] escrowSplit1)234 public void setEscrowData(@Nullable byte[] encryptedEscrowSplit0, 235 @Nullable byte[] escrowSplit1) { 236 mEncryptedEscrowSplit0 = encryptedEscrowSplit0; 237 mEscrowSplit1 = escrowSplit1; 238 } 239 240 /** 241 * Re-creates authentication token from escrow secret (escrowSplit0, returned from 242 * {@link AuthenticationToken#getEscrowSecret}). Escrow data needs to be loaded 243 * by {@link #setEscrowData} before calling this. 244 */ recreateFromEscrow(byte[] escrowSplit0)245 public void recreateFromEscrow(byte[] escrowSplit0) { 246 Objects.requireNonNull(mEscrowSplit1); 247 Objects.requireNonNull(mEncryptedEscrowSplit0); 248 recreate(escrowSplit0, mEscrowSplit1); 249 } 250 251 /** 252 * Re-creates authentication token from synthetic password directly. 253 */ recreateDirectly(byte[] syntheticPassword)254 public void recreateDirectly(byte[] syntheticPassword) { 255 this.mSyntheticPassword = Arrays.copyOf(syntheticPassword, syntheticPassword.length); 256 } 257 258 /** 259 * Generates a new random synthetic password with escrow data. 260 */ create()261 static AuthenticationToken create() { 262 AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3); 263 byte[] escrowSplit0 = secureRandom(SYNTHETIC_PASSWORD_LENGTH); 264 byte[] escrowSplit1 = secureRandom(SYNTHETIC_PASSWORD_LENGTH); 265 result.recreate(escrowSplit0, escrowSplit1); 266 byte[] encrypteEscrowSplit0 = SyntheticPasswordCrypto.encrypt(result.mSyntheticPassword, 267 PERSONALIZATION_E0, escrowSplit0); 268 result.setEscrowData(encrypteEscrowSplit0, escrowSplit1); 269 return result; 270 } 271 272 /** 273 * Re-creates synthetic password from both escrow splits. See javadoc for 274 * AuthenticationToken.mSyntheticPassword for details on what each block means. 275 */ recreate(byte[] escrowSplit0, byte[] escrowSplit1)276 private void recreate(byte[] escrowSplit0, byte[] escrowSplit1) { 277 mSyntheticPassword = String.valueOf(HexEncoding.encode( 278 SyntheticPasswordCrypto.personalisedHash( 279 PERSONALIZATION_SP_SPLIT, escrowSplit0, escrowSplit1))).getBytes(); 280 } 281 282 /** 283 * Returns the escrow secret that can be used later to reconstruct this authentication 284 * token from {@link #recreateFromEscrow(byte[])}. Only possible if escrow is not disabled 285 * (encryptedEscrowSplit0 known). 286 */ getEscrowSecret()287 public byte[] getEscrowSecret() { 288 if (mEncryptedEscrowSplit0 == null) { 289 return null; 290 } 291 return SyntheticPasswordCrypto.decrypt(mSyntheticPassword, PERSONALIZATION_E0, 292 mEncryptedEscrowSplit0); 293 } 294 295 /** 296 * Returns the raw synthetic password that can be used later to reconstruct this 297 * authentication token from {@link #recreateDirectly(byte[])} 298 */ getSyntheticPassword()299 public byte[] getSyntheticPassword() { 300 return mSyntheticPassword; 301 } 302 303 /** 304 * Returns the version of this AuthenticationToken for use with reconstructing 305 * this with a synthetic password version. 306 */ getVersion()307 public byte getVersion() { 308 return mVersion; 309 } 310 } 311 312 static class PasswordData { 313 byte scryptN; 314 byte scryptR; 315 byte scryptP; 316 public int credentialType; 317 byte[] salt; 318 // For GateKeeper-based credential, this is the password handle returned by GK, 319 // for weaver-based credential, this is empty. 320 public byte[] passwordHandle; 321 create(int passwordType)322 public static PasswordData create(int passwordType) { 323 PasswordData result = new PasswordData(); 324 result.scryptN = PASSWORD_SCRYPT_N; 325 result.scryptR = PASSWORD_SCRYPT_R; 326 result.scryptP = PASSWORD_SCRYPT_P; 327 result.credentialType = passwordType; 328 result.salt = secureRandom(PASSWORD_SALT_LENGTH); 329 return result; 330 } 331 fromBytes(byte[] data)332 public static PasswordData fromBytes(byte[] data) { 333 PasswordData result = new PasswordData(); 334 ByteBuffer buffer = ByteBuffer.allocate(data.length); 335 buffer.put(data, 0, data.length); 336 buffer.flip(); 337 result.credentialType = buffer.getInt(); 338 result.scryptN = buffer.get(); 339 result.scryptR = buffer.get(); 340 result.scryptP = buffer.get(); 341 int saltLen = buffer.getInt(); 342 result.salt = new byte[saltLen]; 343 buffer.get(result.salt); 344 int handleLen = buffer.getInt(); 345 if (handleLen > 0) { 346 result.passwordHandle = new byte[handleLen]; 347 buffer.get(result.passwordHandle); 348 } else { 349 result.passwordHandle = null; 350 } 351 return result; 352 } 353 toBytes()354 public byte[] toBytes() { 355 356 ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES 357 + Integer.BYTES + salt.length + Integer.BYTES + 358 (passwordHandle != null ? passwordHandle.length : 0)); 359 buffer.putInt(credentialType); 360 buffer.put(scryptN); 361 buffer.put(scryptR); 362 buffer.put(scryptP); 363 buffer.putInt(salt.length); 364 buffer.put(salt); 365 if (passwordHandle != null && passwordHandle.length > 0) { 366 buffer.putInt(passwordHandle.length); 367 buffer.put(passwordHandle); 368 } else { 369 buffer.putInt(0); 370 } 371 return buffer.array(); 372 } 373 } 374 375 static class SyntheticPasswordBlob { 376 byte mVersion; 377 byte mType; 378 byte[] mContent; 379 create(byte version, byte type, byte[] content)380 public static SyntheticPasswordBlob create(byte version, byte type, byte[] content) { 381 SyntheticPasswordBlob result = new SyntheticPasswordBlob(); 382 result.mVersion = version; 383 result.mType = type; 384 result.mContent = content; 385 return result; 386 } 387 fromBytes(byte[] data)388 public static SyntheticPasswordBlob fromBytes(byte[] data) { 389 SyntheticPasswordBlob result = new SyntheticPasswordBlob(); 390 result.mVersion = data[0]; 391 result.mType = data[1]; 392 result.mContent = Arrays.copyOfRange(data, 2, data.length); 393 return result; 394 } 395 toByte()396 public byte[] toByte() { 397 byte[] blob = new byte[mContent.length + 1 + 1]; 398 blob[0] = mVersion; 399 blob[1] = mType; 400 System.arraycopy(mContent, 0, blob, 2, mContent.length); 401 return blob; 402 } 403 } 404 405 @Retention(RetentionPolicy.SOURCE) 406 @IntDef({TOKEN_TYPE_STRONG, TOKEN_TYPE_WEAK}) 407 @interface TokenType {} 408 static final int TOKEN_TYPE_STRONG = 0; 409 static final int TOKEN_TYPE_WEAK = 1; 410 411 static class TokenData { 412 byte[] secdiscardableOnDisk; 413 byte[] weaverSecret; 414 byte[] aggregatedSecret; 415 @TokenType int mType; 416 EscrowTokenStateChangeCallback mCallback; 417 } 418 419 private final Context mContext; 420 private LockSettingsStorage mStorage; 421 private IWeaver mWeaver; 422 private WeaverConfig mWeaverConfig; 423 private PasswordSlotManager mPasswordSlotManager; 424 425 private final UserManager mUserManager; 426 427 private final RemoteCallbackList<IWeakEscrowTokenRemovedListener> mListeners = 428 new RemoteCallbackList<>(); 429 SyntheticPasswordManager(Context context, LockSettingsStorage storage, UserManager userManager, PasswordSlotManager passwordSlotManager)430 public SyntheticPasswordManager(Context context, LockSettingsStorage storage, 431 UserManager userManager, PasswordSlotManager passwordSlotManager) { 432 mContext = context; 433 mStorage = storage; 434 mUserManager = userManager; 435 mPasswordSlotManager = passwordSlotManager; 436 } 437 438 @VisibleForTesting getWeaverService()439 protected IWeaver getWeaverService() throws RemoteException { 440 try { 441 return IWeaver.getService(/* retry */ true); 442 } catch (NoSuchElementException e) { 443 Slog.i(TAG, "Device does not support weaver"); 444 return null; 445 } 446 } 447 initWeaverService()448 public synchronized void initWeaverService() { 449 if (mWeaver != null) { 450 return; 451 } 452 try { 453 mWeaverConfig = null; 454 mWeaver = getWeaverService(); 455 if (mWeaver != null) { 456 mWeaver.getConfig((int status, WeaverConfig config) -> { 457 if (status == WeaverStatus.OK && config.slots > 0) { 458 mWeaverConfig = config; 459 } else { 460 Slog.e(TAG, "Failed to get weaver config, status " + status 461 + " slots: " + config.slots); 462 mWeaver = null; 463 } 464 }); 465 mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots()); 466 } 467 } catch (RemoteException e) { 468 Slog.e(TAG, "Failed to get weaver service", e); 469 } 470 } 471 isWeaverAvailable()472 private synchronized boolean isWeaverAvailable() { 473 if (mWeaver == null) { 474 //Re-initializing weaver in case there was a transient error preventing access to it. 475 initWeaverService(); 476 } 477 return mWeaver != null && mWeaverConfig.slots > 0; 478 } 479 480 /** 481 * Enroll the given key value pair into the specified weaver slot. if the given key is null, 482 * a default all-zero key is used. If the value is not specified, a fresh random secret is 483 * generated as the value. 484 * 485 * @return the value stored in the weaver slot, or null if the operation fails 486 */ weaverEnroll(int slot, byte[] key, @Nullable byte[] value)487 private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) { 488 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) { 489 throw new IllegalArgumentException("Invalid slot for weaver"); 490 } 491 if (key == null) { 492 key = new byte[mWeaverConfig.keySize]; 493 } else if (key.length != mWeaverConfig.keySize) { 494 throw new IllegalArgumentException("Invalid key size for weaver"); 495 } 496 if (value == null) { 497 value = secureRandom(mWeaverConfig.valueSize); 498 } 499 try { 500 int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value)); 501 if (writeStatus != WeaverStatus.OK) { 502 Slog.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus); 503 return null; 504 } 505 } catch (RemoteException e) { 506 Slog.e(TAG, "weaver write failed", e); 507 return null; 508 } 509 return value; 510 } 511 512 /** 513 * Verify the supplied key against a weaver slot, returning a response indicating whether 514 * the verification is successful, throttled or failed. If successful, the bound secret 515 * is also returned. 516 */ weaverVerify(int slot, byte[] key)517 private VerifyCredentialResponse weaverVerify(int slot, byte[] key) { 518 if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) { 519 throw new IllegalArgumentException("Invalid slot for weaver"); 520 } 521 if (key == null) { 522 key = new byte[mWeaverConfig.keySize]; 523 } else if (key.length != mWeaverConfig.keySize) { 524 throw new IllegalArgumentException("Invalid key size for weaver"); 525 } 526 final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1]; 527 try { 528 mWeaver.read(slot, toByteArrayList(key), 529 (int status, WeaverReadResponse readResponse) -> { 530 switch (status) { 531 case WeaverReadStatus.OK: 532 response[0] = new VerifyCredentialResponse.Builder().setGatekeeperHAT( 533 fromByteArrayList(readResponse.value)).build(); 534 break; 535 case WeaverReadStatus.THROTTLE: 536 response[0] = VerifyCredentialResponse 537 .fromTimeout(readResponse.timeout); 538 Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot); 539 break; 540 case WeaverReadStatus.INCORRECT_KEY: 541 if (readResponse.timeout == 0) { 542 response[0] = VerifyCredentialResponse.ERROR; 543 Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot); 544 } else { 545 response[0] = VerifyCredentialResponse 546 .fromTimeout(readResponse.timeout); 547 Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " 548 + slot); 549 } 550 break; 551 case WeaverReadStatus.FAILED: 552 response[0] = VerifyCredentialResponse.ERROR; 553 Slog.e(TAG, "weaver read failed (FAILED), slot: " + slot); 554 break; 555 default: 556 response[0] = VerifyCredentialResponse.ERROR; 557 Slog.e(TAG, "weaver read unknown status " + status + ", slot: " + slot); 558 break; 559 } 560 }); 561 } catch (RemoteException e) { 562 response[0] = VerifyCredentialResponse.ERROR; 563 Slog.e(TAG, "weaver read failed, slot: " + slot, e); 564 } 565 return response[0]; 566 } 567 removeUser(IGateKeeperService gatekeeper, int userId)568 public void removeUser(IGateKeeperService gatekeeper, int userId) { 569 for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) { 570 destroyWeaverSlot(handle, userId); 571 destroySPBlobKey(getKeyName(handle)); 572 } 573 // Remove potential persistent state (in RPMB), to prevent them from accumulating and 574 // causing problems. 575 try { 576 gatekeeper.clearSecureUserId(fakeUid(userId)); 577 } catch (RemoteException ignore) { 578 Slog.w(TAG, "Failed to clear SID from gatekeeper"); 579 } 580 } 581 getCredentialType(long handle, int userId)582 int getCredentialType(long handle, int userId) { 583 byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId); 584 if (passwordData == null) { 585 Slog.w(TAG, "getCredentialType: encountered empty password data for user " + userId); 586 return LockPatternUtils.CREDENTIAL_TYPE_NONE; 587 } 588 return PasswordData.fromBytes(passwordData).credentialType; 589 } 590 getFrpCredentialType(byte[] payload)591 static int getFrpCredentialType(byte[] payload) { 592 if (payload == null) { 593 return LockPatternUtils.CREDENTIAL_TYPE_NONE; 594 } 595 return PasswordData.fromBytes(payload).credentialType; 596 } 597 598 /** 599 * Initializing a new Authentication token, possibly from an existing credential and hash. 600 * 601 * The authentication token would bear a randomly-generated synthetic password. 602 * 603 * This method has the side effect of rebinding the SID of the given user to the 604 * newly-generated SP. 605 * 606 * If the existing credential hash is non-null, the existing SID mill be migrated so 607 * the synthetic password in the authentication token will produce the same SID 608 * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager 609 * in a per-user data storage.) 610 * 611 * If the existing credential hash is null, it means the given user should have no SID so 612 * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case, 613 * the supplied credential parameter is also ignored. 614 * 615 * Also saves the escrow information necessary to re-generate the synthetic password under 616 * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if 617 * password escrow should be disabled completely on the given user. 618 * 619 */ newSyntheticPasswordAndSid(IGateKeeperService gatekeeper, byte[] hash, LockscreenCredential credential, int userId)620 public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper, 621 byte[] hash, LockscreenCredential credential, int userId) { 622 AuthenticationToken result = AuthenticationToken.create(); 623 GateKeeperResponse response; 624 if (hash != null) { 625 try { 626 response = gatekeeper.enroll(userId, hash, credential.getCredential(), 627 result.deriveGkPassword()); 628 } catch (RemoteException e) { 629 throw new IllegalStateException("Failed to enroll credential duing SP init", e); 630 } 631 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { 632 Slog.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId); 633 clearSidForUser(userId); 634 } else { 635 saveSyntheticPasswordHandle(response.getPayload(), userId); 636 } 637 } else { 638 clearSidForUser(userId); 639 } 640 saveEscrowData(result, userId); 641 return result; 642 } 643 644 /** 645 * Enroll a new password handle and SID for the given synthetic password and persist it on disk. 646 * Used when adding password to previously-unsecured devices. 647 */ newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken, int userId)648 public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken, 649 int userId) { 650 GateKeeperResponse response; 651 try { 652 response = gatekeeper.enroll(userId, null, null, authToken.deriveGkPassword()); 653 } catch (RemoteException e) { 654 throw new IllegalStateException("Failed to create new SID for user", e); 655 } 656 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { 657 throw new IllegalStateException("Fail to create new SID for user " + userId 658 + " response: " + response.getResponseCode()); 659 } 660 saveSyntheticPasswordHandle(response.getPayload(), userId); 661 } 662 663 // Nuke the SP handle (and as a result, its SID) for the given user. clearSidForUser(int userId)664 public void clearSidForUser(int userId) { 665 destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId); 666 } 667 hasSidForUser(int userId)668 public boolean hasSidForUser(int userId) { 669 return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId); 670 } 671 672 // if null, it means there is no SID associated with the user 673 // This can happen if the user is migrated to SP but currently 674 // do not have a lockscreen password. loadSyntheticPasswordHandle(int userId)675 private byte[] loadSyntheticPasswordHandle(int userId) { 676 return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId); 677 } 678 saveSyntheticPasswordHandle(byte[] spHandle, int userId)679 private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) { 680 saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId); 681 } 682 loadEscrowData(AuthenticationToken authToken, int userId)683 private boolean loadEscrowData(AuthenticationToken authToken, int userId) { 684 byte[] e0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId); 685 byte[] p1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId); 686 authToken.setEscrowData(e0, p1); 687 return e0 != null && p1 != null; 688 } 689 saveEscrowData(AuthenticationToken authToken, int userId)690 private void saveEscrowData(AuthenticationToken authToken, int userId) { 691 saveState(SP_E0_NAME, authToken.mEncryptedEscrowSplit0, DEFAULT_HANDLE, userId); 692 saveState(SP_P1_NAME, authToken.mEscrowSplit1, DEFAULT_HANDLE, userId); 693 } 694 hasEscrowData(int userId)695 public boolean hasEscrowData(int userId) { 696 return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId) 697 && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId); 698 } 699 destroyEscrowData(int userId)700 public void destroyEscrowData(int userId) { 701 destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId); 702 destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId); 703 } 704 loadWeaverSlot(long handle, int userId)705 private int loadWeaverSlot(long handle, int userId) { 706 final int LENGTH = Byte.BYTES + Integer.BYTES; 707 byte[] data = loadState(WEAVER_SLOT_NAME, handle, userId); 708 if (data == null || data.length != LENGTH) { 709 return INVALID_WEAVER_SLOT; 710 } 711 ByteBuffer buffer = ByteBuffer.allocate(LENGTH); 712 buffer.put(data, 0, data.length); 713 buffer.flip(); 714 if (buffer.get() != WEAVER_VERSION) { 715 Slog.e(TAG, "Invalid weaver slot version of handle " + handle); 716 return INVALID_WEAVER_SLOT; 717 } 718 return buffer.getInt(); 719 } 720 saveWeaverSlot(int slot, long handle, int userId)721 private void saveWeaverSlot(int slot, long handle, int userId) { 722 ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES + Integer.BYTES); 723 buffer.put(WEAVER_VERSION); 724 buffer.putInt(slot); 725 saveState(WEAVER_SLOT_NAME, buffer.array(), handle, userId); 726 } 727 destroyWeaverSlot(long handle, int userId)728 private void destroyWeaverSlot(long handle, int userId) { 729 int slot = loadWeaverSlot(handle, userId); 730 destroyState(WEAVER_SLOT_NAME, handle, userId); 731 if (slot != INVALID_WEAVER_SLOT) { 732 Set<Integer> usedSlots = getUsedWeaverSlots(); 733 if (!usedSlots.contains(slot)) { 734 Slog.i(TAG, "Destroy weaver slot " + slot + " for user " + userId); 735 weaverEnroll(slot, null, null); 736 mPasswordSlotManager.markSlotDeleted(slot); 737 } else { 738 Slog.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId); 739 } 740 } 741 } 742 743 /** 744 * Return the set of weaver slots that are currently in use by all users on the device. 745 * <p> 746 * <em>Note:</em> Users who are in the process of being deleted are not tracked here 747 * (due to them being marked as partial in UserManager so not visible from 748 * {@link UserManager#getUsers}). As a result their weaver slots will not be considered 749 * taken and can be reused by new users. Care should be taken when cleaning up the 750 * deleted user in {@link #removeUser}, to prevent a reused slot from being erased 751 * unintentionally. 752 */ getUsedWeaverSlots()753 private Set<Integer> getUsedWeaverSlots() { 754 Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers( 755 WEAVER_SLOT_NAME); 756 HashSet<Integer> slots = new HashSet<>(); 757 for (Map.Entry<Integer, List<Long>> entry : slotHandles.entrySet()) { 758 for (Long handle : entry.getValue()) { 759 int slot = loadWeaverSlot(handle, entry.getKey()); 760 slots.add(slot); 761 } 762 } 763 return slots; 764 } 765 getNextAvailableWeaverSlot()766 private int getNextAvailableWeaverSlot() { 767 Set<Integer> usedSlots = getUsedWeaverSlots(); 768 usedSlots.addAll(mPasswordSlotManager.getUsedSlots()); 769 for (int i = 0; i < mWeaverConfig.slots; i++) { 770 if (!usedSlots.contains(i)) { 771 return i; 772 } 773 } 774 throw new IllegalStateException("Run out of weaver slots."); 775 } 776 777 /** 778 * Create a new password based SP blob based on the supplied authentication token, such that 779 * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result 780 * in the same authentication token. 781 * 782 * This method only creates SP blob wrapping around the given synthetic password and does not 783 * handle logic around SID or SP handle. The caller should separately ensure that the user's SID 784 * is consistent with the device state by calling other APIs in this class. 785 * 786 * @see #newSidForUser 787 * @see #clearSidForUser 788 * @return a new password handle for the wrapped SP blob 789 * @throw IllegalStateException if creation fails. 790 */ createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, LockscreenCredential credential, AuthenticationToken authToken, int userId)791 public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, 792 LockscreenCredential credential, AuthenticationToken authToken, int userId) { 793 long handle = generateHandle(); 794 PasswordData pwd = PasswordData.create(credential.getType()); 795 byte[] pwdToken = computePasswordToken(credential, pwd); 796 final long sid; 797 final byte[] applicationId; 798 799 if (isWeaverAvailable()) { 800 // Weaver based user password 801 int weaverSlot = getNextAvailableWeaverSlot(); 802 Slog.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId); 803 byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), 804 null); 805 if (weaverSecret == null) { 806 throw new IllegalStateException( 807 "Fail to enroll user password under weaver " + userId); 808 } 809 saveWeaverSlot(weaverSlot, handle, userId); 810 mPasswordSlotManager.markSlotInUse(weaverSlot); 811 // No need to pass in quality since the credential type already encodes sufficient info 812 synchronizeWeaverFrpPassword(pwd, 0, userId, weaverSlot); 813 814 pwd.passwordHandle = null; 815 sid = GateKeeper.INVALID_SECURE_USER_ID; 816 applicationId = transformUnderWeaverSecret(pwdToken, weaverSecret); 817 } else { 818 // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them 819 // to prevent them from accumulating and causing problems. 820 try { 821 gatekeeper.clearSecureUserId(fakeUid(userId)); 822 } catch (RemoteException ignore) { 823 Slog.w(TAG, "Failed to clear SID from gatekeeper"); 824 } 825 // GateKeeper based user password 826 GateKeeperResponse response; 827 try { 828 response = gatekeeper.enroll(fakeUid(userId), null, null, 829 passwordTokenToGkInput(pwdToken)); 830 } catch (RemoteException e) { 831 throw new IllegalStateException("Failed to enroll password for new SP blob", e); 832 } 833 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { 834 throw new IllegalStateException( 835 "Fail to enroll user password when creating SP for user " + userId); 836 } 837 pwd.passwordHandle = response.getPayload(); 838 sid = sidFromPasswordHandle(pwd.passwordHandle); 839 applicationId = transformUnderSecdiscardable(pwdToken, 840 createSecdiscardable(handle, userId)); 841 // No need to pass in quality since the credential type already encodes sufficient info 842 synchronizeFrpPassword(pwd, 0, userId); 843 } 844 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId); 845 savePasswordMetrics(credential, authToken, handle, userId); 846 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken, 847 applicationId, sid, userId); 848 return handle; 849 } 850 verifyFrpCredential(IGateKeeperService gatekeeper, LockscreenCredential userCredential, ICheckCredentialProgressCallback progressCallback)851 public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper, 852 LockscreenCredential userCredential, 853 ICheckCredentialProgressCallback progressCallback) { 854 PersistentData persistentData = mStorage.readPersistentDataBlock(); 855 if (persistentData.type == PersistentData.TYPE_SP) { 856 PasswordData pwd = PasswordData.fromBytes(persistentData.payload); 857 byte[] pwdToken = computePasswordToken(userCredential, pwd); 858 859 GateKeeperResponse response; 860 try { 861 response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId), 862 0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken)); 863 } catch (RemoteException e) { 864 Slog.e(TAG, "FRP verifyChallenge failed", e); 865 return VerifyCredentialResponse.ERROR; 866 } 867 return VerifyCredentialResponse.fromGateKeeperResponse(response); 868 } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) { 869 if (!isWeaverAvailable()) { 870 Slog.e(TAG, "No weaver service to verify SP-based FRP credential"); 871 return VerifyCredentialResponse.ERROR; 872 } 873 PasswordData pwd = PasswordData.fromBytes(persistentData.payload); 874 byte[] pwdToken = computePasswordToken(userCredential, pwd); 875 int weaverSlot = persistentData.userId; 876 877 return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload(); 878 } else { 879 Slog.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is " 880 + persistentData.type); 881 return VerifyCredentialResponse.ERROR; 882 } 883 } 884 885 migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality)886 public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) { 887 if (mStorage.getPersistentDataBlockManager() != null 888 && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) { 889 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, 890 userInfo.id)); 891 if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { 892 int weaverSlot = loadWeaverSlot(handle, userInfo.id); 893 if (weaverSlot != INVALID_WEAVER_SLOT) { 894 synchronizeWeaverFrpPassword(pwd, requestedQuality, userInfo.id, weaverSlot); 895 } else { 896 synchronizeFrpPassword(pwd, requestedQuality, userInfo.id); 897 } 898 } 899 } 900 } 901 synchronizeFrpPassword(PasswordData pwd, int requestedQuality, int userId)902 private void synchronizeFrpPassword(PasswordData pwd, 903 int requestedQuality, int userId) { 904 if (mStorage.getPersistentDataBlockManager() != null 905 && LockPatternUtils.userOwnsFrpCredential(mContext, 906 mUserManager.getUserInfo(userId))) { 907 if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { 908 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality, 909 pwd.toBytes()); 910 } else { 911 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null); 912 } 913 } 914 } 915 synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId, int weaverSlot)916 private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId, 917 int weaverSlot) { 918 if (mStorage.getPersistentDataBlockManager() != null 919 && LockPatternUtils.userOwnsFrpCredential(mContext, 920 mUserManager.getUserInfo(userId))) { 921 if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { 922 mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot, 923 requestedQuality, pwd.toBytes()); 924 } else { 925 mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, 0, 0, null); 926 } 927 } 928 } 929 930 private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>(); 931 932 /** 933 * Create a token based Synthetic password for the given user. 934 * @return the handle of the token 935 */ createStrongTokenBasedSyntheticPassword(byte[] token, int userId, @Nullable EscrowTokenStateChangeCallback changeCallback)936 public long createStrongTokenBasedSyntheticPassword(byte[] token, int userId, 937 @Nullable EscrowTokenStateChangeCallback changeCallback) { 938 return createTokenBasedSyntheticPassword(token, TOKEN_TYPE_STRONG, userId, 939 changeCallback); 940 } 941 942 /** 943 * Create a weak token based Synthetic password for the given user. 944 * @return the handle of the weak token 945 */ createWeakTokenBasedSyntheticPassword(byte[] token, int userId, @Nullable EscrowTokenStateChangeCallback changeCallback)946 public long createWeakTokenBasedSyntheticPassword(byte[] token, int userId, 947 @Nullable EscrowTokenStateChangeCallback changeCallback) { 948 return createTokenBasedSyntheticPassword(token, TOKEN_TYPE_WEAK, userId, 949 changeCallback); 950 } 951 952 /** 953 * Create a token based Synthetic password of the given type for the given user. 954 * @return the handle of the token 955 */ createTokenBasedSyntheticPassword(byte[] token, @TokenType int type, int userId, @Nullable EscrowTokenStateChangeCallback changeCallback)956 public long createTokenBasedSyntheticPassword(byte[] token, @TokenType int type, int userId, 957 @Nullable EscrowTokenStateChangeCallback changeCallback) { 958 long handle = generateHandle(); 959 if (!tokenMap.containsKey(userId)) { 960 tokenMap.put(userId, new ArrayMap<>()); 961 } 962 TokenData tokenData = new TokenData(); 963 tokenData.mType = type; 964 final byte[] secdiscardable = secureRandom(SECDISCARDABLE_LENGTH); 965 if (isWeaverAvailable()) { 966 tokenData.weaverSecret = secureRandom(mWeaverConfig.valueSize); 967 tokenData.secdiscardableOnDisk = SyntheticPasswordCrypto.encrypt(tokenData.weaverSecret, 968 PERSONALISATION_WEAVER_TOKEN, secdiscardable); 969 } else { 970 tokenData.secdiscardableOnDisk = secdiscardable; 971 tokenData.weaverSecret = null; 972 } 973 tokenData.aggregatedSecret = transformUnderSecdiscardable(token, secdiscardable); 974 tokenData.mCallback = changeCallback; 975 976 tokenMap.get(userId).put(handle, tokenData); 977 return handle; 978 } 979 getPendingTokensForUser(int userId)980 public Set<Long> getPendingTokensForUser(int userId) { 981 if (!tokenMap.containsKey(userId)) { 982 return Collections.emptySet(); 983 } 984 return new ArraySet<>(tokenMap.get(userId).keySet()); 985 } 986 987 /** Remove the given pending token. */ removePendingToken(long handle, int userId)988 public boolean removePendingToken(long handle, int userId) { 989 if (!tokenMap.containsKey(userId)) { 990 return false; 991 } 992 return tokenMap.get(userId).remove(handle) != null; 993 } 994 activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken, int userId)995 public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken, 996 int userId) { 997 if (!tokenMap.containsKey(userId)) { 998 return false; 999 } 1000 TokenData tokenData = tokenMap.get(userId).get(handle); 1001 if (tokenData == null) { 1002 return false; 1003 } 1004 if (!loadEscrowData(authToken, userId)) { 1005 Slog.w(TAG, "User is not escrowable"); 1006 return false; 1007 } 1008 if (isWeaverAvailable()) { 1009 int slot = getNextAvailableWeaverSlot(); 1010 Slog.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId); 1011 if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) { 1012 Slog.e(TAG, "Failed to enroll weaver secret when activating token"); 1013 return false; 1014 } 1015 saveWeaverSlot(slot, handle, userId); 1016 mPasswordSlotManager.markSlotInUse(slot); 1017 } 1018 saveSecdiscardable(handle, tokenData.secdiscardableOnDisk, userId); 1019 createSyntheticPasswordBlob(handle, getTokenBasedBlobType(tokenData.mType), authToken, 1020 tokenData.aggregatedSecret, 0L, userId); 1021 tokenMap.get(userId).remove(handle); 1022 if (tokenData.mCallback != null) { 1023 tokenData.mCallback.onEscrowTokenActivated(handle, userId); 1024 } 1025 return true; 1026 } 1027 createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken, byte[] applicationId, long sid, int userId)1028 private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken, 1029 byte[] applicationId, long sid, int userId) { 1030 final byte[] secret; 1031 if (type == SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED 1032 || type == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) { 1033 secret = authToken.getEscrowSecret(); 1034 } else { 1035 secret = authToken.getSyntheticPassword(); 1036 } 1037 byte[] content = createSPBlob(getKeyName(handle), secret, applicationId, sid); 1038 /* 1039 * We can upgrade from v1 to v2 because that's just a change in the way that 1040 * the SP is stored. However, we can't upgrade to v3 because that is a change 1041 * in the way that passwords are derived from the SP. 1042 */ 1043 byte version = authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3 1044 ? SYNTHETIC_PASSWORD_VERSION_V3 : SYNTHETIC_PASSWORD_VERSION_V2; 1045 1046 SyntheticPasswordBlob blob = SyntheticPasswordBlob.create(version, type, content); 1047 saveState(SP_BLOB_NAME, blob.toByte(), handle, userId); 1048 } 1049 1050 /** 1051 * Decrypt a synthetic password by supplying the user credential and corresponding password 1052 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper 1053 * verification to referesh the SID & Auth token maintained by the system. 1054 */ unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, long handle, @NonNull LockscreenCredential credential, int userId, ICheckCredentialProgressCallback progressCallback)1055 public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, 1056 long handle, @NonNull LockscreenCredential credential, int userId, 1057 ICheckCredentialProgressCallback progressCallback) { 1058 AuthenticationResult result = new AuthenticationResult(); 1059 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId)); 1060 1061 if (!credential.checkAgainstStoredType(pwd.credentialType)) { 1062 Slog.e(TAG, String.format("Credential type mismatch: expected %d actual %d", 1063 pwd.credentialType, credential.getType())); 1064 result.gkResponse = VerifyCredentialResponse.ERROR; 1065 return result; 1066 } 1067 1068 byte[] pwdToken = computePasswordToken(credential, pwd); 1069 1070 final byte[] applicationId; 1071 final long sid; 1072 int weaverSlot = loadWeaverSlot(handle, userId); 1073 if (weaverSlot != INVALID_WEAVER_SLOT) { 1074 // Weaver based user password 1075 if (!isWeaverAvailable()) { 1076 Slog.e(TAG, "No weaver service to unwrap password based SP"); 1077 result.gkResponse = VerifyCredentialResponse.ERROR; 1078 return result; 1079 } 1080 result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)); 1081 if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) { 1082 return result; 1083 } 1084 sid = GateKeeper.INVALID_SECURE_USER_ID; 1085 applicationId = transformUnderWeaverSecret(pwdToken, 1086 result.gkResponse.getGatekeeperHAT()); 1087 } else { 1088 byte[] gkPwdToken = passwordTokenToGkInput(pwdToken); 1089 GateKeeperResponse response; 1090 try { 1091 response = gatekeeper.verifyChallenge(fakeUid(userId), 0L, 1092 pwd.passwordHandle, gkPwdToken); 1093 } catch (RemoteException e) { 1094 Slog.e(TAG, "gatekeeper verify failed", e); 1095 result.gkResponse = VerifyCredentialResponse.ERROR; 1096 return result; 1097 } 1098 int responseCode = response.getResponseCode(); 1099 if (responseCode == GateKeeperResponse.RESPONSE_OK) { 1100 result.gkResponse = VerifyCredentialResponse.OK; 1101 if (response.getShouldReEnroll()) { 1102 GateKeeperResponse reenrollResponse; 1103 try { 1104 reenrollResponse = gatekeeper.enroll(fakeUid(userId), 1105 pwd.passwordHandle, gkPwdToken, gkPwdToken); 1106 } catch (RemoteException e) { 1107 Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e); 1108 reenrollResponse = GateKeeperResponse.ERROR; 1109 // continue the flow anyway 1110 } 1111 if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) { 1112 pwd.passwordHandle = reenrollResponse.getPayload(); 1113 // Use the reenrollment opportunity to update credential type 1114 // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN) 1115 pwd.credentialType = credential.getType(); 1116 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId); 1117 synchronizeFrpPassword(pwd, 0, userId); 1118 } else { 1119 Slog.w(TAG, "Fail to re-enroll user password for user " + userId); 1120 // continue the flow anyway 1121 } 1122 } 1123 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) { 1124 result.gkResponse = VerifyCredentialResponse.fromTimeout(response.getTimeout()); 1125 return result; 1126 } else { 1127 result.gkResponse = VerifyCredentialResponse.ERROR; 1128 return result; 1129 } 1130 sid = sidFromPasswordHandle(pwd.passwordHandle); 1131 applicationId = transformUnderSecdiscardable(pwdToken, 1132 loadSecdiscardable(handle, userId)); 1133 } 1134 // Supplied credential passes first stage weaver/gatekeeper check so it should be correct. 1135 // Notify the callback so the keyguard UI can proceed immediately. 1136 if (progressCallback != null) { 1137 try { 1138 progressCallback.onCredentialVerified(); 1139 } catch (RemoteException e) { 1140 Slog.w(TAG, "progressCallback throws exception", e); 1141 } 1142 } 1143 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, 1144 applicationId, sid, userId); 1145 1146 // Perform verifyChallenge to refresh auth tokens for GK if user password exists. 1147 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId); 1148 1149 // Upgrade case: store the metrics if the device did not have stored metrics before, should 1150 // only happen once on old synthetic password blobs. 1151 if (result.authToken != null && !hasPasswordMetrics(handle, userId)) { 1152 savePasswordMetrics(credential, result.authToken, handle, userId); 1153 } 1154 return result; 1155 } 1156 1157 /** 1158 * Decrypt a synthetic password by supplying an escrow token and corresponding token 1159 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper 1160 * verification to referesh the SID & Auth token maintained by the system. 1161 */ unwrapTokenBasedSyntheticPassword( IGateKeeperService gatekeeper, long handle, byte[] token, int userId)1162 public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword( 1163 IGateKeeperService gatekeeper, long handle, byte[] token, int userId) { 1164 SyntheticPasswordBlob blob = SyntheticPasswordBlob 1165 .fromBytes(loadState(SP_BLOB_NAME, handle, userId)); 1166 return unwrapTokenBasedSyntheticPasswordInternal(gatekeeper, handle, 1167 blob.mType, token, userId); 1168 } 1169 1170 /** 1171 * Decrypt a synthetic password by supplying an strong escrow token and corresponding token 1172 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper 1173 * verification to referesh the SID & Auth token maintained by the system. 1174 */ unwrapStrongTokenBasedSyntheticPassword( IGateKeeperService gatekeeper, long handle, byte[] token, int userId)1175 public @NonNull AuthenticationResult unwrapStrongTokenBasedSyntheticPassword( 1176 IGateKeeperService gatekeeper, long handle, byte[] token, int userId) { 1177 return unwrapTokenBasedSyntheticPasswordInternal(gatekeeper, handle, 1178 SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED, token, userId); 1179 } 1180 1181 /** 1182 * Decrypt a synthetic password by supplying a weak escrow token and corresponding token 1183 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper 1184 * verification to referesh the SID & Auth token maintained by the system. 1185 */ unwrapWeakTokenBasedSyntheticPassword( IGateKeeperService gatekeeper, long handle, byte[] token, int userId)1186 public @NonNull AuthenticationResult unwrapWeakTokenBasedSyntheticPassword( 1187 IGateKeeperService gatekeeper, long handle, byte[] token, int userId) { 1188 return unwrapTokenBasedSyntheticPasswordInternal(gatekeeper, handle, 1189 SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED, token, userId); 1190 } 1191 unwrapTokenBasedSyntheticPasswordInternal( IGateKeeperService gatekeeper, long handle, byte type, byte[] token, int userId)1192 private @NonNull AuthenticationResult unwrapTokenBasedSyntheticPasswordInternal( 1193 IGateKeeperService gatekeeper, long handle, byte type, byte[] token, int userId) { 1194 AuthenticationResult result = new AuthenticationResult(); 1195 byte[] secdiscardable = loadSecdiscardable(handle, userId); 1196 int slotId = loadWeaverSlot(handle, userId); 1197 if (slotId != INVALID_WEAVER_SLOT) { 1198 if (!isWeaverAvailable()) { 1199 Slog.e(TAG, "No weaver service to unwrap token based SP"); 1200 result.gkResponse = VerifyCredentialResponse.ERROR; 1201 return result; 1202 } 1203 VerifyCredentialResponse response = weaverVerify(slotId, null); 1204 if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK || 1205 response.getGatekeeperHAT() == null) { 1206 Slog.e(TAG, "Failed to retrieve weaver secret when unwrapping token"); 1207 result.gkResponse = VerifyCredentialResponse.ERROR; 1208 return result; 1209 } 1210 secdiscardable = SyntheticPasswordCrypto.decrypt(response.getGatekeeperHAT(), 1211 PERSONALISATION_WEAVER_TOKEN, secdiscardable); 1212 } 1213 byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable); 1214 result.authToken = unwrapSyntheticPasswordBlob(handle, type, applicationId, 0L, userId); 1215 if (result.authToken != null) { 1216 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId); 1217 if (result.gkResponse == null) { 1218 // The user currently has no password. return OK with null payload so null 1219 // is propagated to unlockUser() 1220 result.gkResponse = VerifyCredentialResponse.OK; 1221 } 1222 } else { 1223 result.gkResponse = VerifyCredentialResponse.ERROR; 1224 } 1225 return result; 1226 } 1227 unwrapSyntheticPasswordBlob(long handle, byte type, byte[] applicationId, long sid, int userId)1228 private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type, 1229 byte[] applicationId, long sid, int userId) { 1230 byte[] data = loadState(SP_BLOB_NAME, handle, userId); 1231 if (data == null) { 1232 return null; 1233 } 1234 SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(data); 1235 if (blob.mVersion != SYNTHETIC_PASSWORD_VERSION_V3 1236 && blob.mVersion != SYNTHETIC_PASSWORD_VERSION_V2 1237 && blob.mVersion != SYNTHETIC_PASSWORD_VERSION_V1) { 1238 throw new IllegalArgumentException("Unknown blob version"); 1239 } 1240 if (blob.mType != type) { 1241 throw new IllegalArgumentException("Invalid blob type"); 1242 } 1243 final byte[] secret; 1244 if (blob.mVersion == SYNTHETIC_PASSWORD_VERSION_V1) { 1245 secret = SyntheticPasswordCrypto.decryptBlobV1(getKeyName(handle), blob.mContent, 1246 applicationId); 1247 } else { 1248 secret = decryptSPBlob(getKeyName(handle), blob.mContent, applicationId); 1249 } 1250 if (secret == null) { 1251 Slog.e(TAG, "Fail to decrypt SP for user " + userId); 1252 return null; 1253 } 1254 AuthenticationToken result = new AuthenticationToken(blob.mVersion); 1255 if (type == SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED 1256 || type == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) { 1257 if (!loadEscrowData(result, userId)) { 1258 Slog.e(TAG, "User is not escrowable: " + userId); 1259 return null; 1260 } 1261 result.recreateFromEscrow(secret); 1262 } else { 1263 result.recreateDirectly(secret); 1264 } 1265 if (blob.mVersion == SYNTHETIC_PASSWORD_VERSION_V1) { 1266 Slog.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type); 1267 createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId); 1268 } 1269 return result; 1270 } 1271 1272 /** 1273 * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle 1274 * if required. 1275 * 1276 * Normally performing verifyChallenge with an AuthenticationToken should always return 1277 * RESPONSE_OK, since user authentication failures are detected earlier when trying to 1278 * decrypt SP. 1279 */ verifyChallenge(IGateKeeperService gatekeeper, @NonNull AuthenticationToken auth, long challenge, int userId)1280 public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper, 1281 @NonNull AuthenticationToken auth, long challenge, int userId) { 1282 return verifyChallengeInternal(gatekeeper, auth.deriveGkPassword(), challenge, userId); 1283 } 1284 verifyChallengeInternal( IGateKeeperService gatekeeper, @NonNull byte[] gatekeeperPassword, long challenge, int userId)1285 protected @Nullable VerifyCredentialResponse verifyChallengeInternal( 1286 IGateKeeperService gatekeeper, @NonNull byte[] gatekeeperPassword, long challenge, 1287 int userId) { 1288 byte[] spHandle = loadSyntheticPasswordHandle(userId); 1289 if (spHandle == null) { 1290 // There is no password handle associated with the given user, i.e. the user is not 1291 // secured by lockscreen and has no SID, so just return here; 1292 return null; 1293 } 1294 GateKeeperResponse response; 1295 try { 1296 response = gatekeeper.verifyChallenge(userId, challenge, 1297 spHandle, gatekeeperPassword); 1298 } catch (RemoteException e) { 1299 Slog.e(TAG, "Fail to verify with gatekeeper " + userId, e); 1300 return VerifyCredentialResponse.ERROR; 1301 } 1302 int responseCode = response.getResponseCode(); 1303 if (responseCode == GateKeeperResponse.RESPONSE_OK) { 1304 VerifyCredentialResponse result = new VerifyCredentialResponse.Builder() 1305 .setGatekeeperHAT(response.getPayload()).build(); 1306 if (response.getShouldReEnroll()) { 1307 try { 1308 response = gatekeeper.enroll(userId, spHandle, spHandle, 1309 gatekeeperPassword); 1310 } catch (RemoteException e) { 1311 Slog.e(TAG, "Failed to invoke gatekeeper.enroll", e); 1312 response = GateKeeperResponse.ERROR; 1313 } 1314 if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) { 1315 spHandle = response.getPayload(); 1316 saveSyntheticPasswordHandle(spHandle, userId); 1317 // Call self again to re-verify with updated handle 1318 return verifyChallengeInternal(gatekeeper, gatekeeperPassword, challenge, 1319 userId); 1320 } else { 1321 // Fall through, return result from the previous verification attempt. 1322 Slog.w(TAG, "Fail to re-enroll SP handle for user " + userId); 1323 } 1324 } 1325 return result; 1326 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) { 1327 return VerifyCredentialResponse.fromTimeout(response.getTimeout()); 1328 } else { 1329 return VerifyCredentialResponse.ERROR; 1330 } 1331 } 1332 existsHandle(long handle, int userId)1333 public boolean existsHandle(long handle, int userId) { 1334 return hasState(SP_BLOB_NAME, handle, userId); 1335 } 1336 1337 /** Destroy the escrow token with the given handle for the given user. */ destroyTokenBasedSyntheticPassword(long handle, int userId)1338 public void destroyTokenBasedSyntheticPassword(long handle, int userId) { 1339 SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(loadState(SP_BLOB_NAME, handle, 1340 userId)); 1341 destroySyntheticPassword(handle, userId); 1342 destroyState(SECDISCARDABLE_NAME, handle, userId); 1343 if (blob.mType == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) { 1344 notifyWeakEscrowTokenRemovedListeners(handle, userId); 1345 } 1346 } 1347 1348 /** Destroy all weak escrow tokens for the given user. */ destroyAllWeakTokenBasedSyntheticPasswords(int userId)1349 public void destroyAllWeakTokenBasedSyntheticPasswords(int userId) { 1350 List<Long> handles = mStorage.listSyntheticPasswordHandlesForUser(SECDISCARDABLE_NAME, 1351 userId); 1352 for (long handle: handles) { 1353 SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(loadState(SP_BLOB_NAME, 1354 handle, userId)); 1355 if (blob.mType == SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED) { 1356 destroyTokenBasedSyntheticPassword(handle, userId); 1357 } 1358 } 1359 } 1360 destroyPasswordBasedSyntheticPassword(long handle, int userId)1361 public void destroyPasswordBasedSyntheticPassword(long handle, int userId) { 1362 destroySyntheticPassword(handle, userId); 1363 destroyState(SECDISCARDABLE_NAME, handle, userId); 1364 destroyState(PASSWORD_DATA_NAME, handle, userId); 1365 destroyState(PASSWORD_METRICS_NAME, handle, userId); 1366 } 1367 destroySyntheticPassword(long handle, int userId)1368 private void destroySyntheticPassword(long handle, int userId) { 1369 destroyState(SP_BLOB_NAME, handle, userId); 1370 destroySPBlobKey(getKeyName(handle)); 1371 if (hasState(WEAVER_SLOT_NAME, handle, userId)) { 1372 destroyWeaverSlot(handle, userId); 1373 } 1374 } 1375 transformUnderWeaverSecret(byte[] data, byte[] secret)1376 private byte[] transformUnderWeaverSecret(byte[] data, byte[] secret) { 1377 byte[] weaverSecret = SyntheticPasswordCrypto.personalisedHash( 1378 PERSONALISATION_WEAVER_PASSWORD, secret); 1379 byte[] result = new byte[data.length + weaverSecret.length]; 1380 System.arraycopy(data, 0, result, 0, data.length); 1381 System.arraycopy(weaverSecret, 0, result, data.length, weaverSecret.length); 1382 return result; 1383 } 1384 transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable)1385 private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) { 1386 byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash( 1387 PERSONALISATION_SECDISCARDABLE, rawSecdiscardable); 1388 byte[] result = new byte[data.length + secdiscardable.length]; 1389 System.arraycopy(data, 0, result, 0, data.length); 1390 System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length); 1391 return result; 1392 } 1393 createSecdiscardable(long handle, int userId)1394 private byte[] createSecdiscardable(long handle, int userId) { 1395 byte[] data = secureRandom(SECDISCARDABLE_LENGTH); 1396 saveSecdiscardable(handle, data, userId); 1397 return data; 1398 } 1399 saveSecdiscardable(long handle, byte[] secdiscardable, int userId)1400 private void saveSecdiscardable(long handle, byte[] secdiscardable, int userId) { 1401 saveState(SECDISCARDABLE_NAME, secdiscardable, handle, userId); 1402 } 1403 loadSecdiscardable(long handle, int userId)1404 private byte[] loadSecdiscardable(long handle, int userId) { 1405 return loadState(SECDISCARDABLE_NAME, handle, userId); 1406 } 1407 getTokenBasedBlobType(@okenType int type)1408 private byte getTokenBasedBlobType(@TokenType int type) { 1409 switch (type) { 1410 case TOKEN_TYPE_WEAK: 1411 return SYNTHETIC_PASSWORD_WEAK_TOKEN_BASED; 1412 case TOKEN_TYPE_STRONG: 1413 default: 1414 return SYNTHETIC_PASSWORD_STRONG_TOKEN_BASED; 1415 } 1416 } 1417 1418 /** 1419 * Retrieves the saved password metrics associated with a SP handle. Only meaningful to be 1420 * called on the handle of a password-based synthetic password. A valid AuthenticationToken for 1421 * the target user is required in order to be able to decrypt the encrypted password metrics on 1422 * disk. 1423 */ getPasswordMetrics(AuthenticationToken authToken, long handle, int userId)1424 public @Nullable PasswordMetrics getPasswordMetrics(AuthenticationToken authToken, long handle, 1425 int userId) { 1426 final byte[] encrypted = loadState(PASSWORD_METRICS_NAME, handle, userId); 1427 if (encrypted == null) return null; 1428 final byte[] decrypted = SyntheticPasswordCrypto.decrypt(authToken.deriveMetricsKey(), 1429 /* personalization= */ new byte[0], encrypted); 1430 if (decrypted == null) return null; 1431 return VersionedPasswordMetrics.deserialize(decrypted).getMetrics(); 1432 } 1433 savePasswordMetrics(LockscreenCredential credential, AuthenticationToken authToken, long handle, int userId)1434 private void savePasswordMetrics(LockscreenCredential credential, AuthenticationToken authToken, 1435 long handle, int userId) { 1436 final byte[] encrypted = SyntheticPasswordCrypto.encrypt(authToken.deriveMetricsKey(), 1437 /* personalization= */ new byte[0], 1438 new VersionedPasswordMetrics(credential).serialize()); 1439 saveState(PASSWORD_METRICS_NAME, encrypted, handle, userId); 1440 } 1441 hasPasswordMetrics(long handle, int userId)1442 private boolean hasPasswordMetrics(long handle, int userId) { 1443 return hasState(PASSWORD_METRICS_NAME, handle, userId); 1444 } 1445 hasState(String stateName, long handle, int userId)1446 private boolean hasState(String stateName, long handle, int userId) { 1447 return !ArrayUtils.isEmpty(loadState(stateName, handle, userId)); 1448 } 1449 loadState(String stateName, long handle, int userId)1450 private byte[] loadState(String stateName, long handle, int userId) { 1451 return mStorage.readSyntheticPasswordState(userId, handle, stateName); 1452 } 1453 saveState(String stateName, byte[] data, long handle, int userId)1454 private void saveState(String stateName, byte[] data, long handle, int userId) { 1455 mStorage.writeSyntheticPasswordState(userId, handle, stateName, data); 1456 } 1457 destroyState(String stateName, long handle, int userId)1458 private void destroyState(String stateName, long handle, int userId) { 1459 mStorage.deleteSyntheticPasswordState(userId, handle, stateName); 1460 } 1461 decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId)1462 protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) { 1463 return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId); 1464 } 1465 createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid)1466 protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) { 1467 return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid); 1468 } 1469 destroySPBlobKey(String keyAlias)1470 protected void destroySPBlobKey(String keyAlias) { 1471 SyntheticPasswordCrypto.destroyBlobKey(keyAlias); 1472 } 1473 generateHandle()1474 public static long generateHandle() { 1475 SecureRandom rng = new SecureRandom(); 1476 long result; 1477 do { 1478 result = rng.nextLong(); 1479 } while (result == DEFAULT_HANDLE); 1480 return result; 1481 } 1482 fakeUid(int uid)1483 private int fakeUid(int uid) { 1484 return 100000 + uid; 1485 } 1486 secureRandom(int length)1487 protected static byte[] secureRandom(int length) { 1488 try { 1489 return SecureRandom.getInstance("SHA1PRNG").generateSeed(length); 1490 } catch (NoSuchAlgorithmException e) { 1491 e.printStackTrace(); 1492 return null; 1493 } 1494 } 1495 getKeyName(long handle)1496 private String getKeyName(long handle) { 1497 return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle); 1498 } 1499 computePasswordToken(LockscreenCredential credential, PasswordData data)1500 private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) { 1501 final byte[] password = credential.isNone() ? DEFAULT_PASSWORD : credential.getCredential(); 1502 return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP, 1503 PASSWORD_TOKEN_LENGTH); 1504 } 1505 passwordTokenToGkInput(byte[] token)1506 private byte[] passwordTokenToGkInput(byte[] token) { 1507 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token); 1508 } 1509 passwordTokenToWeaverKey(byte[] token)1510 private byte[] passwordTokenToWeaverKey(byte[] token) { 1511 byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token); 1512 if (key.length < mWeaverConfig.keySize) { 1513 throw new IllegalArgumentException("weaver key length too small"); 1514 } 1515 return Arrays.copyOf(key, mWeaverConfig.keySize); 1516 } 1517 sidFromPasswordHandle(byte[] handle)1518 protected long sidFromPasswordHandle(byte[] handle) { 1519 return nativeSidFromPasswordHandle(handle); 1520 } 1521 scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen)1522 protected byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) { 1523 return new Scrypt().scrypt(password, salt, n, r, p, outLen); 1524 } 1525 nativeSidFromPasswordHandle(byte[] handle)1526 native long nativeSidFromPasswordHandle(byte[] handle); 1527 toByteArrayList(byte[] data)1528 protected static ArrayList<Byte> toByteArrayList(byte[] data) { 1529 ArrayList<Byte> result = new ArrayList<Byte>(data.length); 1530 for (int i = 0; i < data.length; i++) { 1531 result.add(data[i]); 1532 } 1533 return result; 1534 } 1535 fromByteArrayList(ArrayList<Byte> data)1536 protected static byte[] fromByteArrayList(ArrayList<Byte> data) { 1537 byte[] result = new byte[data.size()]; 1538 for (int i = 0; i < data.size(); i++) { 1539 result[i] = data.get(i); 1540 } 1541 return result; 1542 } 1543 1544 protected static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(); bytesToHex(byte[] bytes)1545 private static byte[] bytesToHex(byte[] bytes) { 1546 if (bytes == null) { 1547 return "null".getBytes(); 1548 } 1549 byte[] hexBytes = new byte[bytes.length * 2]; 1550 for ( int j = 0; j < bytes.length; j++ ) { 1551 int v = bytes[j] & 0xFF; 1552 hexBytes[j * 2] = HEX_ARRAY[v >>> 4]; 1553 hexBytes[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; 1554 } 1555 return hexBytes; 1556 } 1557 1558 /** 1559 * Migrate all existing SP keystore keys from uid 1000 app domain to LSS selinux domain 1560 */ migrateKeyNamespace()1561 public boolean migrateKeyNamespace() { 1562 boolean success = true; 1563 final Map<Integer, List<Long>> allHandles = 1564 mStorage.listSyntheticPasswordHandlesForAllUsers(SP_BLOB_NAME); 1565 for (List<Long> userHandles : allHandles.values()) { 1566 for (long handle : userHandles) { 1567 success &= SyntheticPasswordCrypto.migrateLockSettingsKey(getKeyName(handle)); 1568 } 1569 } 1570 return success; 1571 } 1572 1573 /** Register the given IWeakEscrowTokenRemovedListener. */ registerWeakEscrowTokenRemovedListener( IWeakEscrowTokenRemovedListener listener)1574 public boolean registerWeakEscrowTokenRemovedListener( 1575 IWeakEscrowTokenRemovedListener listener) { 1576 return mListeners.register(listener); 1577 } 1578 1579 /** Unregister the given IWeakEscrowTokenRemovedListener. */ unregisterWeakEscrowTokenRemovedListener( IWeakEscrowTokenRemovedListener listener)1580 public boolean unregisterWeakEscrowTokenRemovedListener( 1581 IWeakEscrowTokenRemovedListener listener) { 1582 return mListeners.unregister(listener); 1583 } 1584 notifyWeakEscrowTokenRemovedListeners(long handle, int userId)1585 private void notifyWeakEscrowTokenRemovedListeners(long handle, int userId) { 1586 int i = mListeners.beginBroadcast(); 1587 try { 1588 while (i > 0) { 1589 i--; 1590 try { 1591 mListeners.getBroadcastItem(i).onWeakEscrowTokenRemoved(handle, userId); 1592 } catch (RemoteException e) { 1593 Slog.e(TAG, "Exception while notifying WeakEscrowTokenRemovedListener.", 1594 e); 1595 } 1596 } 1597 } finally { 1598 mListeners.finishBroadcast(); 1599 } 1600 } 1601 } 1602