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