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