Update synthetic password terminology to match new design doc

Update terminology to eliminate ambiguity and to match
http://go/android-locksettings-design :

- The class that represents a synthetic password is now called
  SyntheticPassword instead of AuthenticationToken.  This eliminates an
  inconsistency and avoids ambiguity with the other types of
  authentication tokens (HardwareAuthTokens and escrow tokens).

- "LSKF" is now used in preference to "password", which could be
  confused with LSKFs of type password and with the many other types of
  password (synthetic, Keystore, Gatekeeper).  "Password" is still used
  in places like "password data", "password metrics", and "password
  history"; renaming those in the design doc and code is left for later.

- The things that protect the SP are now called "SP protectors", or just
  "protectors" when SP is clear from context.  Previously these were
  called "synthetic passwords" (ambiguous with the SP) or "SP blobs"
  (ambiguous with the spblob file, which is just part of a protector).

- The 64-bit integers that identify protectors are now called "protector
  IDs" instead of "synthetic password handles".  This avoids ambiguity
  with the SP's Gatekeeper password handle (which in the code is just
  called a "synthetic password handle"; a later CL might clarify that),
  and it clarifies that the identified items are SP protectors, not SPs.

- The secret that each protector uses to protect the SP is now called
  the "protector secret" instead of the application ID.  This avoids
  ambiguity with the Keystore application ID, which isn't being used and
  is a less intuitive name.

No behavior changes intended, except for some changed log messages.

Test: atest com.android.server.locksettings
Test: Basic manual test of locksettings core functionality: upgraded a
      device that has a pattern set, without wiping userdata; unlocked;
      changed to PIN; rebooted; unlocked; changed to swipe; rebooted;
      changed to password; rebooted; and unlocked.
Change-Id: I564a738119a47a31b4822d26c6405249f8ce1c06
This commit is contained in:
Eric Biggers
2022-07-15 00:23:41 +00:00
parent 600bc07425
commit c37987fe0d
8 changed files with 569 additions and 558 deletions

View File

@@ -170,7 +170,7 @@ public class LockPatternUtils {
public static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
public static final String SYNTHETIC_PASSWORD_KEY_PREFIX = "synthetic_password_";
public static final String SYNTHETIC_PASSWORD_HANDLE_KEY = "sp-handle";
public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle";
public static final String PASSWORD_HISTORY_DELIMITER = ",";
@UnsupportedAppUsage

View File

@@ -40,8 +40,8 @@ import java.util.List;
import java.util.Objects;
/**
* A class representing a lockscreen credential. It can be either an empty password, a pattern
* or a password (or PIN).
* A class representing a lockscreen credential, also called a Lock Screen Knowledge Factor (LSKF).
* It can be a PIN, pattern, password, or none (a.k.a. empty).
*
* <p> As required by some security certification, the framework tries its best to
* remove copies of the lockscreen credential bytes from memory. In this regard, this class
@@ -52,10 +52,10 @@ import java.util.Objects;
* // Process the credential in some way
* }
* </pre>
* With this construct, we can guarantee that there will be no copies of the password left in
* memory when the credential goes out of scope. This should help mitigate certain class of
* attacks where the attcker gains read-only access to full device memory (cold boot attack,
* unsecured software/hardware memory dumping interfaces such as JTAG).
* With this construct, we can guarantee that there will be no copies of the credential left in
* memory when the object goes out of scope. This should help mitigate certain class of attacks
* where the attacker gains read-only access to full device memory (cold boot attack, unsecured
* software/hardware memory dumping interfaces such as JTAG).
*/
public class LockscreenCredential implements Parcelable, AutoCloseable {

View File

@@ -33,10 +33,10 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSW
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.CURRENT_LSKF_BASED_PROTECTOR_ID_KEY;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
import static com.android.internal.widget.LockPatternUtils.PROFILE_KEY_NAME_DECRYPT;
import static com.android.internal.widget.LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
@@ -139,7 +139,7 @@ import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.locksettings.LockSettingsStorage.PersistentData;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
import com.android.server.locksettings.SyntheticPasswordManager.SyntheticPassword;
import com.android.server.locksettings.SyntheticPasswordManager.TokenType;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.pm.UserManagerInternal;
@@ -199,8 +199,8 @@ public class LockSettingsService extends ILockSettings.Stub {
private static final int PROFILE_KEY_IV_SIZE = 12;
private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
private static final String PREV_SYNTHETIC_PASSWORD_HANDLE_KEY = "prev-sp-handle";
private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts";
private static final String PREV_LSKF_BASED_PROTECTOR_ID_KEY = "prev-sp-handle";
private static final String LSKF_LAST_CHANGED_TIME_KEY = "sp-handle-ts";
private static final String USER_SERIAL_NUMBER_KEY = "serial-number";
// Duration that LockSettingsService will store the gatekeeper password for. This allows
@@ -787,28 +787,28 @@ public class LockSettingsService extends ILockSettings.Stub {
// credential and still needs to be passed to the HAL once that credential is
// removed.
if (mUserManager.getUserInfo(userId).isPrimary() && !isUserSecure(userId)) {
tryDeriveAuthTokenForUnsecuredPrimaryUser(userId);
tryDeriveVendorAuthSecretForUnsecuredPrimaryUser(userId);
}
}
});
}
private void tryDeriveAuthTokenForUnsecuredPrimaryUser(@UserIdInt int userId) {
private void tryDeriveVendorAuthSecretForUnsecuredPrimaryUser(@UserIdInt int userId) {
synchronized (mSpManager) {
// Make sure the user has a synthetic password to derive
// If there is no SP, then there is no vendor auth secret.
if (!isSyntheticPasswordBasedCredentialLocked(userId)) {
return;
}
final long handle = getSyntheticPasswordHandleLocked(userId);
final long protectorId = getCurrentLskfBasedProtectorId(userId);
AuthenticationResult result =
mSpManager.unwrapPasswordBasedSyntheticPassword(getGateKeeperService(),
handle, LockscreenCredential.createNone(), userId, null);
if (result.authToken != null) {
Slog.i(TAG, "Retrieved auth token for user " + userId);
onAuthTokenKnownForUser(userId, result.authToken);
mSpManager.unlockLskfBasedProtector(getGateKeeperService(), protectorId,
LockscreenCredential.createNone(), userId, null);
if (result.syntheticPassword != null) {
Slog.i(TAG, "Unwrapped SP for unsecured primary user " + userId);
onSyntheticPasswordKnown(userId, result.syntheticPassword);
} else {
Slog.e(TAG, "Auth token not available for user " + userId);
Slog.e(TAG, "Failed to unwrap SP for unsecured primary user " + userId);
}
}
}
@@ -912,7 +912,7 @@ public class LockSettingsService extends ILockSettings.Stub {
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userInfo.id);
mSpManager.migrateFrpPasswordLocked(
getSyntheticPasswordHandleLocked(userInfo.id),
getCurrentLskfBasedProtectorId(userInfo.id),
userInfo,
redactActualQualityToMostLenientEquivalentQuality(actualQuality));
}
@@ -1168,8 +1168,8 @@ public class LockSettingsService extends ILockSettings.Stub {
}
synchronized (mSpManager) {
if (isSyntheticPasswordBasedCredentialLocked(userId)) {
final long handle = getSyntheticPasswordHandleLocked(userId);
int rawType = mSpManager.getCredentialType(handle, userId);
final long protectorId = getCurrentLskfBasedProtectorId(userId);
int rawType = mSpManager.getCredentialType(protectorId, userId);
if (rawType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) {
return rawType;
}
@@ -1604,13 +1604,13 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.e(TAG, "Failed to decrypt child profile key", e);
}
}
final long origHandle = getSyntheticPasswordHandleLocked(userId);
AuthenticationResult authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
getGateKeeperService(), origHandle, savedCredential, userId, null);
final long oldProtectorId = getCurrentLskfBasedProtectorId(userId);
AuthenticationResult authResult = mSpManager.unlockLskfBasedProtector(
getGateKeeperService(), oldProtectorId, savedCredential, userId, null);
VerifyCredentialResponse response = authResult.gkResponse;
AuthenticationToken auth = authResult.authToken;
SyntheticPassword sp = authResult.syntheticPassword;
if (auth == null) {
if (sp == null) {
if (response == null
|| response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) {
Slog.w(TAG, "Failed to enroll: incorrect credential.");
@@ -1624,9 +1624,9 @@ public class LockSettingsService extends ILockSettings.Stub {
throw new IllegalStateException("password change failed");
}
onAuthTokenKnownForUser(userId, auth);
setLockCredentialWithAuthTokenLocked(credential, auth, userId);
mSpManager.destroyPasswordBasedSyntheticPassword(origHandle, userId);
onSyntheticPasswordKnown(userId, sp);
setLockCredentialWithSpLocked(credential, sp, userId);
mSpManager.destroyLskfBasedProtector(oldProtectorId, userId);
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
return true;
}
@@ -1832,9 +1832,9 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.w(TAG, "Escrow token is disabled on the current user");
return false;
}
AuthenticationResult authResult = mSpManager.unwrapWeakTokenBasedSyntheticPassword(
AuthenticationResult authResult = mSpManager.unlockWeakTokenBasedProtector(
getGateKeeperService(), handle, token, userId);
if (authResult.authToken == null) {
if (authResult.syntheticPassword == null) {
Slog.w(TAG, "Invalid escrow token supplied");
return false;
}
@@ -1929,7 +1929,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
/** Unlock disk encryption */
/** Unlock file-based encryption */
private void unlockUserKey(int userId, byte[] secret) {
final UserInfo userInfo = mUserManager.getUserInfo(userId);
try {
@@ -2112,20 +2112,20 @@ public class LockSettingsService extends ILockSettings.Stub {
progressCallback);
}
long handle = getSyntheticPasswordHandleLocked(userId);
authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
getGateKeeperService(), handle, credential, userId, progressCallback);
long protectorId = getCurrentLskfBasedProtectorId(userId);
authResult = mSpManager.unlockLskfBasedProtector(
getGateKeeperService(), protectorId, credential, userId, progressCallback);
response = authResult.gkResponse;
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
// credential has matched
mBiometricDeferredQueue.addPendingLockoutResetForUser(userId,
authResult.authToken.deriveGkPassword());
authResult.syntheticPassword.deriveGkPassword());
// perform verifyChallenge with synthetic password which generates the real GK auth
// token and response for the current user
response = mSpManager.verifyChallenge(getGateKeeperService(), authResult.authToken,
0L /* challenge */, userId);
response = mSpManager.verifyChallenge(getGateKeeperService(),
authResult.syntheticPassword, 0L /* challenge */, userId);
if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
// This shouldn't really happen: the unwrapping of SP succeeds, but SP doesn't
// match the recorded GK password handle.
@@ -2135,11 +2135,11 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
onCredentialVerified(authResult.authToken,
onCredentialVerified(authResult.syntheticPassword,
PasswordMetrics.computeForCredential(credential), userId);
if ((flags & VERIFY_FLAG_REQUEST_GK_PW_HANDLE) != 0) {
final long gkHandle = storeGatekeeperPasswordTemporarily(
authResult.authToken.deriveGkPassword());
authResult.syntheticPassword.deriveGkPassword());
response = new VerifyCredentialResponse.Builder()
.setGatekeeperPasswordHandle(gkHandle)
.build();
@@ -2213,9 +2213,9 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
private PasswordMetrics loadPasswordMetrics(AuthenticationToken auth, int userHandle) {
private PasswordMetrics loadPasswordMetrics(SyntheticPassword sp, int userHandle) {
synchronized (mSpManager) {
return mSpManager.getPasswordMetrics(auth, getSyntheticPasswordHandleLocked(userHandle),
return mSpManager.getPasswordMetrics(sp, getCurrentLskfBasedProtectorId(userHandle),
userHandle);
}
}
@@ -2489,24 +2489,23 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) {
private void onSyntheticPasswordKnown(@UserIdInt int userId, SyntheticPassword sp) {
if (mInjector.isGsiRunning()) {
Slog.w(TAG, "Running in GSI; skipping calls to AuthSecret and RebootEscrow");
return;
}
mRebootEscrowManager.callToRebootEscrowIfNeeded(userId, auth.getVersion(),
auth.getSyntheticPassword());
mRebootEscrowManager.callToRebootEscrowIfNeeded(userId, sp.getVersion(),
sp.getSyntheticPassword());
callToAuthSecretIfNeeded(userId, auth);
callToAuthSecretIfNeeded(userId, sp);
}
private void callToAuthSecretIfNeeded(@UserIdInt int userId,
AuthenticationToken auth) {
private void callToAuthSecretIfNeeded(@UserIdInt int userId, SyntheticPassword sp) {
// Pass the primary user's auth secret to the HAL
if (mAuthSecretService != null && mUserManager.getUserInfo(userId).isPrimary()) {
try {
final byte[] rawSecret = auth.deriveVendorAuthSecret();
final byte[] rawSecret = sp.deriveVendorAuthSecret();
final ArrayList<Byte> secret = new ArrayList<>(rawSecret.length);
for (int i = 0; i < rawSecret.length; ++i) {
secret.add(rawSecret[i]);
@@ -2519,66 +2518,51 @@ public class LockSettingsService extends ILockSettings.Stub {
}
/**
* Precondition: vold and keystore unlocked.
* Creates the synthetic password (SP) for the given user and protects it with the user's LSKF.
* This is called just once in the lifetime of the user: the first time a nonempty LSKF is set,
* or when an escrow token is activated on a device with an empty LSKF.
*
* Create new synthetic password, set up synthetic password blob protected by the supplied
* user credential, and make the newly-created SP blob active. This is called just once in the
* lifetime of the user: the first time that a user credential is set (!credential.isNone()), or
* when an escrow token is activated on an unsecured device (credential.isNone()).
*
* The invariant under a synthetic password is:
* 1. If user credential exists, then both vold and keystore and protected with keys derived
* from the synthetic password.
* 2. If user credential does not exist, vold and keystore protection are cleared. This is to
* make it consistent with current behaviour. It also allows ActivityManager to call
* unlockUser() with empty secret.
* 3. Once a user is migrated to have synthetic password, its value will never change, no matter
* whether the user changes their lockscreen PIN or clear/reset it. When the user clears its
* lockscreen PIN, we still maintain the existing synthetic password in a password blob
* protected by a default PIN.
* 4. The user SID is linked with synthetic password, but its cleared/re-created when the user
* clears/re-creates their lockscreen PIN.
* Maintains the SP invariants described in {@link SyntheticPasswordManager}.
*/
@GuardedBy("mSpManager")
@VisibleForTesting
AuthenticationToken initializeSyntheticPasswordLocked(LockscreenCredential credential,
SyntheticPassword initializeSyntheticPasswordLocked(LockscreenCredential credential,
int userId) {
Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
Preconditions.checkState(
getSyntheticPasswordHandleLocked(userId) == SyntheticPasswordManager.DEFAULT_HANDLE,
Preconditions.checkState(getCurrentLskfBasedProtectorId(userId) ==
SyntheticPasswordManager.NULL_PROTECTOR_ID,
"Cannot reinitialize SP");
final AuthenticationToken auth = mSpManager.newSyntheticPassword(userId);
long handle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
credential, auth, userId);
final SyntheticPassword sp = mSpManager.newSyntheticPassword(userId);
long protectorId = mSpManager.createLskfBasedProtector(getGateKeeperService(), credential,
sp, userId);
if (!credential.isNone()) {
mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
mSpManager.verifyChallenge(getGateKeeperService(), auth, 0L, userId);
setUserKeyProtection(userId, auth.deriveDiskEncryptionKey());
setKeystorePassword(auth.deriveKeyStorePassword(), userId);
mSpManager.newSidForUser(getGateKeeperService(), sp, userId);
mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
setUserKeyProtection(userId, sp.deriveFileBasedEncryptionKey());
setKeystorePassword(sp.deriveKeyStorePassword(), userId);
} else {
clearUserKeyProtection(userId, null);
setKeystorePassword(null, userId);
gateKeeperClearSecureUserId(userId);
}
fixateNewestUserKeyAuth(userId);
setSyntheticPasswordHandleLocked(handle, userId);
onAuthTokenKnownForUser(userId, auth);
return auth;
setCurrentLskfBasedProtectorId(protectorId, userId);
onSyntheticPasswordKnown(userId, sp);
return sp;
}
@VisibleForTesting
long getSyntheticPasswordHandleLocked(int userId) {
return getLong(SYNTHETIC_PASSWORD_HANDLE_KEY,
SyntheticPasswordManager.DEFAULT_HANDLE, userId);
long getCurrentLskfBasedProtectorId(int userId) {
return getLong(CURRENT_LSKF_BASED_PROTECTOR_ID_KEY,
SyntheticPasswordManager.NULL_PROTECTOR_ID, userId);
}
private void setSyntheticPasswordHandleLocked(long handle, int userId) {
final long oldHandle = getSyntheticPasswordHandleLocked(userId);
setLong(SYNTHETIC_PASSWORD_HANDLE_KEY, handle, userId);
setLong(PREV_SYNTHETIC_PASSWORD_HANDLE_KEY, oldHandle, userId);
setLong(SYNTHETIC_PASSWORD_UPDATE_TIME_KEY, System.currentTimeMillis(), userId);
private void setCurrentLskfBasedProtectorId(long newProtectorId, int userId) {
final long oldProtectorId = getCurrentLskfBasedProtectorId(userId);
setLong(CURRENT_LSKF_BASED_PROTECTOR_ID_KEY, newProtectorId, userId);
setLong(PREV_LSKF_BASED_PROTECTOR_ID_KEY, oldProtectorId, userId);
setLong(LSKF_LAST_CHANGED_TIME_KEY, System.currentTimeMillis(), userId);
}
@VisibleForTesting
@@ -2593,8 +2577,8 @@ public class LockSettingsService extends ILockSettings.Stub {
final int type = mStorage.readPersistentDataBlock().type;
return type == PersistentData.TYPE_SP || type == PersistentData.TYPE_SP_WEAVER;
}
long handle = getSyntheticPasswordHandleLocked(userId);
return handle != SyntheticPasswordManager.DEFAULT_HANDLE;
long protectorId = getCurrentLskfBasedProtectorId(userId);
return protectorId != SyntheticPasswordManager.NULL_PROTECTOR_ID;
}
/**
@@ -2624,8 +2608,7 @@ public class LockSettingsService extends ILockSettings.Stub {
return handle;
}
private void onCredentialVerified(AuthenticationToken authToken, PasswordMetrics metrics,
int userId) {
private void onCredentialVerified(SyntheticPassword sp, PasswordMetrics metrics, int userId) {
if (metrics != null) {
synchronized (this) {
@@ -2635,21 +2618,21 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.wtf(TAG, "Null metrics after credential verification");
}
unlockKeystore(authToken.deriveKeyStorePassword(), userId);
unlockKeystore(sp.deriveKeyStorePassword(), userId);
{
final byte[] secret = authToken.deriveDiskEncryptionKey();
final byte[] secret = sp.deriveFileBasedEncryptionKey();
unlockUser(userId, secret);
Arrays.fill(secret, (byte) 0);
}
activateEscrowTokens(authToken, userId);
activateEscrowTokens(sp, userId);
if (isProfileWithSeparatedLock(userId)) {
setDeviceUnlockedForUser(userId);
}
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
onAuthTokenKnownForUser(userId, authToken);
onSyntheticPasswordKnown(userId, sp);
}
private void setDeviceUnlockedForUser(int userId) {
@@ -2658,68 +2641,58 @@ public class LockSettingsService extends ILockSettings.Stub {
}
/**
* Change the user's lockscreen password by creating a new SP blob and update the handle, based
* on an existing authentication token. Even though a new SP blob is created, the underlying
* synthetic password is never changed.
* Changes the user's LSKF by creating an LSKF-based protector that uses the new LSKF (which may
* be empty) and setting the new protector as the user's current LSKF-based protector. The old
* LSKF-based protector is not destroyed, and the SP itself is not changed.
*
* When clearing credential, we keep the SP unchanged, but clear its password handle so its
* SID is gone. We also clear password from (software-based) keystore and vold, which will be
* added back when new password is set in future.
* Also maintains the invariants described in {@link SyntheticPasswordManager} by
* setting/clearing the protection (by the SP) on the user's file-based encryption key and
* auth-bound Keystore keys when the LSKF is added/removed, respectively. If the new LSKF is
* nonempty, then the Gatekeeper auth token is also refreshed.
*/
@GuardedBy("mSpManager")
private long setLockCredentialWithAuthTokenLocked(LockscreenCredential credential,
AuthenticationToken auth, int userId) {
if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId);
private long setLockCredentialWithSpLocked(LockscreenCredential credential,
SyntheticPassword sp, int userId) {
if (DEBUG) Slog.d(TAG, "setLockCredentialWithSpLocked: user=" + userId);
final int savedCredentialType = getCredentialTypeInternal(userId);
long newHandle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
credential, auth, userId);
final long newProtectorId = mSpManager.createLskfBasedProtector(getGateKeeperService(),
credential, sp, userId);
final Map<Integer, LockscreenCredential> profilePasswords;
if (!credential.isNone()) {
// not needed by synchronizeUnifiedWorkChallengeForProfiles()
profilePasswords = null;
if (mSpManager.hasSidForUser(userId)) {
// We are changing password of a secured device, nothing more needed as
// createPasswordBasedSyntheticPassword has already taken care of maintaining
// the password handle and SID unchanged.
//refresh auth token
mSpManager.verifyChallenge(getGateKeeperService(), auth, 0L, userId);
mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
} else {
// A new password is set on a previously-unsecured device, we need to generate
// a new SID, and re-add keys to vold and keystore.
mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
mSpManager.verifyChallenge(getGateKeeperService(), auth, 0L, userId);
setUserKeyProtection(userId, auth.deriveDiskEncryptionKey());
mSpManager.newSidForUser(getGateKeeperService(), sp, userId);
mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
setUserKeyProtection(userId, sp.deriveFileBasedEncryptionKey());
fixateNewestUserKeyAuth(userId);
setKeystorePassword(auth.deriveKeyStorePassword(), userId);
setKeystorePassword(sp.deriveKeyStorePassword(), userId);
}
} else {
// Cache all profile password if they use unified work challenge. This will later be
// used to clear the profile's password in synchronizeUnifiedWorkChallengeForProfiles()
profilePasswords = getDecryptedPasswordsForAllTiedProfiles(userId);
// we are clearing password of a secured device, so need to nuke SID as well.
mSpManager.clearSidForUser(userId);
gateKeeperClearSecureUserId(userId);
// Clear key from vold so ActivityManager can just unlock the user with empty secret
// during boot. Vold storage needs to be unlocked before manipulation of the keys can
// succeed.
unlockUserKey(userId, auth.deriveDiskEncryptionKey());
clearUserKeyProtection(userId, auth.deriveDiskEncryptionKey());
unlockUserKey(userId, sp.deriveFileBasedEncryptionKey());
clearUserKeyProtection(userId, sp.deriveFileBasedEncryptionKey());
fixateNewestUserKeyAuth(userId);
unlockKeystore(auth.deriveKeyStorePassword(), userId);
unlockKeystore(sp.deriveKeyStorePassword(), userId);
setKeystorePassword(null, userId);
removeBiometricsForUser(userId);
}
setSyntheticPasswordHandleLocked(newHandle, userId);
setCurrentLskfBasedProtectorId(newProtectorId, userId);
LockPatternUtils.invalidateCredentialTypeCache();
synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords);
setUserPasswordMetrics(credential, userId);
mManagedProfilePasswordCache.removePassword(userId);
if (savedCredentialType != CREDENTIAL_TYPE_NONE) {
mSpManager.destroyAllWeakTokenBasedSyntheticPasswords(userId);
mSpManager.destroyAllWeakTokenBasedProtectors(userId);
}
if (profilePasswords != null) {
@@ -2728,7 +2701,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
return newHandle;
return newProtectorId;
}
private void removeBiometricsForUser(int userId) {
@@ -2825,14 +2798,14 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.w(TAG, "Synthetic password not enabled");
return null;
}
long handle = getSyntheticPasswordHandleLocked(userId);
AuthenticationResult auth = mSpManager.unwrapPasswordBasedSyntheticPassword(
getGateKeeperService(), handle, currentCredential, userId, null);
if (auth.authToken == null) {
long protectorId = getCurrentLskfBasedProtectorId(userId);
AuthenticationResult auth = mSpManager.unlockLskfBasedProtector(
getGateKeeperService(), protectorId, currentCredential, userId, null);
if (auth.syntheticPassword == null) {
Slog.w(TAG, "Current credential is incorrect");
return null;
}
return auth.authToken.derivePasswordHashFactor();
return auth.syntheticPassword.derivePasswordHashFactor();
}
} finally {
scheduleGc();
@@ -2843,46 +2816,47 @@ public class LockSettingsService extends ILockSettings.Stub {
@NonNull EscrowTokenStateChangeCallback callback) {
if (DEBUG) Slog.d(TAG, "addEscrowToken: user=" + userId + ", type=" + type);
synchronized (mSpManager) {
// Migrate to synthetic password based credentials if the user has no password,
// the token can then be activated immediately.
AuthenticationToken auth = null;
// If the user has no LSKF, then the token can be activated immediately, after creating
// the user's SP if it doesn't already exist. Otherwise, the token can't be activated
// until the SP is unlocked by another protector (normally the LSKF-based one).
SyntheticPassword sp = null;
if (!isUserSecure(userId)) {
long handle = getSyntheticPasswordHandleLocked(userId);
if (handle == SyntheticPasswordManager.DEFAULT_HANDLE) {
auth = initializeSyntheticPasswordLocked(LockscreenCredential.createNone(),
long protectorId = getCurrentLskfBasedProtectorId(userId);
if (protectorId == SyntheticPasswordManager.NULL_PROTECTOR_ID) {
sp = initializeSyntheticPasswordLocked(LockscreenCredential.createNone(),
userId);
} else {
auth = mSpManager.unwrapPasswordBasedSyntheticPassword(getGateKeeperService(),
handle, LockscreenCredential.createNone(), userId, null).authToken;
sp = mSpManager.unlockLskfBasedProtector(getGateKeeperService(), protectorId,
LockscreenCredential.createNone(), userId, null).syntheticPassword;
}
}
disableEscrowTokenOnNonManagedDevicesIfNeeded(userId);
if (!mSpManager.hasEscrowData(userId)) {
throw new SecurityException("Escrow token is disabled on the current user");
}
long handle = mSpManager.createTokenBasedSyntheticPassword(token, type, userId,
callback);
if (auth != null) {
mSpManager.activateTokenBasedSyntheticPassword(handle, auth, userId);
long handle = mSpManager.addPendingToken(token, type, userId, callback);
if (sp != null) {
// Activate the token immediately
mSpManager.createTokenBasedProtector(handle, sp, userId);
}
return handle;
}
}
private void activateEscrowTokens(AuthenticationToken auth, int userId) {
private void activateEscrowTokens(SyntheticPassword sp, int userId) {
if (DEBUG) Slog.d(TAG, "activateEscrowTokens: user=" + userId);
synchronized (mSpManager) {
disableEscrowTokenOnNonManagedDevicesIfNeeded(userId);
for (long handle : mSpManager.getPendingTokensForUser(userId)) {
Slog.i(TAG, String.format("activateEscrowTokens: %x %d ", handle, userId));
mSpManager.activateTokenBasedSyntheticPassword(handle, auth, userId);
mSpManager.createTokenBasedProtector(handle, sp, userId);
}
}
}
private boolean isEscrowTokenActive(long handle, int userId) {
synchronized (mSpManager) {
return mSpManager.existsHandle(handle, userId);
return mSpManager.protectorExists(handle, userId);
}
}
@@ -2896,15 +2870,15 @@ public class LockSettingsService extends ILockSettings.Stub {
private boolean removeEscrowToken(long handle, int userId) {
synchronized (mSpManager) {
if (handle == getSyntheticPasswordHandleLocked(userId)) {
Slog.w(TAG, "Cannot remove password handle");
if (handle == getCurrentLskfBasedProtectorId(userId)) {
Slog.w(TAG, "Escrow token handle equals LSKF-based protector ID");
return false;
}
if (mSpManager.removePendingToken(handle, userId)) {
return true;
}
if (mSpManager.existsHandle(handle, userId)) {
mSpManager.destroyTokenBasedSyntheticPassword(handle, userId);
if (mSpManager.protectorExists(handle, userId)) {
mSpManager.destroyTokenBasedProtector(handle, userId);
return true;
} else {
return false;
@@ -2946,23 +2920,23 @@ public class LockSettingsService extends ILockSettings.Stub {
private boolean setLockCredentialWithTokenInternalLocked(LockscreenCredential credential,
long tokenHandle, byte[] token, int userId) {
final AuthenticationResult result;
result = mSpManager.unwrapTokenBasedSyntheticPassword(getGateKeeperService(), tokenHandle,
token, userId);
if (result.authToken == null) {
result = mSpManager.unlockTokenBasedProtector(getGateKeeperService(), tokenHandle, token,
userId);
if (result.syntheticPassword == null) {
Slog.w(TAG, "Invalid escrow token supplied");
return false;
}
if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
// Most likely, an untrusted credential reset happened in the past which
// changed the synthetic password
Slog.e(TAG, "Obsolete token: synthetic password derived but it fails GK "
Slog.e(TAG, "Obsolete token: synthetic password decrypted but it fails GK "
+ "verification.");
return false;
}
onAuthTokenKnownForUser(userId, result.authToken);
long oldHandle = getSyntheticPasswordHandleLocked(userId);
setLockCredentialWithAuthTokenLocked(credential, result.authToken, userId);
mSpManager.destroyPasswordBasedSyntheticPassword(oldHandle, userId);
onSyntheticPasswordKnown(userId, result.syntheticPassword);
final long oldProtectorId = getCurrentLskfBasedProtectorId(userId);
setLockCredentialWithSpLocked(credential, result.syntheticPassword, userId);
mSpManager.destroyLskfBasedProtector(oldProtectorId, userId);
return true;
}
@@ -2973,16 +2947,16 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.w(TAG, "Escrow token is disabled on the current user");
return false;
}
authResult = mSpManager.unwrapTokenBasedSyntheticPassword(getGateKeeperService(),
tokenHandle, token, userId);
if (authResult.authToken == null) {
authResult = mSpManager.unlockTokenBasedProtector(getGateKeeperService(), tokenHandle,
token, userId);
if (authResult.syntheticPassword == null) {
Slog.w(TAG, "Invalid escrow token supplied");
return false;
}
}
onCredentialVerified(authResult.authToken,
loadPasswordMetrics(authResult.authToken, userId), userId);
onCredentialVerified(authResult.syntheticPassword,
loadPasswordMetrics(authResult.syntheticPassword, userId), userId);
return true;
}
@@ -3037,11 +3011,11 @@ public class LockSettingsService extends ILockSettings.Stub {
pw.println("User " + userId);
pw.increaseIndent();
synchronized (mSpManager) {
pw.println(String.format("SP Handle: %x",
getSyntheticPasswordHandleLocked(userId)));
pw.println(String.format("Last changed: %s (%x)",
timestampToString(getLong(SYNTHETIC_PASSWORD_UPDATE_TIME_KEY, 0, userId)),
getLong(PREV_SYNTHETIC_PASSWORD_HANDLE_KEY, 0, userId)));
pw.println(String.format("LSKF-based SP protector ID: %x",
getCurrentLskfBasedProtectorId(userId)));
pw.println(String.format("LSKF last changed: %s (previous protector: %x)",
timestampToString(getLong(LSKF_LAST_CHANGED_TIME_KEY, 0, userId)),
getLong(PREV_LSKF_BASED_PROTECTOR_ID_KEY, 0, userId)));
}
try {
pw.println(String.format("SID: %x",
@@ -3340,14 +3314,15 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
public void onRebootEscrowRestored(byte spVersion, byte[] syntheticPassword, int userId) {
SyntheticPasswordManager.AuthenticationToken
authToken = new SyntheticPasswordManager.AuthenticationToken(spVersion);
authToken.recreateDirectly(syntheticPassword);
public void onRebootEscrowRestored(byte spVersion, byte[] rawSyntheticPassword,
int userId) {
SyntheticPasswordManager.SyntheticPassword
sp = new SyntheticPasswordManager.SyntheticPassword(spVersion);
sp.recreateDirectly(rawSyntheticPassword);
synchronized (mSpManager) {
mSpManager.verifyChallenge(getGateKeeperService(), authToken, 0L, userId);
mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
}
onCredentialVerified(authToken, loadPasswordMetrics(authToken, userId), userId);
onCredentialVerified(sp, loadPasswordMetrics(sp, userId), userId);
}
}
}

View File

@@ -380,29 +380,30 @@ class LockSettingsStorage {
}
}
public void writeSyntheticPasswordState(int userId, long handle, String name, byte[] data) {
public void writeSyntheticPasswordState(int userId, long protectorId, String name,
byte[] data) {
ensureSyntheticPasswordDirectoryForUser(userId);
writeFile(getSyntheticPasswordStateFileForUser(userId, handle, name), data);
writeFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name), data);
}
public byte[] readSyntheticPasswordState(int userId, long handle, String name) {
return readFile(getSyntheticPasswordStateFileForUser(userId, handle, name));
public byte[] readSyntheticPasswordState(int userId, long protectorId, String name) {
return readFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name));
}
public void deleteSyntheticPasswordState(int userId, long handle, String name) {
deleteFile(getSyntheticPasswordStateFileForUser(userId, handle, name));
public void deleteSyntheticPasswordState(int userId, long protectorId, String name) {
deleteFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name));
}
public Map<Integer, List<Long>> listSyntheticPasswordHandlesForAllUsers(String stateName) {
public Map<Integer, List<Long>> listSyntheticPasswordProtectorsForAllUsers(String stateName) {
Map<Integer, List<Long>> result = new ArrayMap<>();
final UserManager um = UserManager.get(mContext);
for (UserInfo user : um.getUsers()) {
result.put(user.id, listSyntheticPasswordHandlesForUser(stateName, user.id));
result.put(user.id, listSyntheticPasswordProtectorsForUser(stateName, user.id));
}
return result;
}
public List<Long> listSyntheticPasswordHandlesForUser(String stateName, int userId) {
public List<Long> listSyntheticPasswordProtectorsForUser(String stateName, int userId) {
File baseDir = getSyntheticPasswordDirectoryForUser(userId);
List<Long> result = new ArrayList<>();
File[] files = baseDir.listFiles();
@@ -415,7 +416,7 @@ class LockSettingsStorage {
try {
result.add(Long.parseUnsignedLong(parts[0], 16));
} catch (NumberFormatException e) {
Slog.e(TAG, "Failed to parse handle " + parts[0]);
Slog.e(TAG, "Failed to parse protector ID " + parts[0]);
}
}
}
@@ -435,8 +436,8 @@ class LockSettingsStorage {
}
}
private File getSyntheticPasswordStateFileForUser(int userId, long handle, String name) {
String fileName = formatSimple("%016x.%s", handle, name);
private File getSyntheticPasswordStateFileForUser(int userId, long protectorId, String name) {
String fileName = formatSimple("%016x.%s", protectorId, name);
return new File(getSyntheticPasswordDirectoryForUser(userId), fileName);
}

View File

@@ -52,7 +52,7 @@ public class SyntheticPasswordCrypto {
private static final int PROFILE_KEY_IV_SIZE = 12;
private static final int DEFAULT_TAG_LENGTH_BITS = 128;
private static final int AES_KEY_LENGTH = 32; // 256-bit AES key
private static final byte[] APPLICATION_ID_PERSONALIZATION = "application-id".getBytes();
private static final byte[] PROTECTOR_SECRET_PERSONALIZATION = "application-id".getBytes();
// Time between the user credential is verified with GK and the decryption of synthetic password
// under the auth-bound key. This should always happen one after the other, but give it 15
// seconds just to be sure.
@@ -127,15 +127,19 @@ public class SyntheticPasswordCrypto {
}
}
public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] applicationId) {
/**
* Decrypt a legacy SP blob which did the Keystore and software encryption layers in the wrong
* order.
*/
public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] protectorSecret) {
try {
KeyStore keyStore = getKeyStore();
SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
if (decryptionKey == null) {
SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null);
if (keyStoreKey == null) {
throw new IllegalStateException("SP key is missing: " + keyAlias);
}
byte[] intermediate = decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, blob);
return decrypt(decryptionKey, intermediate);
byte[] intermediate = decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, blob);
return decrypt(keyStoreKey, intermediate);
} catch (Exception e) {
Slog.e(TAG, "Failed to decrypt V1 blob", e);
throw new IllegalStateException("Failed to decrypt blob", e);
@@ -157,16 +161,19 @@ public class SyntheticPasswordCrypto {
return keyStore;
}
public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) {
/**
* Decrypts an SP blob that was created by {@link #createBlob}.
*/
public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] protectorSecret) {
try {
final KeyStore keyStore = getKeyStore();
SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
if (decryptionKey == null) {
SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null);
if (keyStoreKey == null) {
throw new IllegalStateException("SP key is missing: " + keyAlias);
}
byte[] intermediate = decrypt(decryptionKey, blob);
return decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, intermediate);
byte[] intermediate = decrypt(keyStoreKey, blob);
return decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, intermediate);
} catch (CertificateException | IOException | BadPaddingException
| IllegalBlockSizeException
| KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
@@ -177,11 +184,22 @@ public class SyntheticPasswordCrypto {
}
}
public static byte[] createBlob(String keyAlias, byte[] data, byte[] applicationId, long sid) {
/**
* Creates a new SP blob by encrypting the given data. Two encryption layers are applied: an
* inner layer using a hash of protectorSecret as the key, and an outer layer using a new
* Keystore key with the given alias and optionally bound to a SID.
*
* The reason we use a layer of software encryption, instead of using protectorSecret as the
* applicationId of the Keystore key, is to work around buggy KeyMint implementations that don't
* cryptographically bind the applicationId to the key. The Keystore layer has to be the outer
* layer, so that LSKF verification is ratelimited by Gatekeeper when Weaver is unavailable.
*/
public static byte[] createBlob(String keyAlias, byte[] data, byte[] protectorSecret,
long sid) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
keyGenerator.init(AES_KEY_LENGTH * 8, new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
SecretKey keyStoreKey = keyGenerator.generateKey();
final KeyStore keyStore = getKeyStore();
KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
@@ -194,10 +212,10 @@ public class SyntheticPasswordCrypto {
}
keyStore.setEntry(keyAlias,
new KeyStore.SecretKeyEntry(secretKey),
new KeyStore.SecretKeyEntry(keyStoreKey),
builder.build());
byte[] intermediate = encrypt(applicationId, APPLICATION_ID_PERSONALIZATION, data);
return encrypt(secretKey, intermediate);
byte[] intermediate = encrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, data);
return encrypt(keyStoreKey, intermediate);
} catch (CertificateException | IOException | BadPaddingException
| IllegalBlockSizeException
| KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException

View File

@@ -47,7 +47,7 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
private ArrayMap<String, byte[]> mBlobs = new ArrayMap<>();
@Override
protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] applicationId) {
protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] protectorSecret) {
if (mBlobs.containsKey(blobKeyName) && !Arrays.equals(mBlobs.get(blobKeyName), blob)) {
throw new AssertionFailedError("blobKeyName content is overwritten: " + blobKeyName);
}
@@ -59,11 +59,11 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
byte[] data = new byte[len];
buffer.get(data);
len = buffer.getInt();
byte[] appId = new byte[len];
buffer.get(appId);
byte[] storedProtectorSecret = new byte[len];
buffer.get(storedProtectorSecret);
long sid = buffer.getLong();
if (!Arrays.equals(appId, applicationId)) {
throw new AssertionFailedError("Invalid application id");
if (!Arrays.equals(storedProtectorSecret, protectorSecret)) {
throw new AssertionFailedError("Invalid protector secret");
}
if (sid != 0 && mGateKeeper.getAuthTokenForSid(sid) == null) {
throw new AssertionFailedError("No valid auth token");
@@ -72,13 +72,14 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
}
@Override
protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] applicationId, long sid) {
protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] protectorSecret,
long sid) {
ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + data.length + Integer.BYTES
+ applicationId.length + Long.BYTES);
+ protectorSecret.length + Long.BYTES);
buffer.putInt(data.length);
buffer.put(data);
buffer.putInt(applicationId.length);
buffer.put(applicationId);
buffer.putInt(protectorSecret.length);
buffer.put(protectorSecret);
buffer.putLong(sid);
byte[] result = buffer.array();
mBlobs.put(blobKeyName, result);

View File

@@ -19,7 +19,7 @@ package com.android.server.locksettings;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
import static com.android.internal.widget.LockPatternUtils.CURRENT_LSKF_BASED_PROTECTOR_ID_KEY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -45,8 +45,8 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
import com.android.server.locksettings.SyntheticPasswordManager.PasswordData;
import com.android.server.locksettings.SyntheticPasswordManager.SyntheticPassword;
import org.junit.Before;
import org.junit.Test;
@@ -74,28 +74,28 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
@Test
public void testPasswordBasedSyntheticPassword() throws RemoteException {
public void testLskfBasedProtector() throws RemoteException {
final int USER_ID = 10;
final LockscreenCredential password = newPassword("user-password");
final LockscreenCredential badPassword = newPassword("bad-password");
MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage,
mGateKeeperService, mUserManager, mPasswordSlotManager);
AuthenticationToken authToken = manager.newSyntheticPassword(USER_ID);
long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService,
password, authToken, USER_ID);
SyntheticPassword sp = manager.newSyntheticPassword(USER_ID);
long protectorId = manager.createLskfBasedProtector(mGateKeeperService, password, sp,
USER_ID);
AuthenticationResult result = manager.unwrapPasswordBasedSyntheticPassword(
mGateKeeperService, handle, password, USER_ID, null);
assertArrayEquals(result.authToken.deriveKeyStorePassword(),
authToken.deriveKeyStorePassword());
AuthenticationResult result = manager.unlockLskfBasedProtector(mGateKeeperService,
protectorId, password, USER_ID, null);
assertArrayEquals(result.syntheticPassword.deriveKeyStorePassword(),
sp.deriveKeyStorePassword());
result = manager.unwrapPasswordBasedSyntheticPassword(mGateKeeperService, handle,
badPassword, USER_ID, null);
assertNull(result.authToken);
result = manager.unlockLskfBasedProtector(mGateKeeperService, protectorId, badPassword,
USER_ID, null);
assertNull(result.syntheticPassword);
}
private boolean hasSyntheticPassword(int userId) throws RemoteException {
return mService.getLong(SYNTHETIC_PASSWORD_HANDLE_KEY, 0, userId) != 0;
return mService.getLong(CURRENT_LSKF_BASED_PROTECTOR_ID_KEY, 0, userId) != 0;
}
private void initializeCredential(LockscreenCredential password, int userId)
@@ -544,12 +544,12 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
private void assertNoOrphanedFilesLeft(int userId) {
String handleString = String.format("%016x",
mService.getSyntheticPasswordHandleLocked(userId));
String lskfProtectorPrefix = String.format("%016x",
mService.getCurrentLskfBasedProtectorId(userId));
File directory = mStorage.getSyntheticPasswordDirectoryForUser(userId);
for (File file : directory.listFiles()) {
String[] parts = file.getName().split("\\.");
if (!parts[0].equals(handleString) && !parts[0].equals("0000000000000000")) {
if (!parts[0].equals(lskfProtectorPrefix) && !parts[0].equals("0000000000000000")) {
fail("Orphaned state left: " + file.getName());
}
}