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 package com.android.server; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.os.RemoteException; 21 import android.service.gatekeeper.GateKeeperResponse; 22 import android.service.gatekeeper.IGateKeeperService; 23 import android.util.ArrayMap; 24 import android.util.Log; 25 26 import com.android.internal.util.ArrayUtils; 27 import com.android.internal.widget.LockPatternUtils; 28 import com.android.internal.widget.VerifyCredentialResponse; 29 30 import libcore.util.HexEncoding; 31 32 import java.nio.ByteBuffer; 33 import java.security.MessageDigest; 34 import java.security.NoSuchAlgorithmException; 35 import java.security.SecureRandom; 36 import java.util.Arrays; 37 import java.util.Collections; 38 import java.util.Set; 39 40 41 /** 42 * A class that maintains the wrapping of synthetic password by user credentials or escrow tokens. 43 * It's (mostly) a pure storage for synthetic passwords, providing APIs to creating and destroying 44 * synthetic password blobs which are wrapped by user credentials or escrow tokens. 45 * 46 * Here is the assumptions it makes: 47 * Each user has one single synthetic password at any time. 48 * The SP has an associated password handle, which binds to the SID for that user. The password 49 * handle is persisted by SyntheticPasswordManager internally. 50 * If the user credential is null, it's treated as if the credential is DEFAULT_PASSWORD 51 * 52 * Information persisted on disk: 53 * for each user (stored under DEFAULT_HANDLE): 54 * SP_HANDLE_NAME: GateKeeper password handle of synthetic password. Only available if user 55 * credential exists, cleared when user clears their credential. 56 * SP_E0_NAME, SP_P1_NAME: Secret to derive synthetic password when combined with escrow 57 * tokens. Destroyed when escrow support is turned off for the given user. 58 * 59 * for each SP blob under the user (stored under the corresponding handle): 60 * SP_BLOB_NAME: The encrypted synthetic password. Always exists. 61 * PASSWORD_DATA_NAME: Metadata about user credential. Only exists for password based SP. 62 * SECDISCARDABLE_NAME: Part of the necessary ingredient to decrypt SP_BLOB_NAME for the 63 * purpose of secure deletion. 64 * 65 */ 66 public class SyntheticPasswordManager { 67 private static final String SP_BLOB_NAME = "spblob"; 68 private static final String SP_E0_NAME = "e0"; 69 private static final String SP_P1_NAME = "p1"; 70 private static final String SP_HANDLE_NAME = "handle"; 71 private static final String SECDISCARDABLE_NAME = "secdis"; 72 private static final int SECDISCARDABLE_LENGTH = 16 * 1024; 73 private static final String PASSWORD_DATA_NAME = "pwd"; 74 75 public static final long DEFAULT_HANDLE = 0; 76 private static final String DEFAULT_PASSWORD = "default-password"; 77 78 private static final byte SYNTHETIC_PASSWORD_VERSION = 1; 79 private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0; 80 private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1; 81 82 // 256-bit synthetic password 83 private static final byte SYNTHETIC_PASSWORD_LENGTH = 256 / 8; 84 85 private static final int PASSWORD_SCRYPT_N = 11; 86 private static final int PASSWORD_SCRYPT_R = 3; 87 private static final int PASSWORD_SCRYPT_P = 1; 88 private static final int PASSWORD_SALT_LENGTH = 16; 89 private static final int PASSWORD_TOKEN_LENGTH = 32; 90 private static final String TAG = "SyntheticPasswordManager"; 91 92 private static final byte[] PERSONALISATION_SECDISCARDABLE = "secdiscardable-transform".getBytes(); 93 private static final byte[] PERSONALIZATION_KEY_STORE_PASSWORD = "keystore-password".getBytes(); 94 private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes(); 95 private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes(); 96 private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes(); 97 private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes(); 98 private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes(); 99 100 static class AuthenticationResult { 101 public AuthenticationToken authToken; 102 public VerifyCredentialResponse gkResponse; 103 } 104 105 static class AuthenticationToken { 106 /* 107 * Here is the relationship between all three fields: 108 * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not. 109 * syntheticPassword = hash(P0 || P1) 110 * E0 = P0 encrypted under syntheticPassword, stored on disk. 111 */ 112 private @Nullable byte[] E0; 113 private @Nullable byte[] P1; 114 private @NonNull String syntheticPassword; 115 deriveKeyStorePassword()116 public String deriveKeyStorePassword() { 117 return bytesToHex(SyntheticPasswordCrypto.personalisedHash( 118 PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes())); 119 } 120 deriveGkPassword()121 public byte[] deriveGkPassword() { 122 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH, 123 syntheticPassword.getBytes()); 124 } 125 deriveDiskEncryptionKey()126 public byte[] deriveDiskEncryptionKey() { 127 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY, 128 syntheticPassword.getBytes()); 129 } 130 initialize(byte[] P0, byte[] P1)131 private void initialize(byte[] P0, byte[] P1) { 132 this.P1 = P1; 133 this.syntheticPassword = String.valueOf(HexEncoding.encode( 134 SyntheticPasswordCrypto.personalisedHash( 135 PERSONALIZATION_SP_SPLIT, P0, P1))); 136 this.E0 = SyntheticPasswordCrypto.encrypt(this.syntheticPassword.getBytes(), 137 PERSONALIZATION_E0, P0); 138 } 139 recreate(byte[] secret)140 public void recreate(byte[] secret) { 141 initialize(secret, this.P1); 142 } 143 create()144 protected static AuthenticationToken create() { 145 AuthenticationToken result = new AuthenticationToken(); 146 result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH), 147 secureRandom(SYNTHETIC_PASSWORD_LENGTH)); 148 return result; 149 } 150 computeP0()151 public byte[] computeP0() { 152 if (E0 == null) { 153 return null; 154 } 155 return SyntheticPasswordCrypto.decrypt(syntheticPassword.getBytes(), PERSONALIZATION_E0, 156 E0); 157 } 158 } 159 160 static class PasswordData { 161 byte scryptN; 162 byte scryptR; 163 byte scryptP; 164 public int passwordType; 165 byte[] salt; 166 public byte[] passwordHandle; 167 create(int passwordType)168 public static PasswordData create(int passwordType) { 169 PasswordData result = new PasswordData(); 170 result.scryptN = PASSWORD_SCRYPT_N; 171 result.scryptR = PASSWORD_SCRYPT_R; 172 result.scryptP = PASSWORD_SCRYPT_P; 173 result.passwordType = passwordType; 174 result.salt = secureRandom(PASSWORD_SALT_LENGTH); 175 return result; 176 } 177 fromBytes(byte[] data)178 public static PasswordData fromBytes(byte[] data) { 179 PasswordData result = new PasswordData(); 180 ByteBuffer buffer = ByteBuffer.allocate(data.length); 181 buffer.put(data, 0, data.length); 182 buffer.flip(); 183 result.passwordType = buffer.getInt(); 184 result.scryptN = buffer.get(); 185 result.scryptR = buffer.get(); 186 result.scryptP = buffer.get(); 187 int saltLen = buffer.getInt(); 188 result.salt = new byte[saltLen]; 189 buffer.get(result.salt); 190 int handleLen = buffer.getInt(); 191 result.passwordHandle = new byte[handleLen]; 192 buffer.get(result.passwordHandle); 193 return result; 194 } 195 toBytes()196 public byte[] toBytes() { 197 ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES 198 + Integer.BYTES + salt.length + Integer.BYTES + passwordHandle.length); 199 buffer.putInt(passwordType); 200 buffer.put(scryptN); 201 buffer.put(scryptR); 202 buffer.put(scryptP); 203 buffer.putInt(salt.length); 204 buffer.put(salt); 205 buffer.putInt(passwordHandle.length); 206 buffer.put(passwordHandle); 207 return buffer.array(); 208 } 209 } 210 211 private LockSettingsStorage mStorage; 212 SyntheticPasswordManager(LockSettingsStorage storage)213 public SyntheticPasswordManager(LockSettingsStorage storage) { 214 mStorage = storage; 215 } 216 217 getCredentialType(long handle, int userId)218 public int getCredentialType(long handle, int userId) { 219 byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId); 220 if (passwordData == null) { 221 Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId); 222 return LockPatternUtils.CREDENTIAL_TYPE_NONE; 223 } 224 return PasswordData.fromBytes(passwordData).passwordType; 225 } 226 227 /** 228 * Initializing a new Authentication token, possibly from an existing credential and hash. 229 * 230 * The authentication token would bear a randomly-generated synthetic password. 231 * 232 * This method has the side effect of rebinding the SID of the given user to the 233 * newly-generated SP. 234 * 235 * If the existing credential hash is non-null, the existing SID mill be migrated so 236 * the synthetic password in the authentication token will produce the same SID 237 * (the corresponding synthetic password handle is persisted by SyntheticPasswordManager 238 * in a per-user data storage.) 239 * 240 * If the existing credential hash is null, it means the given user should have no SID so 241 * SyntheticPasswordManager will nuke any SP handle previously persisted. In this case, 242 * the supplied credential parameter is also ignored. 243 * 244 * Also saves the escrow information necessary to re-generate the synthetic password under 245 * an escrow scheme. This information can be removed with {@link #destroyEscrowData} if 246 * password escrow should be disabled completely on the given user. 247 * 248 */ newSyntheticPasswordAndSid(IGateKeeperService gatekeeper, byte[] hash, String credential, int userId)249 public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper, 250 byte[] hash, String credential, int userId) throws RemoteException { 251 AuthenticationToken result = AuthenticationToken.create(); 252 GateKeeperResponse response; 253 if (hash != null) { 254 response = gatekeeper.enroll(userId, hash, credential.getBytes(), 255 result.deriveGkPassword()); 256 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { 257 Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId); 258 clearSidForUser(userId); 259 } else { 260 saveSyntheticPasswordHandle(response.getPayload(), userId); 261 } 262 } else { 263 clearSidForUser(userId); 264 } 265 saveEscrowData(result, userId); 266 return result; 267 } 268 269 /** 270 * Enroll a new password handle and SID for the given synthetic password and persist it on disk. 271 * Used when adding password to previously-unsecured devices. 272 */ newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken, int userId)273 public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken, 274 int userId) throws RemoteException { 275 GateKeeperResponse response = gatekeeper.enroll(userId, null, null, 276 authToken.deriveGkPassword()); 277 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { 278 Log.e(TAG, "Fail to create new SID for user " + userId); 279 return; 280 } 281 saveSyntheticPasswordHandle(response.getPayload(), userId); 282 } 283 284 // Nuke the SP handle (and as a result, its SID) for the given user. clearSidForUser(int userId)285 public void clearSidForUser(int userId) { 286 destroyState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId); 287 } 288 hasSidForUser(int userId)289 public boolean hasSidForUser(int userId) { 290 return hasState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId); 291 } 292 293 // if null, it means there is no SID associated with the user 294 // This can happen if the user is migrated to SP but currently 295 // do not have a lockscreen password. loadSyntheticPasswordHandle(int userId)296 private byte[] loadSyntheticPasswordHandle(int userId) { 297 return loadState(SP_HANDLE_NAME, DEFAULT_HANDLE, userId); 298 } 299 saveSyntheticPasswordHandle(byte[] spHandle, int userId)300 private void saveSyntheticPasswordHandle(byte[] spHandle, int userId) { 301 saveState(SP_HANDLE_NAME, spHandle, DEFAULT_HANDLE, userId); 302 } 303 loadEscrowData(AuthenticationToken authToken, int userId)304 private boolean loadEscrowData(AuthenticationToken authToken, int userId) { 305 authToken.E0 = loadState(SP_E0_NAME, DEFAULT_HANDLE, userId); 306 authToken.P1 = loadState(SP_P1_NAME, DEFAULT_HANDLE, userId); 307 return authToken.E0 != null && authToken.P1 != null; 308 } 309 saveEscrowData(AuthenticationToken authToken, int userId)310 private void saveEscrowData(AuthenticationToken authToken, int userId) { 311 saveState(SP_E0_NAME, authToken.E0, DEFAULT_HANDLE, userId); 312 saveState(SP_P1_NAME, authToken.P1, DEFAULT_HANDLE, userId); 313 } 314 hasEscrowData(int userId)315 public boolean hasEscrowData(int userId) { 316 return hasState(SP_E0_NAME, DEFAULT_HANDLE, userId) 317 && hasState(SP_P1_NAME, DEFAULT_HANDLE, userId); 318 } 319 destroyEscrowData(int userId)320 public void destroyEscrowData(int userId) { 321 destroyState(SP_E0_NAME, DEFAULT_HANDLE, userId); 322 destroyState(SP_P1_NAME, DEFAULT_HANDLE, userId); 323 } 324 325 /** 326 * Create a new password based SP blob based on the supplied authentication token, such that 327 * a future successful authentication with unwrapPasswordBasedSyntheticPassword() would result 328 * in the same authentication token. 329 * 330 * This method only creates SP blob wrapping around the given synthetic password and does not 331 * handle logic around SID or SP handle. The caller should separately ensure that the user's SID 332 * is consistent with the device state by calling other APIs in this class. 333 * 334 * @see #newSidForUser 335 * @see #clearSidForUser 336 */ createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, String credential, int credentialType, AuthenticationToken authToken, int userId)337 public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, 338 String credential, int credentialType, AuthenticationToken authToken, int userId) 339 throws RemoteException { 340 if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) { 341 credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE; 342 credential = DEFAULT_PASSWORD; 343 } 344 345 long handle = generateHandle(); 346 PasswordData pwd = PasswordData.create(credentialType); 347 byte[] pwdToken = computePasswordToken(credential, pwd); 348 349 // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them 350 // to prevent them from accumulating and causing problems. 351 gatekeeper.clearSecureUserId(fakeUid(userId)); 352 GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null, 353 passwordTokenToGkInput(pwdToken)); 354 if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { 355 Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId); 356 return DEFAULT_HANDLE; 357 } 358 pwd.passwordHandle = response.getPayload(); 359 long sid = sidFromPasswordHandle(pwd.passwordHandle); 360 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId); 361 362 byte[] applicationId = transformUnderSecdiscardable(pwdToken, 363 createSecdiscardable(handle, userId)); 364 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken, 365 applicationId, sid, userId); 366 return handle; 367 } 368 369 private ArrayMap<Integer, ArrayMap<Long, byte[]>> tokenMap = new ArrayMap<>(); 370 createTokenBasedSyntheticPassword(byte[] token, int userId)371 public long createTokenBasedSyntheticPassword(byte[] token, int userId) { 372 long handle = generateHandle(); 373 byte[] applicationId = transformUnderSecdiscardable(token, 374 createSecdiscardable(handle, userId)); 375 if (!tokenMap.containsKey(userId)) { 376 tokenMap.put(userId, new ArrayMap<>()); 377 } 378 tokenMap.get(userId).put(handle, applicationId); 379 return handle; 380 } 381 getPendingTokensForUser(int userId)382 public Set<Long> getPendingTokensForUser(int userId) { 383 if (!tokenMap.containsKey(userId)) { 384 return Collections.emptySet(); 385 } 386 return tokenMap.get(userId).keySet(); 387 } 388 removePendingToken(long handle, int userId)389 public boolean removePendingToken(long handle, int userId) { 390 if (!tokenMap.containsKey(userId)) { 391 return false; 392 } 393 return tokenMap.get(userId).remove(handle) != null; 394 } 395 activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken, int userId)396 public boolean activateTokenBasedSyntheticPassword(long handle, AuthenticationToken authToken, 397 int userId) { 398 if (!tokenMap.containsKey(userId)) { 399 return false; 400 } 401 byte[] applicationId = tokenMap.get(userId).get(handle); 402 if (applicationId == null) { 403 return false; 404 } 405 if (!loadEscrowData(authToken, userId)) { 406 Log.w(TAG, "User is not escrowable"); 407 return false; 408 } 409 createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, authToken, 410 applicationId, 0L, userId); 411 tokenMap.get(userId).remove(handle); 412 return true; 413 } 414 createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken, byte[] applicationId, long sid, int userId)415 private void createSyntheticPasswordBlob(long handle, byte type, AuthenticationToken authToken, 416 byte[] applicationId, long sid, int userId) { 417 final byte[] secret; 418 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) { 419 secret = authToken.computeP0(); 420 } else { 421 secret = authToken.syntheticPassword.getBytes(); 422 } 423 byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid); 424 byte[] blob = new byte[content.length + 1 + 1]; 425 blob[0] = SYNTHETIC_PASSWORD_VERSION; 426 blob[1] = type; 427 System.arraycopy(content, 0, blob, 2, content.length); 428 saveState(SP_BLOB_NAME, blob, handle, userId); 429 } 430 431 /** 432 * Decrypt a synthetic password by supplying the user credential and corresponding password 433 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper 434 * verification to referesh the SID & Auth token maintained by the system. 435 */ unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, long handle, String credential, int userId)436 public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, 437 long handle, String credential, int userId) throws RemoteException { 438 if (credential == null) { 439 credential = DEFAULT_PASSWORD; 440 } 441 AuthenticationResult result = new AuthenticationResult(); 442 PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId)); 443 byte[] pwdToken = computePasswordToken(credential, pwd); 444 byte[] gkPwdToken = passwordTokenToGkInput(pwdToken); 445 446 GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L, 447 pwd.passwordHandle, gkPwdToken); 448 int responseCode = response.getResponseCode(); 449 if (responseCode == GateKeeperResponse.RESPONSE_OK) { 450 result.gkResponse = VerifyCredentialResponse.OK; 451 if (response.getShouldReEnroll()) { 452 GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId), 453 pwd.passwordHandle, gkPwdToken, gkPwdToken); 454 if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) { 455 pwd.passwordHandle = reenrollResponse.getPayload(); 456 saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId); 457 } else { 458 Log.w(TAG, "Fail to re-enroll user password for user " + userId); 459 // continue the flow anyway 460 } 461 } 462 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) { 463 result.gkResponse = new VerifyCredentialResponse(response.getTimeout()); 464 return result; 465 } else { 466 result.gkResponse = VerifyCredentialResponse.ERROR; 467 return result; 468 } 469 470 471 byte[] applicationId = transformUnderSecdiscardable(pwdToken, 472 loadSecdiscardable(handle, userId)); 473 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, 474 applicationId, userId); 475 476 // Perform verifyChallenge to refresh auth tokens for GK if user password exists. 477 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId); 478 return result; 479 } 480 481 /** 482 * Decrypt a synthetic password by supplying an escrow token and corresponding token 483 * blob handle generated previously. If the decryption is successful, initiate a GateKeeper 484 * verification to referesh the SID & Auth token maintained by the system. 485 */ unwrapTokenBasedSyntheticPassword( IGateKeeperService gatekeeper, long handle, byte[] token, int userId)486 public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword( 487 IGateKeeperService gatekeeper, long handle, byte[] token, int userId) 488 throws RemoteException { 489 AuthenticationResult result = new AuthenticationResult(); 490 byte[] applicationId = transformUnderSecdiscardable(token, 491 loadSecdiscardable(handle, userId)); 492 result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED, 493 applicationId, userId); 494 if (result.authToken != null) { 495 result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId); 496 if (result.gkResponse == null) { 497 // The user currently has no password. return OK with null payload so null 498 // is propagated to unlockUser() 499 result.gkResponse = VerifyCredentialResponse.OK; 500 } 501 } else { 502 result.gkResponse = VerifyCredentialResponse.ERROR; 503 } 504 return result; 505 } 506 unwrapSyntheticPasswordBlob(long handle, byte type, byte[] applicationId, int userId)507 private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type, 508 byte[] applicationId, int userId) { 509 byte[] blob = loadState(SP_BLOB_NAME, handle, userId); 510 if (blob == null) { 511 return null; 512 } 513 if (blob[0] != SYNTHETIC_PASSWORD_VERSION) { 514 throw new RuntimeException("Unknown blob version"); 515 } 516 if (blob[1] != type) { 517 throw new RuntimeException("Invalid blob type"); 518 } 519 byte[] secret = decryptSPBlob(getHandleName(handle), 520 Arrays.copyOfRange(blob, 2, blob.length), applicationId); 521 if (secret == null) { 522 Log.e(TAG, "Fail to decrypt SP for user " + userId); 523 return null; 524 } 525 AuthenticationToken result = new AuthenticationToken(); 526 if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) { 527 if (!loadEscrowData(result, userId)) { 528 Log.e(TAG, "User is not escrowable: " + userId); 529 return null; 530 } 531 result.recreate(secret); 532 } else { 533 result.syntheticPassword = new String(secret); 534 } 535 return result; 536 } 537 538 /** 539 * performs GK verifyChallenge and returns auth token, re-enrolling SP password handle 540 * if required. 541 * 542 * Normally performing verifyChallenge with an AuthenticationToken should always return 543 * RESPONSE_OK, since user authentication failures are detected earlier when trying to 544 * decrypt SP. 545 */ verifyChallenge(IGateKeeperService gatekeeper, @NonNull AuthenticationToken auth, long challenge, int userId)546 public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper, 547 @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException { 548 byte[] spHandle = loadSyntheticPasswordHandle(userId); 549 if (spHandle == null) { 550 // There is no password handle associated with the given user, i.e. the user is not 551 // secured by lockscreen and has no SID, so just return here; 552 return null; 553 } 554 VerifyCredentialResponse result; 555 GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge, 556 spHandle, auth.deriveGkPassword()); 557 int responseCode = response.getResponseCode(); 558 if (responseCode == GateKeeperResponse.RESPONSE_OK) { 559 result = new VerifyCredentialResponse(response.getPayload()); 560 if (response.getShouldReEnroll()) { 561 response = gatekeeper.enroll(userId, spHandle, 562 spHandle, auth.deriveGkPassword()); 563 if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) { 564 spHandle = response.getPayload(); 565 saveSyntheticPasswordHandle(spHandle, userId); 566 // Call self again to re-verify with updated handle 567 return verifyChallenge(gatekeeper, auth, challenge, userId); 568 } else { 569 Log.w(TAG, "Fail to re-enroll SP handle for user " + userId); 570 // Fall through, return existing handle 571 } 572 } 573 } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) { 574 result = new VerifyCredentialResponse(response.getTimeout()); 575 } else { 576 result = VerifyCredentialResponse.ERROR; 577 } 578 return result; 579 } 580 existsHandle(long handle, int userId)581 public boolean existsHandle(long handle, int userId) { 582 return hasState(SP_BLOB_NAME, handle, userId); 583 } 584 destroyTokenBasedSyntheticPassword(long handle, int userId)585 public void destroyTokenBasedSyntheticPassword(long handle, int userId) { 586 destroySyntheticPassword(handle, userId); 587 destroyState(SECDISCARDABLE_NAME, handle, userId); 588 } 589 destroyPasswordBasedSyntheticPassword(long handle, int userId)590 public void destroyPasswordBasedSyntheticPassword(long handle, int userId) { 591 destroySyntheticPassword(handle, userId); 592 destroyState(SECDISCARDABLE_NAME, handle, userId); 593 destroyState(PASSWORD_DATA_NAME, handle, userId); 594 } 595 destroySyntheticPassword(long handle, int userId)596 private void destroySyntheticPassword(long handle, int userId) { 597 destroyState(SP_BLOB_NAME, handle, userId); 598 destroySPBlobKey(getHandleName(handle)); 599 } 600 transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable)601 private byte[] transformUnderSecdiscardable(byte[] data, byte[] rawSecdiscardable) { 602 byte[] secdiscardable = SyntheticPasswordCrypto.personalisedHash( 603 PERSONALISATION_SECDISCARDABLE, rawSecdiscardable); 604 byte[] result = new byte[data.length + secdiscardable.length]; 605 System.arraycopy(data, 0, result, 0, data.length); 606 System.arraycopy(secdiscardable, 0, result, data.length, secdiscardable.length); 607 return result; 608 } 609 createSecdiscardable(long handle, int userId)610 private byte[] createSecdiscardable(long handle, int userId) { 611 byte[] data = secureRandom(SECDISCARDABLE_LENGTH); 612 saveState(SECDISCARDABLE_NAME, data, handle, userId); 613 return data; 614 } 615 loadSecdiscardable(long handle, int userId)616 private byte[] loadSecdiscardable(long handle, int userId) { 617 return loadState(SECDISCARDABLE_NAME, handle, userId); 618 } 619 hasState(String stateName, long handle, int userId)620 private boolean hasState(String stateName, long handle, int userId) { 621 return !ArrayUtils.isEmpty(loadState(stateName, handle, userId)); 622 } 623 loadState(String stateName, long handle, int userId)624 private byte[] loadState(String stateName, long handle, int userId) { 625 return mStorage.readSyntheticPasswordState(userId, handle, stateName); 626 } 627 saveState(String stateName, byte[] data, long handle, int userId)628 private void saveState(String stateName, byte[] data, long handle, int userId) { 629 mStorage.writeSyntheticPasswordState(userId, handle, stateName, data); 630 } 631 destroyState(String stateName, long handle, int userId)632 private void destroyState(String stateName, long handle, int userId) { 633 mStorage.deleteSyntheticPasswordState(userId, handle, stateName); 634 } 635 decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId)636 protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) { 637 return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, applicationId); 638 } 639 createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid)640 protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) { 641 return SyntheticPasswordCrypto.createBlob(blobKeyName, data, applicationId, sid); 642 } 643 destroySPBlobKey(String keyAlias)644 protected void destroySPBlobKey(String keyAlias) { 645 SyntheticPasswordCrypto.destroyBlobKey(keyAlias); 646 } 647 generateHandle()648 public static long generateHandle() { 649 SecureRandom rng = new SecureRandom(); 650 long result; 651 do { 652 result = rng.nextLong(); 653 } while (result == DEFAULT_HANDLE); 654 return result; 655 } 656 fakeUid(int uid)657 private int fakeUid(int uid) { 658 return 100000 + uid; 659 } 660 secureRandom(int length)661 protected static byte[] secureRandom(int length) { 662 try { 663 return SecureRandom.getInstance("SHA1PRNG").generateSeed(length); 664 } catch (NoSuchAlgorithmException e) { 665 e.printStackTrace(); 666 return null; 667 } 668 } 669 getHandleName(long handle)670 private String getHandleName(long handle) { 671 return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle); 672 } 673 computePasswordToken(String password, PasswordData data)674 private byte[] computePasswordToken(String password, PasswordData data) { 675 return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP, 676 PASSWORD_TOKEN_LENGTH); 677 } 678 passwordTokenToGkInput(byte[] token)679 private byte[] passwordTokenToGkInput(byte[] token) { 680 return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_USER_GK_AUTH, token); 681 } 682 sidFromPasswordHandle(byte[] handle)683 protected long sidFromPasswordHandle(byte[] handle) { 684 return nativeSidFromPasswordHandle(handle); 685 } 686 scrypt(String password, byte[] salt, int N, int r, int p, int outLen)687 protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) { 688 return nativeScrypt(password.getBytes(), salt, N, r, p, outLen); 689 } 690 nativeSidFromPasswordHandle(byte[] handle)691 native long nativeSidFromPasswordHandle(byte[] handle); nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen)692 native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen); 693 694 final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); bytesToHex(byte[] bytes)695 public static String bytesToHex(byte[] bytes) { 696 if (bytes == null) { 697 return "null"; 698 } 699 char[] hexChars = new char[bytes.length * 2]; 700 for ( int j = 0; j < bytes.length; j++ ) { 701 int v = bytes[j] & 0xFF; 702 hexChars[j * 2] = hexArray[v >>> 4]; 703 hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 704 } 705 return new String(hexChars); 706 } 707 } 708