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