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