Merge "Remove obsolete support for nonempty credential without SP"

This commit is contained in:
Eric Biggers
2022-06-30 20:44:03 +00:00
committed by Android (Google) Code Review
8 changed files with 164 additions and 706 deletions

View File

@@ -73,7 +73,6 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.sqlite.SQLiteDatabase;
import android.hardware.authsecret.V1_0.IAuthSecret;
import android.hardware.biometrics.BiometricManager;
import android.hardware.face.Face;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.Fingerprint;
@@ -89,7 +88,6 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -110,7 +108,6 @@ import android.security.keystore.recovery.RecoveryCertPath;
import android.security.keystore.recovery.WrappedApplicationKey;
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import android.security.keystore2.AndroidKeyStoreProvider;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.system.keystore2.Domain;
import android.text.TextUtils;
@@ -141,7 +138,6 @@ import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
import com.android.server.locksettings.LockSettingsStorage.PersistentData;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
@@ -163,7 +159,6 @@ import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
@@ -517,11 +512,6 @@ public class LockSettingsService extends ILockSettings.Stub {
return new RebootEscrowManager(mContext, callbacks, storage);
}
public boolean hasEnrolledBiometrics(int userId) {
BiometricManager bm = mContext.getSystemService(BiometricManager.class);
return bm.hasEnrolledBiometrics(userId);
}
public int binderGetCallingUid() {
return Binder.getCallingUid();
}
@@ -1285,11 +1275,8 @@ public class LockSettingsService extends ILockSettings.Stub {
return mStorage.getString(key, defaultValue, userId);
}
private void setKeyguardStoredQuality(int quality, int userId) {
if (DEBUG) Slog.d(TAG, "setKeyguardStoredQuality: user=" + userId + " quality=" + quality);
mStorage.setLong(LockPatternUtils.PASSWORD_TYPE_KEY, quality, userId);
}
// Not relevant for new devices, but some legacy devices still have PASSWORD_TYPE_KEY around to
// distinguish between credential types.
private int getKeyguardStoredQuality(int userId) {
return (int) mStorage.getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
@@ -1326,18 +1313,6 @@ public class LockSettingsService extends ILockSettings.Stub {
return pinOrPasswordQualityToCredentialType(getKeyguardStoredQuality(userId));
}
}
// Intentional duplication of the getKeyguardStoredQuality() call above since this is a
// unlikely code path (device with pre-synthetic password credential). We want to skip
// calling getKeyguardStoredQuality whenever possible.
final int savedQuality = getKeyguardStoredQuality(userId);
if (savedQuality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
&& mStorage.hasPattern(userId)) {
return CREDENTIAL_TYPE_PATTERN;
}
if (savedQuality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
&& mStorage.hasPassword(userId)) {
return pinOrPasswordQualityToCredentialType(savedQuality);
}
return CREDENTIAL_TYPE_NONE;
}
@@ -1742,33 +1717,15 @@ public class LockSettingsService extends ILockSettings.Stub {
LockscreenCredential savedCredential, int userId, boolean isLockTiedToParent) {
Objects.requireNonNull(credential);
Objects.requireNonNull(savedCredential);
if (DEBUG) Slog.d(TAG, "setLockCredentialInternal: user=" + userId);
synchronized (mSpManager) {
if (isSyntheticPasswordBasedCredentialLocked(userId)) {
return spBasedSetLockCredentialInternalLocked(credential, savedCredential, userId,
isLockTiedToParent);
}
}
if (credential.isNone()) {
clearUserKeyProtection(userId, null);
gateKeeperClearSecureUserId(userId);
mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId);
// Still update PASSWORD_TYPE_KEY if we are running in pre-synthetic password code path,
// since it forms part of the state that determines the credential type
// @see getCredentialTypeInternal
setKeyguardStoredQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
setKeystorePassword(null, userId);
fixateNewestUserKeyAuth(userId);
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
setUserPasswordMetrics(LockscreenCredential.createNone(), userId);
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
return true;
}
CredentialHash currentHandle = mStorage.readCredentialHash(userId);
if (isProfileWithUnifiedLock(userId)) {
// get credential from keystore when managed/clone profile has unified lock
if (savedCredential.isNone()) {
if (!isSyntheticPasswordBasedCredentialLocked(userId)) {
if (!savedCredential.isNone()) {
throw new IllegalStateException("Saved credential given, but user has no SP");
}
initializeSyntheticPasswordLocked(savedCredential, userId);
} else if (savedCredential.isNone() && isProfileWithUnifiedLock(userId)) {
// get credential from keystore when profile has unified lock
try {
//TODO: remove as part of b/80170828
savedCredential = getDecryptedPasswordForTiedProfile(userId);
@@ -1781,19 +1738,31 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.e(TAG, "Failed to decrypt child profile key", e);
}
}
} else {
if (currentHandle.hash == null) {
if (!savedCredential.isNone()) {
Slog.w(TAG, "Saved credential provided, but none stored");
final long origHandle = getSyntheticPasswordHandleLocked(userId);
AuthenticationResult authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
getGateKeeperService(), origHandle, savedCredential, userId, null);
VerifyCredentialResponse response = authResult.gkResponse;
AuthenticationToken auth = authResult.authToken;
if (auth == null) {
if (response == null
|| response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) {
Slog.w(TAG, "Failed to enroll: incorrect credential.");
return false;
}
savedCredential.close();
savedCredential = LockscreenCredential.createNone();
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
Slog.w(TAG, "Failed to enroll: rate limit exceeded.");
return false;
}
// Should not be reachable, but just in case.
throw new IllegalStateException("password change failed");
}
}
synchronized (mSpManager) {
initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential, userId);
return spBasedSetLockCredentialInternalLocked(credential, savedCredential, userId,
isLockTiedToParent);
onAuthTokenKnownForUser(userId, auth);
setLockCredentialWithAuthTokenLocked(credential, auth, userId);
mSpManager.destroyPasswordBasedSyntheticPassword(origHandle, userId);
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
return true;
}
}
@@ -1912,10 +1881,6 @@ public class LockSettingsService extends ILockSettings.Stub {
return getUserManagerFromCache(userId).isCredentialSharableWithParent();
}
private VerifyCredentialResponse convertResponse(GateKeeperResponse gateKeeperResponse) {
return VerifyCredentialResponse.fromGateKeeperResponse(gateKeeperResponse);
}
private void setCredentialRequiredToDecrypt(boolean required) {
if (isDeviceEncryptionEnabled()) {
Settings.Global.putInt(mContext.getContentResolver(),
@@ -2089,21 +2054,6 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
private static byte[] secretFromCredential(LockscreenCredential credential) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-512");
// Personalize the hash
byte[] personalization = "Android FBE credential hash".getBytes();
// Pad it to the block size of the hash function
personalization = Arrays.copyOf(personalization, 128);
digest.update(personalization);
digest.update(credential.getCredential());
return digest.digest();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("NoSuchAlgorithmException for SHA-512");
}
}
private boolean isUserKeyUnlocked(int userId) {
try {
return mStorageManager.isUserKeyUnlocked(userId);
@@ -2262,9 +2212,8 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
/*
* Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
* format.
/**
* Verify user credential and unlock the user.
* @param credential User's lockscreen credential
* @param userId User to verify the credential for
* @param progressCallback Receive progress callbacks
@@ -2282,36 +2231,59 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.e(TAG, "FRP credential can only be verified prior to provisioning.");
return VerifyCredentialResponse.ERROR;
}
Slog.d(TAG, "doVerifyCredential: user=" + userId);
VerifyCredentialResponse response = spBasedDoVerifyCredential(credential, userId,
progressCallback, flags);
final AuthenticationResult authResult;
VerifyCredentialResponse response;
// The user employs synthetic password based credential.
if (response != null) {
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
sendCredentialsOnUnlockIfRequired(credential, userId);
synchronized (mSpManager) {
if (!isSyntheticPasswordBasedCredentialLocked(userId)) {
Slog.wtf(TAG, "Unexpected credential type, should be SP based.");
return VerifyCredentialResponse.ERROR;
}
if (userId == USER_FRP) {
return mSpManager.verifyFrpCredential(getGateKeeperService(), credential,
progressCallback);
}
long handle = getSyntheticPasswordHandleLocked(userId);
authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
getGateKeeperService(), handle, credential, userId, progressCallback);
response = authResult.gkResponse;
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
// credential has matched
mBiometricDeferredQueue.addPendingLockoutResetForUser(userId,
authResult.authToken.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);
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.
Slog.wtf(TAG, "verifyChallenge with SP failed.");
return VerifyCredentialResponse.ERROR;
}
}
return response;
}
if (userId == USER_FRP) {
Slog.wtf(TAG, "Unexpected FRP credential type, should be SP based.");
return VerifyCredentialResponse.ERROR;
}
final CredentialHash storedHash = mStorage.readCredentialHash(userId);
if (!credential.checkAgainstStoredType(storedHash.type)) {
Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??"
+ " stored: " + storedHash.type + " passed in: " + credential.getType());
return VerifyCredentialResponse.ERROR;
}
response = verifyCredential(userId, storedHash, credential, progressCallback);
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
onCredentialVerified(authResult.authToken,
PasswordMetrics.computeForCredential(credential), userId);
if ((flags & VERIFY_FLAG_REQUEST_GK_PW_HANDLE) != 0) {
final long gkHandle = storeGatekeeperPasswordTemporarily(
authResult.authToken.deriveGkPassword());
response = new VerifyCredentialResponse.Builder()
.setGatekeeperPasswordHandle(gkHandle)
.build();
}
sendCredentialsOnUnlockIfRequired(credential, userId);
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
if (response.getTimeout() > 0) {
requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
}
}
return response;
}
@@ -2350,83 +2322,6 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
/**
* Lowest-level credential verification routine that talks to GateKeeper. If verification
* passes, unlock the corresponding user and keystore. Also handles the migration from legacy
* hash to GK.
*/
private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
LockscreenCredential credential, ICheckCredentialProgressCallback progressCallback) {
if ((storedHash == null || storedHash.hash.length == 0) && credential.isNone()) {
// don't need to pass empty credentials to GateKeeper
return VerifyCredentialResponse.OK;
}
if (storedHash == null || storedHash.hash.length == 0 || credential.isNone()) {
return VerifyCredentialResponse.ERROR;
}
// We're potentially going to be doing a bunch of disk I/O below as part
// of unlocking the user, so yell if calling from the main thread.
StrictMode.noteDiskRead();
GateKeeperResponse gateKeeperResponse;
try {
gateKeeperResponse = getGateKeeperService().verifyChallenge(
userId, 0L /* challenge */, storedHash.hash, credential.getCredential());
} catch (RemoteException e) {
Slog.e(TAG, "gatekeeper verify failed", e);
gateKeeperResponse = GateKeeperResponse.ERROR;
}
VerifyCredentialResponse response = convertResponse(gateKeeperResponse);
boolean shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
// credential has matched
if (progressCallback != null) {
try {
progressCallback.onCredentialVerified();
} catch (RemoteException e) {
Slog.w(TAG, "progressCallback throws exception", e);
}
}
setUserPasswordMetrics(credential, userId);
unlockKeystore(credential.getCredential(), userId);
Slog.i(TAG, "Unlocking user " + userId);
unlockUser(userId, secretFromCredential(credential));
if (isProfileWithSeparatedLock(userId)) {
setDeviceUnlockedForUser(userId);
}
if (shouldReEnroll) {
setLockCredentialInternal(credential, credential,
userId, /* isLockTiedToParent= */ false);
} else {
// Now that we've cleared of all required GK migration, let's do the final
// migration to synthetic password.
synchronized (mSpManager) {
if (shouldMigrateToSyntheticPasswordLocked(userId)) {
AuthenticationToken auth = initializeSyntheticPasswordLocked(
storedHash.hash, credential, userId);
activateEscrowTokens(auth, userId);
}
}
}
// Use credentials to create recoverable keystore snapshot.
sendCredentialsOnUnlockIfRequired(credential, userId);
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
if (response.getTimeout() > 0) {
requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
}
}
return response;
}
/**
* Keep track of the given user's latest password metric. This should be called
* when the user is authenticating or when a new password is being set. In comparison,
@@ -2779,7 +2674,9 @@ public class LockSettingsService extends ILockSettings.Stub {
* Precondition: vold and keystore unlocked.
*
* Create new synthetic password, set up synthetic password blob protected by the supplied
* user credential, and make the newly-created SP blob active.
* 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
@@ -2793,47 +2690,21 @@ public class LockSettingsService extends ILockSettings.Stub {
* 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.
*
*
* Different cases of calling this method:
* 1. credentialHash != null
* This implies credential != null, a new SP blob will be provisioned, and existing SID
* migrated to associate with the new SP.
* This happens during a normal migration case when the user currently has password.
*
* 2. credentialhash == null and credential == null
* A new SP blob and will be created, while the user has no credentials.
* This can happens when we are activating an escrow token on a unsecured device, during
* which we want to create the SP structure with an empty user credential.
* This could also happen during an untrusted reset to clear password.
*
* 3. credentialhash == null and credential != null
* The user sets a new lockscreen password FOR THE FIRST TIME on a SP-enabled device.
* New credential and new SID will be created
*/
@GuardedBy("mSpManager")
@VisibleForTesting
protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
LockscreenCredential credential, int userId) {
AuthenticationToken initializeSyntheticPasswordLocked(LockscreenCredential credential,
int userId) {
Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
Preconditions.checkState(
getSyntheticPasswordHandleLocked(userId) == SyntheticPasswordManager.DEFAULT_HANDLE,
"Cannot reinitialize SP");
final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(
getGateKeeperService(), credentialHash, credential, userId);
if (auth == null) {
Slog.wtf(TAG, "initializeSyntheticPasswordLocked returns null auth token");
return null;
}
final AuthenticationToken auth = mSpManager.newSyntheticPassword(userId);
long handle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
credential, auth, userId);
if (!credential.isNone()) {
if (credentialHash == null) {
// Since when initializing SP, we didn't provide an existing password handle
// for it to migrate SID, we need to create a new SID for the user.
mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
}
mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
mSpManager.verifyChallenge(getGateKeeperService(), auth, 0L, userId);
setUserKeyProtection(userId, auth.deriveDiskEncryptionKey());
setKeystorePassword(auth.deriveKeyStorePassword(), userId);
@@ -2878,73 +2749,6 @@ public class LockSettingsService extends ILockSettings.Stub {
return handle != SyntheticPasswordManager.DEFAULT_HANDLE;
}
@VisibleForTesting
protected boolean shouldMigrateToSyntheticPasswordLocked(int userId) {
return getSyntheticPasswordHandleLocked(userId) == SyntheticPasswordManager.DEFAULT_HANDLE;
}
private VerifyCredentialResponse spBasedDoVerifyCredential(LockscreenCredential userCredential,
int userId, ICheckCredentialProgressCallback progressCallback,
@LockPatternUtils.VerifyFlag int flags) {
final boolean hasEnrolledBiometrics = mInjector.hasEnrolledBiometrics(userId);
Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId
+ " hasEnrolledBiometrics=" + hasEnrolledBiometrics);
final AuthenticationResult authResult;
VerifyCredentialResponse response;
final boolean requestGkPw = (flags & VERIFY_FLAG_REQUEST_GK_PW_HANDLE) != 0;
synchronized (mSpManager) {
if (!isSyntheticPasswordBasedCredentialLocked(userId)) {
return null;
}
if (userId == USER_FRP) {
return mSpManager.verifyFrpCredential(getGateKeeperService(),
userCredential, progressCallback);
}
long handle = getSyntheticPasswordHandleLocked(userId);
authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
getGateKeeperService(), handle, userCredential, userId, progressCallback);
response = authResult.gkResponse;
// credential has matched
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
mBiometricDeferredQueue.addPendingLockoutResetForUser(userId,
authResult.authToken.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);
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.
Slog.wtf(TAG, "verifyChallenge with SP failed.");
return VerifyCredentialResponse.ERROR;
}
}
}
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
onCredentialVerified(authResult.authToken,
PasswordMetrics.computeForCredential(userCredential), userId);
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
if (response.getTimeout() > 0) {
requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
}
}
if (response.isMatched() && requestGkPw) {
final long handle = storeGatekeeperPasswordTemporarily(
authResult.authToken.deriveGkPassword());
return new VerifyCredentialResponse.Builder().setGatekeeperPasswordHandle(handle)
.build();
} else {
return response;
}
}
/**
* Stores the gatekeeper password temporarily.
* @param gatekeeperPassword unlocked upon successful Synthetic Password
@@ -3148,56 +2952,6 @@ public class LockSettingsService extends ILockSettings.Stub {
};
}
/**
* @param savedCredential if the user is a profile with unified challenge and
* savedCredential is empty, LSS will try to re-derive the profile password internally.
* TODO (b/80170828): Fix this so profile password is always passed in.
*/
@GuardedBy("mSpManager")
private boolean spBasedSetLockCredentialInternalLocked(LockscreenCredential credential,
LockscreenCredential savedCredential, int userId, boolean isLockTiedToParent) {
if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
if (savedCredential.isNone() && isProfileWithUnifiedLock(userId)) {
// get credential from keystore when profile has unified lock
try {
//TODO: remove as part of b/80170828
savedCredential = getDecryptedPasswordForTiedProfile(userId);
} catch (FileNotFoundException e) {
Slog.i(TAG, "Child profile key not found");
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
| NoSuchAlgorithmException | NoSuchPaddingException
| InvalidAlgorithmParameterException | IllegalBlockSizeException
| BadPaddingException | CertificateException | IOException e) {
Slog.e(TAG, "Failed to decrypt child profile key", e);
}
}
long handle = getSyntheticPasswordHandleLocked(userId);
AuthenticationResult authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
getGateKeeperService(), handle, savedCredential, userId, null);
VerifyCredentialResponse response = authResult.gkResponse;
AuthenticationToken auth = authResult.authToken;
if (auth == null) {
if (response == null
|| response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) {
Slog.w(TAG, "Failed to enroll: incorrect credential.");
return false;
}
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
Slog.w(TAG, "Failed to enroll: rate limit exceeded.");
return false;
}
// Should not be reachable, but just in case.
throw new IllegalStateException("password change failed");
}
onAuthTokenKnownForUser(userId, auth);
setLockCredentialWithAuthTokenLocked(credential, auth, userId);
mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
return true;
}
/**
* Returns a fixed pseudorandom byte string derived from the user's synthetic password.
* This is used to salt the password history hash to protect the hash against offline
@@ -3244,20 +2998,18 @@ public class LockSettingsService extends ILockSettings.Stub {
// the token can then be activated immediately.
AuthenticationToken auth = null;
if (!isUserSecure(userId)) {
if (shouldMigrateToSyntheticPasswordLocked(userId)) {
auth = initializeSyntheticPasswordLocked(
/* credentialHash */ null, LockscreenCredential.createNone(), userId);
} else /* isSyntheticPasswordBasedCredentialLocked(userId) */ {
long pwdHandle = getSyntheticPasswordHandleLocked(userId);
long handle = getSyntheticPasswordHandleLocked(userId);
if (handle == SyntheticPasswordManager.DEFAULT_HANDLE) {
auth = initializeSyntheticPasswordLocked(LockscreenCredential.createNone(),
userId);
} else {
auth = mSpManager.unwrapPasswordBasedSyntheticPassword(getGateKeeperService(),
pwdHandle, LockscreenCredential.createNone(), userId, null).authToken;
handle, LockscreenCredential.createNone(), userId, null).authToken;
}
}
if (isSyntheticPasswordBasedCredentialLocked(userId)) {
disableEscrowTokenOnNonManagedDevicesIfNeeded(userId);
if (!mSpManager.hasEscrowData(userId)) {
throw new SecurityException("Escrow token is disabled on the current user");
}
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);

View File

@@ -85,8 +85,6 @@ class LockSettingsStorage extends WatchableImpl {
};
private static final String SYSTEM_DIRECTORY = "/system/";
private static final String LOCK_PATTERN_FILE = "gatekeeper.pattern.key";
private static final String LOCK_PASSWORD_FILE = "gatekeeper.password.key";
private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";
private static final String REBOOT_ESCROW_FILE = "reboot.escrow.key";
@@ -248,38 +246,6 @@ class LockSettingsStorage extends WatchableImpl {
}
cursor.close();
}
// Populate cache by reading the password and pattern files.
readCredentialHash(userId);
}
private CredentialHash readPasswordHashIfExists(int userId) {
byte[] stored = readFile(getLockPasswordFilename(userId));
if (!ArrayUtils.isEmpty(stored)) {
return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN);
}
return null;
}
private CredentialHash readPatternHashIfExists(int userId) {
byte[] stored = readFile(getLockPatternFilename(userId));
if (!ArrayUtils.isEmpty(stored)) {
return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
}
return null;
}
public CredentialHash readCredentialHash(int userId) {
CredentialHash passwordHash = readPasswordHashIfExists(userId);
if (passwordHash != null) {
return passwordHash;
}
CredentialHash patternHash = readPatternHashIfExists(userId);
if (patternHash != null) {
return patternHash;
}
return CredentialHash.createEmptyHash();
}
public void removeChildProfileLock(int userId) {
@@ -336,14 +302,6 @@ class LockSettingsStorage extends WatchableImpl {
deleteFile(getRebootEscrowServerBlob());
}
public boolean hasPassword(int userId) {
return hasFile(getLockPasswordFilename(userId));
}
public boolean hasPattern(int userId) {
return hasFile(getLockPatternFilename(userId));
}
private boolean hasFile(String name) {
byte[] contents = readFile(name);
return contents != null && contents.length > 0;
@@ -429,33 +387,6 @@ class LockSettingsStorage extends WatchableImpl {
}
}
public void writeCredentialHash(CredentialHash hash, int userId) {
byte[] patternHash = null;
byte[] passwordHash = null;
if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN
|| hash.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
|| hash.type == LockPatternUtils.CREDENTIAL_TYPE_PIN) {
passwordHash = hash.hash;
} else if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
patternHash = hash.hash;
} else {
Preconditions.checkArgument(hash.type == LockPatternUtils.CREDENTIAL_TYPE_NONE,
"Unknown credential type");
}
writeFile(getLockPasswordFilename(userId), passwordHash);
writeFile(getLockPatternFilename(userId), patternHash);
}
@VisibleForTesting
String getLockPatternFilename(int userId) {
return getLockCredentialFilePathForUser(userId, LOCK_PATTERN_FILE);
}
@VisibleForTesting
String getLockPasswordFilename(int userId) {
return getLockCredentialFilePathForUser(userId, LOCK_PASSWORD_FILE);
}
@VisibleForTesting
String getChildProfileLockFile(int userId) {
return getLockCredentialFilePathForUser(userId, CHILD_PROFILE_LOCK_FILE);
@@ -567,12 +498,7 @@ class LockSettingsStorage extends WatchableImpl {
if (parentInfo == null) {
// This user owns its lock settings files - safe to delete them
synchronized (mFileWriteLock) {
deleteFilesAndRemoveCache(
getLockPasswordFilename(userId),
getLockPatternFilename(userId),
getRebootEscrowFile(userId));
}
deleteFile(getRebootEscrowFile(userId));
} else {
// Managed profile
removeChildProfileLock(userId);
@@ -594,17 +520,6 @@ class LockSettingsStorage extends WatchableImpl {
dispatchChange(this);
}
private void deleteFilesAndRemoveCache(String... names) {
for (String name : names) {
File file = new File(name);
if (file.exists()) {
file.delete();
mCache.putFile(name, null);
dispatchChange(this);
}
}
}
public void setBoolean(String key, boolean value, int userId) {
setString(key, value ? "1" : "0", userId);
}

View File

@@ -599,47 +599,19 @@ public class SyntheticPasswordManager {
}
/**
* Initializing a new Authentication token, possibly from an existing credential and hash.
* Initializes a new Authentication token for the given user.
*
* The authentication token would bear a randomly-generated synthetic password.
* The authentication token will bear a randomly-generated synthetic password.
*
* This method has the side effect of rebinding the SID of the given user to the
* newly-generated SP.
*
* If the existing credential hash is non-null, the existing SID mill be migrated so
* the synthetic password in the authentication token will produce the same SID
* (the corresponding synthetic password handle is persisted by SyntheticPasswordManager
* in a per-user data storage.)
*
* If the existing credential hash is null, it means the given user should have no SID so
* SyntheticPasswordManager will nuke any SP handle previously persisted. In this case,
* the supplied credential parameter is also ignored.
* Any existing SID for the user is cleared.
*
* Also saves the escrow information necessary to re-generate the synthetic password under
* an escrow scheme. This information can be removed with {@link #destroyEscrowData} if
* password escrow should be disabled completely on the given user.
*
*/
public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
byte[] hash, LockscreenCredential credential, int userId) {
AuthenticationToken newSyntheticPassword(int userId) {
clearSidForUser(userId);
AuthenticationToken result = AuthenticationToken.create();
GateKeeperResponse response;
if (hash != null) {
try {
response = gatekeeper.enroll(userId, hash, credential.getCredential(),
result.deriveGkPassword());
} catch (RemoteException e) {
throw new IllegalStateException("Failed to enroll credential duing SP init", e);
}
if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
Slog.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
clearSidForUser(userId);
} else {
saveSyntheticPasswordHandle(response.getPayload(), userId);
}
} else {
clearSidForUser(userId);
}
saveEscrowData(result, userId);
return result;
}

View File

@@ -135,11 +135,6 @@ public class LockSettingsServiceTestable extends LockSettingsService {
return mUserManagerInternal;
}
@Override
public boolean hasEnrolledBiometrics(int userId) {
return false;
}
@Override
public int binderGetCallingUid() {
return Process.SYSTEM_UID;

View File

@@ -42,11 +42,8 @@ import android.service.gatekeeper.GateKeeperResponse;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.locksettings.FakeGateKeeperService.VerifyHandle;
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
import org.junit.Before;
import org.junit.Test;
@@ -96,17 +93,16 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
@Test
public void testChangePasswordFailPrimaryUser() throws RemoteException {
final long sid = 1234;
initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"), sid);
initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"));
assertFalse(mService.setLockCredential(newPassword("newpwd"), newPassword("badpwd"),
PRIMARY_USER_ID));
assertVerifyCredentials(PRIMARY_USER_ID, newPassword("password"), sid);
assertVerifyCredentials(PRIMARY_USER_ID, newPassword("password"));
}
@Test
public void testClearPasswordPrimaryUser() throws RemoteException {
initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"), 1234);
initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"));
assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"),
PRIMARY_USER_ID));
assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(PRIMARY_USER_ID));
@@ -264,10 +260,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
public void testSetLockCredential_forProfileWithSeparateChallenge_updatesCredentials()
throws Exception {
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, true, null);
initializeStorageWithCredential(
MANAGED_PROFILE_USER_ID,
newPattern("12345"),
1234);
initializeStorageWithCredential(MANAGED_PROFILE_USER_ID, newPattern("12345"));
assertTrue(mService.setLockCredential(
newPassword("newPassword"),
@@ -300,8 +293,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
throws Exception {
final LockscreenCredential oldCredential = newPassword("oldPassword");
final LockscreenCredential newCredential = newPassword("newPassword");
initializeStorageWithCredential(
PRIMARY_USER_ID, oldCredential, 1234);
initializeStorageWithCredential(PRIMARY_USER_ID, oldCredential);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
assertTrue(mService.setLockCredential(
@@ -321,7 +313,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
public void
testSetLockCredential_forPrimaryUserWithUnifiedChallengeProfile_removesBothCredentials()
throws Exception {
initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("oldPassword"), 1234);
initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("oldPassword"));
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
assertTrue(mService.setLockCredential(
@@ -337,10 +329,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
@Test
public void testSetLockCredential_nullCredential_removeBiometrics() throws RemoteException {
initializeStorageWithCredential(
PRIMARY_USER_ID,
newPattern("123654"),
1234);
initializeStorageWithCredential(PRIMARY_USER_ID, newPattern("123654"));
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
mService.setLockCredential(nonePassword(), newPattern("123654"), PRIMARY_USER_ID);
@@ -358,7 +347,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
throws Exception {
final LockscreenCredential parentPassword = newPassword("parentPassword");
final LockscreenCredential profilePassword = newPassword("profilePassword");
initializeStorageWithCredential(PRIMARY_USER_ID, parentPassword, 1234);
initializeStorageWithCredential(PRIMARY_USER_ID, parentPassword);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
assertTrue(mService.setLockCredential(
@@ -377,7 +366,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
throws Exception {
final LockscreenCredential parentPassword = newPassword("parentPassword");
final LockscreenCredential profilePassword = newPattern("12345");
initializeStorageWithCredential(PRIMARY_USER_ID, parentPassword, 1234);
initializeStorageWithCredential(PRIMARY_USER_ID, parentPassword);
// Create and verify separate profile credentials.
testCreateCredential(MANAGED_PROFILE_USER_ID, profilePassword);
@@ -393,7 +382,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
@Test
public void testVerifyCredential_forPrimaryUser_sendsCredentials() throws Exception {
final LockscreenCredential password = newPassword("password");
initializeStorageWithCredential(PRIMARY_USER_ID, password, 1234);
initializeStorageWithCredential(PRIMARY_USER_ID, password);
reset(mRecoverableKeyStoreManager);
mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */);
@@ -424,7 +413,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
public void verifyCredential_forPrimaryUserWithUnifiedChallengeProfile_sendsCredentialsForBoth()
throws Exception {
final LockscreenCredential pattern = newPattern("12345");
initializeStorageWithCredential(PRIMARY_USER_ID, pattern, 1234);
initializeStorageWithCredential(PRIMARY_USER_ID, pattern);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
reset(mRecoverableKeyStoreManager);
@@ -464,7 +453,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
private void testCreateCredential(int userId, LockscreenCredential credential)
throws RemoteException {
assertTrue(mService.setLockCredential(credential, nonePassword(), userId));
assertVerifyCredentials(userId, credential, -1);
assertVerifyCredentials(userId, credential);
}
private void testCreateCredentialFailsWithoutLockScreen(
@@ -483,19 +472,17 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
private void testChangeCredentials(int userId, LockscreenCredential newCredential,
LockscreenCredential oldCredential) throws RemoteException {
final long sid = 1234;
initializeStorageWithCredential(userId, oldCredential, sid);
initializeStorageWithCredential(userId, oldCredential);
assertTrue(mService.setLockCredential(newCredential, oldCredential, userId));
assertVerifyCredentials(userId, newCredential, sid);
assertVerifyCredentials(userId, newCredential);
}
private void assertVerifyCredentials(int userId, LockscreenCredential credential, long sid)
private void assertVerifyCredentials(int userId, LockscreenCredential credential)
throws RemoteException{
VerifyCredentialResponse response = mService.verifyCredential(credential, userId,
0 /* flags */);
assertEquals(GateKeeperResponse.RESPONSE_OK, response.getResponseCode());
if (sid != -1) assertEquals(sid, mGateKeeperService.getSecureUserId(userId));
if (credential.isPassword()) {
assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(userId));
} else if (credential.isPin()) {
@@ -517,19 +504,11 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
badCredential, userId, 0 /* flags */).getResponseCode());
}
private void initializeStorageWithCredential(int userId, LockscreenCredential credential,
long sid) throws RemoteException {
byte[] oldHash = new VerifyHandle(credential.getCredential(), sid).toBytes();
if (mService.shouldMigrateToSyntheticPasswordLocked(userId)) {
mService.initializeSyntheticPasswordLocked(oldHash, credential, userId);
} else {
if (credential.isPassword() || credential.isPin()) {
mStorage.writeCredentialHash(CredentialHash.create(oldHash,
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId);
} else {
mStorage.writeCredentialHash(CredentialHash.create(oldHash,
LockPatternUtils.CREDENTIAL_TYPE_PATTERN), userId);
}
}
@SuppressWarnings("GuardedBy") // for initializeSyntheticPasswordLocked
private void initializeStorageWithCredential(int userId, LockscreenCredential credential)
throws RemoteException {
assertEquals(0, mGateKeeperService.getSecureUserId(userId));
mService.initializeSyntheticPasswordLocked(credential, userId);
assertNotEquals(0, mGateKeeperService.getSecureUserId(userId));
}
}

View File

@@ -63,18 +63,6 @@ public class LockSettingsStorageTestable extends LockSettingsStorage {
}).when(mPersistentDataBlockManager).getFrpCredentialHandle();
}
@Override
String getLockPatternFilename(int userId) {
return makeDirs(mStorageDir,
super.getLockPatternFilename(userId)).getAbsolutePath();
}
@Override
String getLockPasswordFilename(int userId) {
return makeDirs(mStorageDir,
super.getLockPasswordFilename(userId)).getAbsolutePath();
}
@Override
String getChildProfileLockFile(int userId) {
return makeDirs(mStorageDir,

View File

@@ -48,9 +48,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
import com.android.server.locksettings.LockSettingsStorage.PersistentData;
import org.junit.After;
@@ -72,10 +70,7 @@ import java.util.concurrent.CountDownLatch;
@RunWith(AndroidJUnit4.class)
public class LockSettingsStorageTests {
private static final int SOME_USER_ID = 1034;
private final byte[] PASSWORD_0 = "thepassword0".getBytes();
private final byte[] PASSWORD_1 = "password1".getBytes();
private final byte[] PATTERN_0 = "123654".getBytes();
private final byte[] PATTERN_1 = "147852369".getBytes();
private final byte[] PASSWORD = "thepassword".getBytes();
public static final byte[] PAYLOAD = new byte[] {1, 2, -1, -2, 33};
@@ -216,161 +211,64 @@ public class LockSettingsStorageTests {
@Test
public void testRemoveUser() {
mStorage.writeKeyValue("key", "value", 0);
writePasswordBytes(PASSWORD_0, 0);
writePatternBytes(PATTERN_0, 0);
mStorage.writeKeyValue("key", "value", 1);
writePasswordBytes(PASSWORD_1, 1);
writePatternBytes(PATTERN_1, 1);
mStorage.removeUser(0);
assertEquals("value", mStorage.readKeyValue("key", "default", 1));
assertEquals("default", mStorage.readKeyValue("key", "default", 0));
assertEquals(LockPatternUtils.CREDENTIAL_TYPE_NONE, mStorage.readCredentialHash(0).type);
assertPatternBytes(PATTERN_1, 1);
}
@Test
public void testCredential_Default() {
assertEquals(mStorage.readCredentialHash(0).type, LockPatternUtils.CREDENTIAL_TYPE_NONE);
}
@Test
public void testPassword_Write() {
writePasswordBytes(PASSWORD_0, 0);
assertPasswordBytes(PASSWORD_0, 0);
mStorage.clearCache();
assertPasswordBytes(PASSWORD_0, 0);
}
@Test
public void testPassword_WriteProfileWritesParent() {
writePasswordBytes(PASSWORD_0, 1);
writePasswordBytes(PASSWORD_1, 2);
assertPasswordBytes(PASSWORD_0, 1);
assertPasswordBytes(PASSWORD_1, 2);
mStorage.clearCache();
assertPasswordBytes(PASSWORD_0, 1);
assertPasswordBytes(PASSWORD_1, 2);
}
@Test
public void testLockType_WriteProfileWritesParent() {
writePasswordBytes(PASSWORD_0, 10);
writePatternBytes(PATTERN_0, 20);
assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN,
mStorage.readCredentialHash(10).type);
assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
mStorage.readCredentialHash(20).type);
mStorage.clearCache();
assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN,
mStorage.readCredentialHash(10).type);
assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
mStorage.readCredentialHash(20).type);
}
@Test
public void testPassword_WriteParentWritesProfile() {
writePasswordBytes(PASSWORD_0, 2);
writePasswordBytes(PASSWORD_1, 1);
assertPasswordBytes(PASSWORD_1, 1);
assertPasswordBytes(PASSWORD_0, 2);
mStorage.clearCache();
assertPasswordBytes(PASSWORD_1, 1);
assertPasswordBytes(PASSWORD_0, 2);
}
@Test
public void testProfileLock_ReadWriteChildProfileLock() {
assertFalse(mStorage.hasChildProfileLock(20));
mStorage.writeChildProfileLock(20, PASSWORD_0);
assertArrayEquals(PASSWORD_0, mStorage.readChildProfileLock(20));
mStorage.writeChildProfileLock(20, PASSWORD);
assertArrayEquals(PASSWORD, mStorage.readChildProfileLock(20));
assertTrue(mStorage.hasChildProfileLock(20));
mStorage.clearCache();
assertArrayEquals(PASSWORD_0, mStorage.readChildProfileLock(20));
assertArrayEquals(PASSWORD, mStorage.readChildProfileLock(20));
assertTrue(mStorage.hasChildProfileLock(20));
}
@Test
public void testPattern_Write() {
writePatternBytes(PATTERN_0, 0);
assertPatternBytes(PATTERN_0, 0);
mStorage.clearCache();
assertPatternBytes(PATTERN_0, 0);
}
@Test
public void testPattern_WriteProfileWritesParent() {
writePatternBytes(PATTERN_0, 1);
writePatternBytes(PATTERN_1, 2);
assertPatternBytes(PATTERN_0, 1);
assertPatternBytes(PATTERN_1, 2);
mStorage.clearCache();
assertPatternBytes(PATTERN_0, 1);
assertPatternBytes(PATTERN_1, 2);
}
@Test
public void testPattern_WriteParentWritesProfile() {
writePatternBytes(PATTERN_1, 2);
writePatternBytes(PATTERN_0, 1);
assertPatternBytes(PATTERN_0, 1);
assertPatternBytes(PATTERN_1, 2);
mStorage.clearCache();
assertPatternBytes(PATTERN_0, 1);
assertPatternBytes(PATTERN_1, 2);
}
@Test
public void testPrefetch() {
mStorage.writeKeyValue("key", "toBeFetched", 0);
writePatternBytes(PATTERN_0, 0);
mStorage.clearCache();
mStorage.prefetchUser(0);
assertEquals("toBeFetched", mStorage.readKeyValue("key", "default", 0));
assertPatternBytes(PATTERN_0, 0);
}
@Test
public void testFileLocation_Owner() {
LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
assertEquals("/data/system/gatekeeper.pattern.key", storage.getLockPatternFilename(0));
assertEquals("/data/system/gatekeeper.password.key", storage.getLockPasswordFilename(0));
assertEquals("/data/system/gatekeeper.profile.key", storage.getChildProfileLockFile(0));
}
@Test
public void testFileLocation_SecondaryUser() {
LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
assertEquals("/data/system/users/1/gatekeeper.pattern.key", storage.getLockPatternFilename(1));
assertEquals("/data/system/users/1/gatekeeper.password.key", storage.getLockPasswordFilename(1));
assertEquals("/data/system/users/1/gatekeeper.profile.key",
storage.getChildProfileLockFile(1));
}
@Test
public void testFileLocation_ProfileToSecondary() {
LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
assertEquals("/data/system/users/2/gatekeeper.pattern.key", storage.getLockPatternFilename(2));
assertEquals("/data/system/users/2/gatekeeper.password.key", storage.getLockPasswordFilename(2));
assertEquals("/data/system/users/2/gatekeeper.profile.key",
storage.getChildProfileLockFile(2));
}
@Test
public void testFileLocation_ProfileToOwner() {
LockSettingsStorage storage = new LockSettingsStorage(InstrumentationRegistry.getContext());
assertEquals("/data/system/users/3/gatekeeper.pattern.key", storage.getLockPatternFilename(3));
assertEquals("/data/system/users/3/gatekeeper.password.key", storage.getLockPasswordFilename(3));
assertEquals("/data/system/users/3/gatekeeper.profile.key",
storage.getChildProfileLockFile(3));
}
@Test
@@ -483,28 +381,6 @@ public class LockSettingsStorageTests {
}
}
private void writePasswordBytes(byte[] password, int userId) {
mStorage.writeCredentialHash(CredentialHash.create(
password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId);
}
private void writePatternBytes(byte[] pattern, int userId) {
mStorage.writeCredentialHash(CredentialHash.create(
pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN), userId);
}
private void assertPasswordBytes(byte[] password, int userId) {
CredentialHash cred = mStorage.readCredentialHash(userId);
assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN, cred.type);
assertArrayEquals(password, cred.hash);
}
private void assertPatternBytes(byte[] pattern, int userId) {
CredentialHash cred = mStorage.readCredentialHash(userId);
assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, cred.type);
assertArrayEquals(pattern, cred.hash);
}
/**
* Suppresses reporting of the WTF to system_server, so we don't pollute the dropbox with
* intentionally caused WTFs.

View File

@@ -19,7 +19,6 @@ 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_ENABLED_KEY;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
import static org.junit.Assert.assertEquals;
@@ -38,7 +37,6 @@ import static org.mockito.Mockito.when;
import android.app.admin.PasswordMetrics;
import android.app.PropertyInvalidatedCache;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -82,8 +80,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
final LockscreenCredential badPassword = newPassword("bad-password");
MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage,
mGateKeeperService, mUserManager, mPasswordSlotManager);
AuthenticationToken authToken = manager.newSyntheticPasswordAndSid(mGateKeeperService, null,
null, USER_ID);
AuthenticationToken authToken = manager.newSyntheticPassword(USER_ID);
long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService,
password, authToken, USER_ID);
@@ -97,32 +94,23 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
assertNull(result.authToken);
}
private void disableSyntheticPassword() throws RemoteException {
mService.setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM);
}
private void enableSyntheticPassword() throws RemoteException {
mService.setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM);
}
private boolean hasSyntheticPassword(int userId) throws RemoteException {
return mService.getLong(SYNTHETIC_PASSWORD_HANDLE_KEY, 0, userId) != 0;
}
protected void initializeCredentialUnderSP(LockscreenCredential password, int userId)
private void initializeCredential(LockscreenCredential password, int userId)
throws RemoteException {
enableSyntheticPassword();
assertTrue(mService.setLockCredential(password, nonePassword(), userId));
assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(userId));
assertTrue(mService.isSyntheticPasswordBasedCredential(userId));
}
@Test
public void testSyntheticPasswordChangeCredential() throws RemoteException {
public void testChangeCredential() throws RemoteException {
final LockscreenCredential password = newPassword("password");
final LockscreenCredential newPassword = newPassword("newpassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
mService.setLockCredential(newPassword, password, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
@@ -131,11 +119,11 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
@Test
public void testSyntheticPasswordVerifyCredential() throws RemoteException {
public void testVerifyCredential() throws RemoteException {
LockscreenCredential password = newPassword("password");
LockscreenCredential badPassword = newPassword("badpassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
@@ -144,11 +132,11 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
@Test
public void testSyntheticPasswordClearCredential() throws RemoteException {
public void testClearCredential() throws RemoteException {
LockscreenCredential password = newPassword("password");
LockscreenCredential badPassword = newPassword("newpassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
// clear password
mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID);
@@ -162,11 +150,11 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
@Test
public void testSyntheticPasswordChangeCredentialKeepsAuthSecret() throws RemoteException {
public void testChangeCredentialKeepsAuthSecret() throws RemoteException {
LockscreenCredential password = newPassword("password");
LockscreenCredential badPassword = newPassword("new");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
mService.setLockCredential(badPassword, password, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
badPassword, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
@@ -178,11 +166,10 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
@Test
public void testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret() throws RemoteException {
public void testVerifyPassesPrimaryUserAuthSecret() throws RemoteException {
LockscreenCredential password = newPassword("password");
LockscreenCredential newPassword = newPassword("new");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
reset(mAuthSecretService);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
@@ -193,7 +180,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
public void testSecondaryUserDoesNotPassAuthSecret() throws RemoteException {
LockscreenCredential password = newPassword("password");
initializeCredentialUnderSP(password, SECONDARY_USER_ID);
initializeCredential(password, SECONDARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
password, SECONDARY_USER_ID, 0 /* flags */).getResponseCode());
verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
@@ -207,9 +194,9 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
@Test
public void testSyntheticPasswordAndCredentialDoesNotPassAuthSecret() throws RemoteException {
LockscreenCredential password = newPassword("passwordForASyntheticPassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
public void testCredentialDoesNotPassAuthSecret() throws RemoteException {
LockscreenCredential password = newPassword("password");
initializeCredential(password, PRIMARY_USER_ID);
reset(mAuthSecretService);
mService.onUnlockUser(PRIMARY_USER_ID);
@@ -219,8 +206,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
@Test
public void testSyntheticPasswordButNoCredentialPassesAuthSecret() throws RemoteException {
LockscreenCredential password = newPassword("getASyntheticPassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
LockscreenCredential password = newPassword("password");
initializeCredential(password, PRIMARY_USER_ID);
mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID);
reset(mAuthSecretService);
@@ -234,7 +221,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
LockscreenCredential password = newPassword("password");
LockscreenCredential pattern = newPattern("123654");
byte[] token = "some-high-entropy-secure-token".getBytes();
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
// Disregard any reportPasswordChanged() invocations as part of credential setup.
flushHandlerTasks();
reset(mDevicePolicyManager);
@@ -269,7 +256,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
LockscreenCredential password = newPassword("password");
LockscreenCredential pattern = newPattern("123654");
byte[] token = "some-high-entropy-secure-token".getBytes();
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
@@ -295,7 +282,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
LockscreenCredential pattern = newPattern("123654");
LockscreenCredential newPassword = newPassword("password");
byte[] token = "some-high-entropy-secure-token".getBytes();
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
@@ -318,7 +305,6 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNeedsMigration()
throws RemoteException {
final byte[] token = "some-high-entropy-secure-token".getBytes();
enableSyntheticPassword();
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
@@ -331,7 +317,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
final byte[] token = "some-high-entropy-secure-token".getBytes();
// By first setting a password and then clearing it, we enter the state where SP is
// initialized but the user currently has no password
initializeCredentialUnderSP(newPassword("password"), PRIMARY_USER_ID);
initializeCredential(newPassword("password"), PRIMARY_USER_ID);
assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"),
PRIMARY_USER_ID));
assertTrue(mService.isSyntheticPasswordBasedCredential(PRIMARY_USER_ID));
@@ -343,19 +329,15 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
@Test
public void testEscrowTokenActivatedLaterWithUserPasswordNeedsMigration()
throws RemoteException {
public void testEscrowTokenActivatedLaterWithUserPassword() throws RemoteException {
byte[] token = "some-high-entropy-secure-token".getBytes();
LockscreenCredential password = newPassword("password");
// Set up pre-SP user password
disableSyntheticPassword();
mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID);
enableSyntheticPassword();
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
// Token not activated immediately since user password exists
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
// Activate token (password gets migrated to SP at the same time)
// Activate token
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
// Verify token is activated
@@ -383,7 +365,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
LockscreenCredential password = newPassword("password");
LockscreenCredential pattern = newPattern("123654");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
long handle0 = mLocalService.addEscrowToken(token0, PRIMARY_USER_ID, null);
long handle1 = mLocalService.addEscrowToken(token1, PRIMARY_USER_ID, null);
@@ -412,7 +394,6 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
byte[] token = "some-high-entropy-secure-token".getBytes();
mService.mHasSecureLockScreen = false;
enableSyntheticPassword();
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
@@ -515,7 +496,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
LockscreenCredential password = newPassword("testGsiDisablesAuthSecret-password");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
@@ -525,7 +506,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
public void testUnlockUserWithToken() throws Exception {
LockscreenCredential password = newPassword("password");
byte[] token = "some-high-entropy-secure-token".getBytes();
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
// Disregard any reportPasswordChanged() invocations as part of credential setup.
flushHandlerTasks();
reset(mDevicePolicyManager);
@@ -546,7 +527,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
@Test
public void testPasswordChange_NoOrphanedFilesLeft() throws Exception {
LockscreenCredential password = newPassword("password");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
initializeCredential(password, PRIMARY_USER_ID);
assertTrue(mService.setLockCredential(password, password, PRIMARY_USER_ID));
assertNoOrphanedFilesLeft(PRIMARY_USER_ID);
}