Introduce LockscreenCredential to LockSettingsService
Use LockscreenCredential as the representation format for credentials throughout the LSS system service, until when the raw credential bits are needed. Most of the changes are just mechanical, except in places when null was used to represent empty credential, but now it has its own concrete object. Test: atest com.android.server.locksettings Test: atest com.android.server.devicepolicy.DevicePolicyManagerTest Test: atest MixedManagedProfileOwnerTest#testResetPasswordWithToken Test: atest com.android.cts.devicepolicy.PasswordComplexityTest Test: atest com.android.cts.devicepolicy.ManagedProfilePasswordTest Bug:65239740 Change-Id: I3997c3c2b6651d11a0e447448ac3a8523a18fa36
This commit is contained in:
@@ -24,6 +24,7 @@ import android.security.keystore.recovery.KeyChainSnapshot;
|
||||
import android.security.keystore.recovery.KeyChainProtectionParams;
|
||||
import android.security.keystore.recovery.RecoveryCertPath;
|
||||
import com.android.internal.widget.ICheckCredentialProgressCallback;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.internal.widget.VerifyCredentialResponse;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -42,19 +43,19 @@ interface ILockSettings {
|
||||
long getLong(in String key, in long defaultValue, in int userId);
|
||||
@UnsupportedAppUsage
|
||||
String getString(in String key, in String defaultValue, in int userId);
|
||||
boolean setLockCredential(in byte[] credential, int type, in byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange);
|
||||
boolean setLockCredential(in LockscreenCredential credential, in LockscreenCredential savedCredential, int userId, boolean allowUntrustedChange);
|
||||
void resetKeyStore(int userId);
|
||||
VerifyCredentialResponse checkCredential(in byte[] credential, int type, int userId,
|
||||
VerifyCredentialResponse checkCredential(in LockscreenCredential credential, int userId,
|
||||
in ICheckCredentialProgressCallback progressCallback);
|
||||
VerifyCredentialResponse verifyCredential(in byte[] credential, int type, long challenge, int userId);
|
||||
VerifyCredentialResponse verifyTiedProfileChallenge(in byte[] credential, int type, long challenge, int userId);
|
||||
VerifyCredentialResponse verifyCredential(in LockscreenCredential credential, long challenge, int userId);
|
||||
VerifyCredentialResponse verifyTiedProfileChallenge(in LockscreenCredential credential, long challenge, int userId);
|
||||
boolean checkVoldPassword(int userId);
|
||||
@UnsupportedAppUsage
|
||||
boolean havePattern(int userId);
|
||||
@UnsupportedAppUsage
|
||||
boolean havePassword(int userId);
|
||||
byte[] getHashFactor(in byte[] currentCredential, int userId);
|
||||
void setSeparateProfileChallengeEnabled(int userId, boolean enabled, in byte[] managedUserPassword);
|
||||
byte[] getHashFactor(in LockscreenCredential currentCredential, int userId);
|
||||
void setSeparateProfileChallengeEnabled(int userId, boolean enabled, in LockscreenCredential managedUserPassword);
|
||||
boolean getSeparateProfileChallengeEnabled(int userId);
|
||||
void registerStrongAuthTracker(in IStrongAuthTracker tracker);
|
||||
void unregisterStrongAuthTracker(in IStrongAuthTracker tracker);
|
||||
|
||||
@@ -59,10 +59,10 @@ import android.util.SparseLongArray;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import libcore.util.HexEncoding;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import libcore.util.HexEncoding;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.security.MessageDigest;
|
||||
@@ -383,7 +383,7 @@ public class LockPatternUtils {
|
||||
throwIfCalledOnMainThread();
|
||||
try {
|
||||
VerifyCredentialResponse response = getLockSettings().verifyCredential(
|
||||
credential.getCredential(), credential.getType(), challenge, userId);
|
||||
credential, challenge, userId);
|
||||
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||
return response.getPayload();
|
||||
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
|
||||
@@ -413,8 +413,7 @@ public class LockPatternUtils {
|
||||
throwIfCalledOnMainThread();
|
||||
try {
|
||||
VerifyCredentialResponse response = getLockSettings().checkCredential(
|
||||
credential.getCredential(), credential.getType(),
|
||||
userId, wrapCallback(progressCallback));
|
||||
credential, userId, wrapCallback(progressCallback));
|
||||
|
||||
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||
return true;
|
||||
@@ -447,8 +446,7 @@ public class LockPatternUtils {
|
||||
throwIfCalledOnMainThread();
|
||||
try {
|
||||
VerifyCredentialResponse response =
|
||||
getLockSettings().verifyTiedProfileChallenge(
|
||||
credential.getCredential(), credential.getType(), challenge, userId);
|
||||
getLockSettings().verifyTiedProfileChallenge(credential, challenge, userId);
|
||||
|
||||
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||
return response.getPayload();
|
||||
@@ -482,7 +480,7 @@ public class LockPatternUtils {
|
||||
public byte[] getPasswordHistoryHashFactor(@NonNull LockscreenCredential currentPassword,
|
||||
int userId) {
|
||||
try {
|
||||
return getLockSettings().getHashFactor(currentPassword.getCredential(), userId);
|
||||
return getLockSettings().getHashFactor(currentPassword, userId);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "failed to get hash factor", e);
|
||||
return null;
|
||||
@@ -689,9 +687,7 @@ public class LockPatternUtils {
|
||||
|
||||
try {
|
||||
if (!getLockSettings().setLockCredential(
|
||||
newCredential.getCredential(), newCredential.getType(),
|
||||
savedCredential.getCredential(),
|
||||
newCredential.getQuality(), userHandle, allowUntrustedChange)) {
|
||||
newCredential, savedCredential, userHandle, allowUntrustedChange)) {
|
||||
setKeyguardStoredPasswordQuality(currentQuality, userHandle);
|
||||
return false;
|
||||
}
|
||||
@@ -920,17 +916,17 @@ public class LockPatternUtils {
|
||||
*
|
||||
* @param userHandle Managed profile user id
|
||||
* @param enabled True if separate challenge is enabled
|
||||
* @param managedUserPassword Managed profile previous password. Null when {@code enabled} is
|
||||
* @param profilePassword Managed profile previous password. Null when {@code enabled} is
|
||||
* true
|
||||
*/
|
||||
public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled,
|
||||
LockscreenCredential managedUserPassword) {
|
||||
LockscreenCredential profilePassword) {
|
||||
if (!isManagedProfile(userHandle)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
getLockSettings().setSeparateProfileChallengeEnabled(userHandle, enabled,
|
||||
managedUserPassword.getCredential());
|
||||
profilePassword);
|
||||
reportEnabledTrustAgentsChanged(userHandle);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Couldn't update work profile challenge enabled");
|
||||
@@ -1543,8 +1539,8 @@ public class LockPatternUtils {
|
||||
* @param userHandle The user who's lock credential to be changed
|
||||
* @return {@code true} if the operation is successful.
|
||||
*/
|
||||
public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
|
||||
byte[] token, int userHandle) {
|
||||
public boolean setLockCredentialWithToken(@NonNull LockscreenCredential credential,
|
||||
long tokenHandle, byte[] token, int userHandle) {
|
||||
if (!hasSecureLockScreen()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This operation requires the lock screen feature.");
|
||||
@@ -1556,9 +1552,8 @@ public class LockPatternUtils {
|
||||
setKeyguardStoredPasswordQuality(credential.getQuality(), userHandle);
|
||||
|
||||
try {
|
||||
if (!localService.setLockCredentialWithToken(credential.getCredential(),
|
||||
credential.getType(),
|
||||
tokenHandle, token, credential.getQuality(), userHandle)) {
|
||||
if (!localService.setLockCredentialWithToken(
|
||||
credential, tokenHandle, token, userHandle)) {
|
||||
setKeyguardStoredPasswordQuality(currentQuality, userHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ public abstract class LockSettingsInternal {
|
||||
*
|
||||
* @return true if password is set.
|
||||
*/
|
||||
public abstract boolean setLockCredentialWithToken(byte[] credential, int type,
|
||||
long tokenHandle, byte[] token, int requestedQuality, int userId);
|
||||
public abstract boolean setLockCredentialWithToken(LockscreenCredential credential,
|
||||
long tokenHandle, byte[] token, int userId);
|
||||
|
||||
public abstract boolean unlockUserWithToken(long tokenHandle, byte[] token, int userId);
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.widget;
|
||||
|
||||
/**
|
||||
* A class representing a lockscreen credential.
|
||||
*/
|
||||
parcelable LockscreenCredential;
|
||||
|
||||
@@ -110,6 +110,18 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
|
||||
charSequenceToByteArray(password));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LockscreenCredential object representing a managed password for profile with
|
||||
* unified challenge. This credentiall will have type {@code CREDENTIAL_TYPE_PASSWORD} for now.
|
||||
* TODO: consider add a new credential type for this. This can then supersede the
|
||||
* isLockTiedToParent argument in various places in LSS.
|
||||
*/
|
||||
public static LockscreenCredential createManagedPassword(@NonNull byte[] password) {
|
||||
return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
|
||||
PASSWORD_QUALITY_ALPHABETIC,
|
||||
Arrays.copyOf(password, password.length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LockscreenCredential object representing the given numeric PIN.
|
||||
*/
|
||||
@@ -143,18 +155,6 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a LockscreenCredential object based on raw credential and type
|
||||
* TODO: Remove once LSS.setUserPasswordMetrics accepts a LockscreenCredential
|
||||
*/
|
||||
public static LockscreenCredential createRaw(int type, byte[] credential) {
|
||||
if (type == CREDENTIAL_TYPE_NONE) {
|
||||
return createNone();
|
||||
} else {
|
||||
return new LockscreenCredential(type, PASSWORD_QUALITY_UNSPECIFIED, credential);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureNotZeroized() {
|
||||
Preconditions.checkState(mCredential != null, "Credential is already zeroized");
|
||||
}
|
||||
|
||||
@@ -116,7 +116,6 @@ import com.android.internal.util.Preconditions;
|
||||
import com.android.internal.widget.ICheckCredentialProgressCallback;
|
||||
import com.android.internal.widget.ILockSettings;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockPatternUtils.CredentialType;
|
||||
import com.android.internal.widget.LockSettingsInternal;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.internal.widget.VerifyCredentialResponse;
|
||||
@@ -317,14 +316,34 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private LockscreenCredential generateRandomProfilePassword() {
|
||||
byte[] randomLockSeed = new byte[] {};
|
||||
try {
|
||||
randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40);
|
||||
char[] newPasswordChars = HexEncoding.encode(randomLockSeed);
|
||||
byte[] newPassword = new byte[newPasswordChars.length];
|
||||
for (int i = 0; i < newPasswordChars.length; i++) {
|
||||
newPassword[i] = (byte) newPasswordChars[i];
|
||||
}
|
||||
LockscreenCredential credential =
|
||||
LockscreenCredential.createManagedPassword(newPassword);
|
||||
Arrays.fill(newPasswordChars, '\u0000');
|
||||
Arrays.fill(newPassword, (byte) 0);
|
||||
Arrays.fill(randomLockSeed, (byte) 0);
|
||||
return credential;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("Fail to tie managed profile", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tie managed profile to primary profile if it is in unified mode and not tied before.
|
||||
*
|
||||
* @param managedUserId Managed profile user Id
|
||||
* @param managedUserPassword Managed profile original password (when it has separated lock).
|
||||
* NULL when it does not have a separated lock before.
|
||||
*/
|
||||
public void tieManagedProfileLockIfNecessary(int managedUserId, byte[] managedUserPassword) {
|
||||
public void tieManagedProfileLockIfNecessary(int managedUserId,
|
||||
LockscreenCredential managedUserPassword) {
|
||||
if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + managedUserId);
|
||||
// Only for managed profile
|
||||
if (!mUserManager.getUserInfo(managedUserId).isManagedProfile()) {
|
||||
@@ -356,27 +375,15 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) Slog.v(TAG, "Tie managed profile to parent now!");
|
||||
byte[] randomLockSeed = new byte[] {};
|
||||
try {
|
||||
randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40);
|
||||
char[] newPasswordChars = HexEncoding.encode(randomLockSeed);
|
||||
byte[] newPassword = new byte[newPasswordChars.length];
|
||||
for (int i = 0; i < newPasswordChars.length; i++) {
|
||||
newPassword[i] = (byte) newPasswordChars[i];
|
||||
}
|
||||
Arrays.fill(newPasswordChars, '\u0000');
|
||||
try (LockscreenCredential unifiedProfilePassword = generateRandomProfilePassword()) {
|
||||
final int quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
|
||||
setLockCredentialInternal(newPassword, CREDENTIAL_TYPE_PASSWORD, managedUserPassword,
|
||||
quality, managedUserId, false, /* isLockTiedToParent= */ true);
|
||||
setLockCredentialInternal(unifiedProfilePassword, managedUserPassword, managedUserId,
|
||||
false, /* isLockTiedToParent= */ true);
|
||||
// We store a private credential for the managed user that's unlocked by the primary
|
||||
// account holder's credential. As such, the user will never be prompted to enter this
|
||||
// password directly, so we always store a password.
|
||||
// account holder's credential. As such, the user will never be prompted to enter
|
||||
// this password directly, so we always store a password.
|
||||
setLong(LockPatternUtils.PASSWORD_TYPE_KEY, quality, managedUserId);
|
||||
tieProfileLockToParent(managedUserId, newPassword);
|
||||
Arrays.fill(newPassword, (byte) 0);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Slog.e(TAG, "Fail to tie managed profile", e);
|
||||
// Nothing client can do to fix this issue, so we do not throw exception out
|
||||
tieProfileLockToParent(managedUserId, unifiedProfilePassword);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,7 +689,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
hideEncryptionNotification(new UserHandle(userId));
|
||||
|
||||
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
|
||||
tieManagedProfileLockIfNecessary(userId, null);
|
||||
tieManagedProfileLockIfNecessary(userId, LockscreenCredential.createNone());
|
||||
}
|
||||
|
||||
// If the user doesn't have a credential, try and derive their secret for the
|
||||
@@ -704,10 +711,9 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
|
||||
final long handle = getSyntheticPasswordHandleLocked(userId);
|
||||
final byte[] noCredential = null;
|
||||
AuthenticationResult result =
|
||||
mSpManager.unwrapPasswordBasedSyntheticPassword(
|
||||
getGateKeeperService(), handle, noCredential, userId, null);
|
||||
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);
|
||||
@@ -1031,21 +1037,22 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
@Override
|
||||
public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
|
||||
byte[] managedUserPassword) {
|
||||
LockscreenCredential managedUserPassword) {
|
||||
checkWritePermission(userId);
|
||||
if (!mLockPatternUtils.hasSecureLockScreen()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This operation requires secure lock screen feature.");
|
||||
}
|
||||
synchronized (mSeparateChallengeLock) {
|
||||
setSeparateProfileChallengeEnabledLocked(userId, enabled, managedUserPassword);
|
||||
setSeparateProfileChallengeEnabledLocked(userId, enabled, managedUserPassword != null
|
||||
? managedUserPassword : LockscreenCredential.createNone());
|
||||
}
|
||||
notifySeparateProfileChallengeChanged(userId);
|
||||
}
|
||||
|
||||
@GuardedBy("mSeparateChallengeLock")
|
||||
private void setSeparateProfileChallengeEnabledLocked(@UserIdInt int userId,
|
||||
boolean enabled, byte[] managedUserPassword) {
|
||||
boolean enabled, LockscreenCredential managedUserPassword) {
|
||||
final boolean old = getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
|
||||
setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
|
||||
try {
|
||||
@@ -1205,8 +1212,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
ks.unlock(userHandle, passwordString);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected byte[] getDecryptedPasswordForTiedProfile(int userId)
|
||||
@VisibleForTesting /** Note: this method is overridden in unit tests */
|
||||
protected LockscreenCredential getDecryptedPasswordForTiedProfile(int userId)
|
||||
throws KeyStoreException, UnrecoverableKeyException,
|
||||
NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
|
||||
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException,
|
||||
@@ -1230,7 +1237,10 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv));
|
||||
decryptionResult = cipher.doFinal(encryptedPassword);
|
||||
return decryptionResult;
|
||||
LockscreenCredential credential = LockscreenCredential.createManagedPassword(
|
||||
decryptionResult);
|
||||
Arrays.fill(decryptionResult, (byte) 0);
|
||||
return credential;
|
||||
}
|
||||
|
||||
private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated,
|
||||
@@ -1238,7 +1248,6 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
@Nullable ArrayList<PendingResetLockout> resetLockouts) {
|
||||
try {
|
||||
doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
|
||||
CREDENTIAL_TYPE_PASSWORD,
|
||||
challengeType, challenge, profileHandle, null /* progressCallback */,
|
||||
resetLockouts);
|
||||
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
|
||||
@@ -1351,11 +1360,11 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
&& mUserManager.isUserRunning(userInfo.id);
|
||||
}
|
||||
|
||||
private Map<Integer, byte[]> getDecryptedPasswordsForAllTiedProfiles(int userId) {
|
||||
private Map<Integer, LockscreenCredential> getDecryptedPasswordsForAllTiedProfiles(int userId) {
|
||||
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
|
||||
return null;
|
||||
}
|
||||
Map<Integer, byte[]> result = new ArrayMap<Integer, byte[]>();
|
||||
Map<Integer, LockscreenCredential> result = new ArrayMap<>();
|
||||
final List<UserInfo> profiles = mUserManager.getProfiles(userId);
|
||||
final int size = profiles.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
@@ -1393,7 +1402,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
* terminates when the user is a managed profile.
|
||||
*/
|
||||
private void synchronizeUnifiedWorkChallengeForProfiles(int userId,
|
||||
Map<Integer, byte[]> profilePasswordMap) {
|
||||
Map<Integer, LockscreenCredential> profilePasswordMap) {
|
||||
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
|
||||
return;
|
||||
}
|
||||
@@ -1408,20 +1417,23 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
continue;
|
||||
}
|
||||
if (isSecure) {
|
||||
tieManagedProfileLockIfNecessary(managedUserId, null);
|
||||
tieManagedProfileLockIfNecessary(managedUserId,
|
||||
LockscreenCredential.createNone());
|
||||
} else {
|
||||
// We use cached work profile password computed before clearing the parent's
|
||||
// credential, otherwise they get lost
|
||||
if (profilePasswordMap != null && profilePasswordMap.containsKey(managedUserId)) {
|
||||
setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE,
|
||||
if (profilePasswordMap != null
|
||||
&& profilePasswordMap.containsKey(managedUserId)) {
|
||||
setLockCredentialInternal(LockscreenCredential.createNone(),
|
||||
profilePasswordMap.get(managedUserId),
|
||||
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId,
|
||||
managedUserId,
|
||||
false, /* isLockTiedToParent= */ true);
|
||||
} else {
|
||||
Slog.wtf(TAG, "clear tied profile challenges, but no password supplied.");
|
||||
// Supplying null here would lead to untrusted credential change
|
||||
setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE, null,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId,
|
||||
// Attempt an untrusted reset by supplying an empty credential.
|
||||
setLockCredentialInternal(LockscreenCredential.createNone(),
|
||||
LockscreenCredential.createNone(),
|
||||
managedUserId,
|
||||
true, /* isLockTiedToParent= */ true);
|
||||
}
|
||||
mStorage.removeChildProfileLock(managedUserId);
|
||||
@@ -1445,8 +1457,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
* Send credentials for user {@code userId} to {@link RecoverableKeyStoreManager} during an
|
||||
* unlock operation.
|
||||
*/
|
||||
private void sendCredentialsOnUnlockIfRequired(
|
||||
int credentialType, @NonNull byte[] credential, int userId) {
|
||||
private void sendCredentialsOnUnlockIfRequired(LockscreenCredential credential, int userId) {
|
||||
// Don't send credentials during the factory reset protection flow.
|
||||
if (userId == USER_FRP) {
|
||||
return;
|
||||
@@ -1459,10 +1470,12 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return;
|
||||
}
|
||||
|
||||
// RecoverableKeyStoreManager expects null for empty credential.
|
||||
final byte[] secret = credential.isNone() ? null : credential.getCredential();
|
||||
// Send credentials for the user and any child profiles that share its lock screen.
|
||||
for (int profileId : getProfilesWithSameLockScreen(userId)) {
|
||||
mRecoverableKeyStoreManager.lockScreenSecretAvailable(
|
||||
credentialType, credential, profileId);
|
||||
credential.getType(), secret, profileId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1471,7 +1484,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
* credentials are set/changed.
|
||||
*/
|
||||
private void sendCredentialsOnChangeIfRequired(
|
||||
int credentialType, byte[] credential, int userId, boolean isLockTiedToParent) {
|
||||
LockscreenCredential credential, int userId, boolean isLockTiedToParent) {
|
||||
// A profile whose lock screen is being tied to its parent's will either have a randomly
|
||||
// generated credential (creation) or null (removal). We rely on the parent to send its
|
||||
// credentials for the profile in both cases as it stores the unified lock credential.
|
||||
@@ -1479,10 +1492,12 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return;
|
||||
}
|
||||
|
||||
// RecoverableKeyStoreManager expects null for empty credential.
|
||||
final byte[] secret = credential.isNone() ? null : credential.getCredential();
|
||||
// Send credentials for the user and any child profiles that share its lock screen.
|
||||
for (int profileId : getProfilesWithSameLockScreen(userId)) {
|
||||
mRecoverableKeyStoreManager.lockScreenSecretChanged(
|
||||
credentialType, credential, profileId);
|
||||
credential.getType(), secret, profileId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1505,9 +1520,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
// This method should be called by LockPatternUtil only, all internal methods in this class
|
||||
// should call setLockCredentialInternal.
|
||||
@Override
|
||||
public boolean setLockCredential(byte[] credential, int type,
|
||||
byte[] savedCredential, int requestedQuality, int userId,
|
||||
boolean allowUntrustedChange) {
|
||||
public boolean setLockCredential(LockscreenCredential credential,
|
||||
LockscreenCredential savedCredential, int userId, boolean allowUntrustedChange) {
|
||||
|
||||
if (!mLockPatternUtils.hasSecureLockScreen()) {
|
||||
throw new UnsupportedOperationException(
|
||||
@@ -1515,11 +1529,11 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
checkWritePermission(userId);
|
||||
synchronized (mSeparateChallengeLock) {
|
||||
if (!setLockCredentialInternal(credential, type, savedCredential, requestedQuality,
|
||||
if (!setLockCredentialInternal(credential, savedCredential,
|
||||
userId, allowUntrustedChange, /* isLockTiedToParent= */ false)) {
|
||||
return false;
|
||||
}
|
||||
setSeparateProfileChallengeEnabledLocked(userId, true, null);
|
||||
setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
|
||||
notifyPasswordChanged(userId);
|
||||
}
|
||||
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
|
||||
@@ -1531,51 +1545,40 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param savedCredential if the user is a managed 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.
|
||||
* @param isLockTiedToParent is {@code true} if {@code userId} is a profile and its new
|
||||
* credentials are being tied to its parent's credentials.
|
||||
*/
|
||||
private boolean setLockCredentialInternal(byte[] credential, @CredentialType int credentialType,
|
||||
byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange,
|
||||
boolean isLockTiedToParent) {
|
||||
// Normalize savedCredential and credential such that empty string is always represented
|
||||
// as null.
|
||||
if (savedCredential == null || savedCredential.length == 0) {
|
||||
savedCredential = null;
|
||||
}
|
||||
if (credential == null || credential.length == 0) {
|
||||
credential = null;
|
||||
}
|
||||
private boolean setLockCredentialInternal(LockscreenCredential credential,
|
||||
LockscreenCredential savedCredential, int userId,
|
||||
boolean allowUntrustedChange, boolean isLockTiedToParent) {
|
||||
Preconditions.checkNotNull(credential);
|
||||
Preconditions.checkNotNull(savedCredential);
|
||||
synchronized (mSpManager) {
|
||||
if (isSyntheticPasswordBasedCredentialLocked(userId)) {
|
||||
return spBasedSetLockCredentialInternalLocked(credential, credentialType,
|
||||
savedCredential, requestedQuality, userId, allowUntrustedChange,
|
||||
isLockTiedToParent);
|
||||
return spBasedSetLockCredentialInternalLocked(credential, savedCredential, userId,
|
||||
allowUntrustedChange, isLockTiedToParent);
|
||||
}
|
||||
}
|
||||
|
||||
if (credentialType == CREDENTIAL_TYPE_NONE) {
|
||||
if (credential != null) {
|
||||
Slog.wtf(TAG, "CredentialType is none, but credential is non-null.");
|
||||
}
|
||||
if (credential.isNone()) {
|
||||
clearUserKeyProtection(userId);
|
||||
gateKeeperClearSecureUserId(userId);
|
||||
mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId);
|
||||
setKeystorePassword(null, userId);
|
||||
fixateNewestUserKeyAuth(userId);
|
||||
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
|
||||
setUserPasswordMetrics(CREDENTIAL_TYPE_NONE, null, userId);
|
||||
sendCredentialsOnChangeIfRequired(
|
||||
credentialType, credential, userId, isLockTiedToParent);
|
||||
setUserPasswordMetrics(LockscreenCredential.createNone(), userId);
|
||||
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
|
||||
return true;
|
||||
}
|
||||
if (credential == null) {
|
||||
throw new IllegalArgumentException("Null credential with mismatched credential type");
|
||||
}
|
||||
|
||||
CredentialHash currentHandle = mStorage.readCredentialHash(userId);
|
||||
if (isManagedProfileWithUnifiedLock(userId)) {
|
||||
// get credential from keystore when managed profile has unified lock
|
||||
if (savedCredential == null) {
|
||||
if (savedCredential.isNone()) {
|
||||
try {
|
||||
savedCredential = getDecryptedPasswordForTiedProfile(userId);
|
||||
} catch (FileNotFoundException e) {
|
||||
@@ -1589,47 +1592,45 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
} else {
|
||||
if (currentHandle.hash == null) {
|
||||
if (savedCredential != null) {
|
||||
if (!savedCredential.isNone()) {
|
||||
Slog.w(TAG, "Saved credential provided, but none stored");
|
||||
}
|
||||
savedCredential = null;
|
||||
savedCredential.close();
|
||||
savedCredential = LockscreenCredential.createNone();
|
||||
}
|
||||
}
|
||||
synchronized (mSpManager) {
|
||||
if (shouldMigrateToSyntheticPasswordLocked(userId)) {
|
||||
initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential,
|
||||
currentHandle.type, requestedQuality, userId);
|
||||
return spBasedSetLockCredentialInternalLocked(credential, credentialType,
|
||||
savedCredential, requestedQuality, userId, allowUntrustedChange,
|
||||
isLockTiedToParent);
|
||||
initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential, userId);
|
||||
return spBasedSetLockCredentialInternalLocked(credential, savedCredential, userId,
|
||||
allowUntrustedChange, isLockTiedToParent);
|
||||
}
|
||||
}
|
||||
if (DEBUG) Slog.d(TAG, "setLockCredentialInternal: user=" + userId);
|
||||
byte[] enrolledHandle = enrollCredential(currentHandle.hash, savedCredential, credential,
|
||||
userId);
|
||||
byte[] enrolledHandle = enrollCredential(currentHandle.hash,
|
||||
savedCredential.getCredential(), credential.getCredential(), userId);
|
||||
if (enrolledHandle == null) {
|
||||
Slog.w(TAG, String.format("Failed to enroll %s: incorrect credential",
|
||||
credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
|
||||
credential.isPattern() ? "pattern" : "password"));
|
||||
return false;
|
||||
}
|
||||
CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType);
|
||||
CredentialHash willStore = CredentialHash.create(enrolledHandle, credential.getType());
|
||||
mStorage.writeCredentialHash(willStore, userId);
|
||||
// push new secret and auth token to vold
|
||||
GateKeeperResponse gkResponse;
|
||||
try {
|
||||
gkResponse = getGateKeeperService().verifyChallenge(userId, 0, willStore.hash,
|
||||
credential);
|
||||
credential.getCredential());
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException("Failed to verify current credential", e);
|
||||
}
|
||||
setUserKeyProtection(userId, credential, convertResponse(gkResponse));
|
||||
fixateNewestUserKeyAuth(userId);
|
||||
// Refresh the auth token
|
||||
doVerifyCredential(credential, credentialType, CHALLENGE_FROM_CALLER, 0, userId,
|
||||
doVerifyCredential(credential, CHALLENGE_FROM_CALLER, 0, userId,
|
||||
null /* progressCallback */);
|
||||
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
|
||||
sendCredentialsOnChangeIfRequired(
|
||||
credentialType, credential, userId, isLockTiedToParent);
|
||||
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1637,8 +1638,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return VerifyCredentialResponse.fromGateKeeperResponse(gateKeeperResponse);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void tieProfileLockToParent(int userId, byte[] password) {
|
||||
@VisibleForTesting /** Note: this method is overridden in unit tests */
|
||||
protected void tieProfileLockToParent(int userId, LockscreenCredential password) {
|
||||
if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
|
||||
byte[] encryptionResult;
|
||||
byte[] iv;
|
||||
@@ -1673,7 +1674,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
|
||||
+ KeyProperties.ENCRYPTION_PADDING_NONE);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey);
|
||||
encryptionResult = cipher.doFinal(password);
|
||||
encryptionResult = cipher.doFinal(password.getCredential());
|
||||
iv = cipher.getIV();
|
||||
} finally {
|
||||
// The original key can now be discarded.
|
||||
@@ -1728,7 +1729,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
addUserKeyAuth(userId, null, key);
|
||||
}
|
||||
|
||||
private void setUserKeyProtection(int userId, byte[] credential, VerifyCredentialResponse vcr) {
|
||||
private void setUserKeyProtection(int userId, LockscreenCredential credential,
|
||||
VerifyCredentialResponse vcr) {
|
||||
if (DEBUG) Slog.d(TAG, "setUserKeyProtection: user=" + userId);
|
||||
if (vcr == null) {
|
||||
throw new IllegalArgumentException("Null response verifying a credential we just set");
|
||||
@@ -1749,7 +1751,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
addUserKeyAuth(userId, null, null);
|
||||
}
|
||||
|
||||
private static byte[] secretFromCredential(byte[] credential) {
|
||||
private static byte[] secretFromCredential(LockscreenCredential credential) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
||||
// Personalize the hash
|
||||
@@ -1757,7 +1759,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
// Pad it to the block size of the hash function
|
||||
personalization = Arrays.copyOf(personalization, 128);
|
||||
digest.update(personalization);
|
||||
digest.update(credential);
|
||||
digest.update(credential.getCredential());
|
||||
return digest.digest();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("NoSuchAlgorithmException for SHA-512");
|
||||
@@ -1815,7 +1817,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
checkWritePermission(userId);
|
||||
if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId);
|
||||
int managedUserId = -1;
|
||||
byte[] managedUserDecryptedPassword = null;
|
||||
LockscreenCredential managedUserDecryptedPassword = null;
|
||||
final List<UserInfo> profiles = mUserManager.getProfiles(userId);
|
||||
for (UserInfo pi : profiles) {
|
||||
// Unlock managed profile with unified lock
|
||||
@@ -1852,30 +1854,30 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
tieProfileLockToParent(managedUserId, managedUserDecryptedPassword);
|
||||
}
|
||||
}
|
||||
if (managedUserDecryptedPassword != null && managedUserDecryptedPassword.length > 0) {
|
||||
Arrays.fill(managedUserDecryptedPassword, (byte) 0);
|
||||
if (managedUserDecryptedPassword != null) {
|
||||
managedUserDecryptedPassword.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerifyCredentialResponse checkCredential(byte[] credential, int type, int userId,
|
||||
public VerifyCredentialResponse checkCredential(LockscreenCredential credential, int userId,
|
||||
ICheckCredentialProgressCallback progressCallback) {
|
||||
checkPasswordReadPermission(userId);
|
||||
return doVerifyCredential(credential, type, CHALLENGE_NONE, 0, userId, progressCallback);
|
||||
return doVerifyCredential(credential, CHALLENGE_NONE, 0, userId, progressCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerifyCredentialResponse verifyCredential(byte[] credential, int type, long challenge,
|
||||
int userId) {
|
||||
public VerifyCredentialResponse verifyCredential(LockscreenCredential credential,
|
||||
long challenge, int userId) {
|
||||
checkPasswordReadPermission(userId);
|
||||
return doVerifyCredential(credential, type, CHALLENGE_FROM_CALLER, challenge, userId,
|
||||
return doVerifyCredential(credential, CHALLENGE_FROM_CALLER, challenge, userId,
|
||||
null /* progressCallback */);
|
||||
}
|
||||
|
||||
private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType,
|
||||
private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
|
||||
@ChallengeType int challengeType, long challenge, int userId,
|
||||
ICheckCredentialProgressCallback progressCallback) {
|
||||
return doVerifyCredential(credential, credentialType, challengeType, challenge, userId,
|
||||
return doVerifyCredential(credential, challengeType, challenge, userId,
|
||||
progressCallback, null /* resetLockouts */);
|
||||
}
|
||||
|
||||
@@ -1883,11 +1885,11 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
* Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
|
||||
* format.
|
||||
*/
|
||||
private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType,
|
||||
private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
|
||||
@ChallengeType int challengeType, long challenge, int userId,
|
||||
ICheckCredentialProgressCallback progressCallback,
|
||||
@Nullable ArrayList<PendingResetLockout> resetLockouts) {
|
||||
if (credential == null || credential.length == 0) {
|
||||
if (credential == null || credential.isNone()) {
|
||||
throw new IllegalArgumentException("Credential can't be null or empty");
|
||||
}
|
||||
if (userId == USER_FRP && Settings.Global.getInt(mContext.getContentResolver(),
|
||||
@@ -1896,12 +1898,12 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return VerifyCredentialResponse.ERROR;
|
||||
}
|
||||
VerifyCredentialResponse response = null;
|
||||
response = spBasedDoVerifyCredential(credential, credentialType, challengeType, challenge,
|
||||
response = spBasedDoVerifyCredential(credential, challengeType, challenge,
|
||||
userId, progressCallback, resetLockouts);
|
||||
// The user employs synthetic password based credential.
|
||||
if (response != null) {
|
||||
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||
sendCredentialsOnUnlockIfRequired(credentialType, credential, userId);
|
||||
sendCredentialsOnUnlockIfRequired(credential, userId);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -1912,9 +1914,9 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
|
||||
final CredentialHash storedHash = mStorage.readCredentialHash(userId);
|
||||
if (storedHash.type != credentialType) {
|
||||
if (storedHash.type != credential.getType()) {
|
||||
Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??"
|
||||
+ " stored: " + storedHash.type + " passed in: " + credentialType);
|
||||
+ " stored: " + storedHash.type + " passed in: " + credential.getType());
|
||||
return VerifyCredentialResponse.ERROR;
|
||||
}
|
||||
|
||||
@@ -1929,7 +1931,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerifyCredentialResponse verifyTiedProfileChallenge(byte[] credential, int type,
|
||||
public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential,
|
||||
long challenge, int userId) {
|
||||
checkPasswordReadPermission(userId);
|
||||
if (!isManagedProfileWithUnifiedLock(userId)) {
|
||||
@@ -1939,7 +1941,6 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
// Unlock parent by using parent's challenge
|
||||
final VerifyCredentialResponse parentResponse = doVerifyCredential(
|
||||
credential,
|
||||
type,
|
||||
CHALLENGE_FROM_CALLER,
|
||||
challenge,
|
||||
parentProfileId,
|
||||
@@ -1952,7 +1953,6 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
try {
|
||||
// Unlock work profile, and work profile with unified lock must use password only
|
||||
return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId),
|
||||
CREDENTIAL_TYPE_PASSWORD,
|
||||
CHALLENGE_FROM_CALLER,
|
||||
challenge,
|
||||
userId, null /* progressCallback */);
|
||||
@@ -1971,15 +1971,14 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
* hash to GK.
|
||||
*/
|
||||
private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
|
||||
byte[] credential, @ChallengeType int challengeType, long challenge,
|
||||
LockscreenCredential credential, @ChallengeType int challengeType, long challenge,
|
||||
ICheckCredentialProgressCallback progressCallback) {
|
||||
if ((storedHash == null || storedHash.hash.length == 0)
|
||||
&& (credential == null || credential.length == 0)) {
|
||||
if ((storedHash == null || storedHash.hash.length == 0) && credential.isNone()) {
|
||||
// don't need to pass empty credentials to GateKeeper
|
||||
return VerifyCredentialResponse.OK;
|
||||
}
|
||||
|
||||
if (storedHash == null || credential == null || credential.length == 0) {
|
||||
if (storedHash == null || storedHash.hash.length == 0 || credential.isNone()) {
|
||||
return VerifyCredentialResponse.ERROR;
|
||||
}
|
||||
|
||||
@@ -1989,8 +1988,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
GateKeeperResponse gateKeeperResponse;
|
||||
try {
|
||||
gateKeeperResponse = getGateKeeperService()
|
||||
.verifyChallenge(userId, challenge, storedHash.hash, credential);
|
||||
gateKeeperResponse = getGateKeeperService().verifyChallenge(
|
||||
userId, challenge, storedHash.hash, credential.getCredential());
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "gatekeeper verify failed", e);
|
||||
gateKeeperResponse = GateKeeperResponse.ERROR;
|
||||
@@ -2009,8 +2008,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
Log.w(TAG, "progressCallback throws exception", e);
|
||||
}
|
||||
}
|
||||
setUserPasswordMetrics(storedHash.type, credential, userId);
|
||||
unlockKeystore(credential, userId);
|
||||
setUserPasswordMetrics(credential, userId);
|
||||
unlockKeystore(credential.getCredential(), userId);
|
||||
|
||||
Slog.i(TAG, "Unlocking user " + userId + " with token length "
|
||||
+ response.getPayload().length);
|
||||
@@ -2019,27 +2018,22 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
if (isManagedProfileWithSeparatedLock(userId)) {
|
||||
setDeviceUnlockedForUser(userId);
|
||||
}
|
||||
int reEnrollQuality = storedHash.type == CREDENTIAL_TYPE_PATTERN
|
||||
? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
|
||||
: DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
|
||||
/* TODO(roosa): keep the same password quality */;
|
||||
if (shouldReEnroll) {
|
||||
setLockCredentialInternal(credential, storedHash.type, credential,
|
||||
reEnrollQuality, userId, false, /* isLockTiedToParent= */ false);
|
||||
setLockCredentialInternal(credential, credential,
|
||||
userId, false, /* 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, storedHash.type, reEnrollQuality,
|
||||
userId);
|
||||
storedHash.hash, credential, userId);
|
||||
activateEscrowTokens(auth, userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Use credentials to create recoverable keystore snapshot.
|
||||
sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId);
|
||||
sendCredentialsOnUnlockIfRequired(credential, userId);
|
||||
|
||||
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
|
||||
if (response.getTimeout() > 0) {
|
||||
@@ -2055,12 +2049,9 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
* when the user is authenticating or when a new password is being set. In comparison,
|
||||
* {@link #notifyPasswordChanged} only needs to be called when the user changes the password.
|
||||
*/
|
||||
private void setUserPasswordMetrics(@CredentialType int credentialType, byte[] password,
|
||||
@UserIdInt int userHandle) {
|
||||
private void setUserPasswordMetrics(LockscreenCredential password, @UserIdInt int userHandle) {
|
||||
synchronized (this) {
|
||||
mUserPasswordMetrics.put(userHandle,
|
||||
PasswordMetrics.computeForCredential(
|
||||
LockscreenCredential.createRaw(credentialType, password)));
|
||||
mUserPasswordMetrics.put(userHandle, PasswordMetrics.computeForCredential(password));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2089,6 +2080,14 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
});
|
||||
}
|
||||
|
||||
private LockscreenCredential createPattern(String patternString) {
|
||||
final byte[] patternBytes = patternString.getBytes();
|
||||
LockscreenCredential pattern = LockscreenCredential.createPattern(
|
||||
LockPatternUtils.byteArrayToPattern(patternBytes));
|
||||
Arrays.fill(patternBytes, (byte) 0);
|
||||
return pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkVoldPassword(int userId) {
|
||||
if (!mFirstCallToVold) {
|
||||
@@ -2125,21 +2124,21 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
try {
|
||||
if (mLockPatternUtils.isLockPatternEnabled(userId)) {
|
||||
if (checkCredential(password.getBytes(), CREDENTIAL_TYPE_PATTERN,
|
||||
userId, null /* progressCallback */)
|
||||
try (LockscreenCredential credential = createPattern(password)) {
|
||||
if (checkCredential(credential, userId, null /* progressCallback */)
|
||||
.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
|
||||
if (checkCredential(password.getBytes(), CREDENTIAL_TYPE_PASSWORD,
|
||||
userId, null /* progressCallback */)
|
||||
try (LockscreenCredential credential =
|
||||
LockscreenCredential.createPassword(password)) {
|
||||
if (checkCredential(credential, userId, null /* progressCallback */)
|
||||
.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -2524,7 +2523,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
@GuardedBy("mSpManager")
|
||||
@VisibleForTesting
|
||||
protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
|
||||
byte[] credential, int credentialType, int requestedQuality, int userId) {
|
||||
LockscreenCredential credential, int userId) {
|
||||
Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
|
||||
final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(
|
||||
getGateKeeperService(), credentialHash, credential, userId);
|
||||
@@ -2534,8 +2533,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return null;
|
||||
}
|
||||
long handle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
|
||||
credential, credentialType, auth, requestedQuality, userId);
|
||||
if (credential != null) {
|
||||
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.
|
||||
@@ -2592,8 +2591,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM);
|
||||
}
|
||||
|
||||
private VerifyCredentialResponse spBasedDoVerifyCredential(byte[] userCredential,
|
||||
@CredentialType int credentialType, @ChallengeType int challengeType, long challenge,
|
||||
private VerifyCredentialResponse spBasedDoVerifyCredential(LockscreenCredential userCredential,
|
||||
@ChallengeType int challengeType, long challenge,
|
||||
int userId, ICheckCredentialProgressCallback progressCallback,
|
||||
@Nullable ArrayList<PendingResetLockout> resetLockouts) {
|
||||
|
||||
@@ -2601,9 +2600,6 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId + " challengeType=" + challengeType
|
||||
+ " hasEnrolledBiometrics=" + hasEnrolledBiometrics);
|
||||
if (credentialType == CREDENTIAL_TYPE_NONE) {
|
||||
userCredential = null;
|
||||
}
|
||||
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
// TODO: When lockout is handled under the HAL for all biometrics (fingerprint),
|
||||
@@ -2625,15 +2621,16 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
if (userId == USER_FRP) {
|
||||
return mSpManager.verifyFrpCredential(getGateKeeperService(),
|
||||
userCredential, credentialType, progressCallback);
|
||||
userCredential, progressCallback);
|
||||
}
|
||||
|
||||
long handle = getSyntheticPasswordHandleLocked(userId);
|
||||
authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
|
||||
getGateKeeperService(), handle, userCredential, userId, progressCallback);
|
||||
|
||||
if (authResult.credentialType != credentialType) {
|
||||
Slog.e(TAG, "Credential type mismatch.");
|
||||
if (authResult.credentialType != userCredential.getType()) {
|
||||
Slog.e(TAG, String.format("Credential type mismatch: expected %d actual %d",
|
||||
authResult.credentialType, userCredential.getType()));
|
||||
return VerifyCredentialResponse.ERROR;
|
||||
}
|
||||
response = authResult.gkResponse;
|
||||
@@ -2653,7 +2650,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
|
||||
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||
setUserPasswordMetrics(credentialType, userCredential, userId);
|
||||
setUserPasswordMetrics(userCredential, userId);
|
||||
unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId);
|
||||
|
||||
// Do resetLockout / revokeChallenge when all profiles are unlocked
|
||||
@@ -2700,14 +2697,13 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
* added back when new password is set in future.
|
||||
*/
|
||||
@GuardedBy("mSpManager")
|
||||
private long setLockCredentialWithAuthTokenLocked(byte[] credential,
|
||||
@CredentialType int credentialType, AuthenticationToken auth, int requestedQuality,
|
||||
int userId) {
|
||||
private long setLockCredentialWithAuthTokenLocked(LockscreenCredential credential,
|
||||
AuthenticationToken auth, int userId) {
|
||||
if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId);
|
||||
long newHandle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
|
||||
credential, credentialType, auth, requestedQuality, userId);
|
||||
final Map<Integer, byte[]> profilePasswords;
|
||||
if (credential != null) {
|
||||
credential, auth, userId);
|
||||
final Map<Integer, LockscreenCredential> profilePasswords;
|
||||
if (!credential.isNone()) {
|
||||
// not needed by synchronizeUnifiedWorkChallengeForProfiles()
|
||||
profilePasswords = null;
|
||||
|
||||
@@ -2748,11 +2744,11 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
setSyntheticPasswordHandleLocked(newHandle, userId);
|
||||
synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords);
|
||||
|
||||
setUserPasswordMetrics(credentialType, credential, userId);
|
||||
setUserPasswordMetrics(credential, userId);
|
||||
|
||||
if (profilePasswords != null) {
|
||||
for (Map.Entry<Integer, byte[]> entry : profilePasswords.entrySet()) {
|
||||
Arrays.fill(entry.getValue(), (byte) 0);
|
||||
for (Map.Entry<Integer, LockscreenCredential> entry : profilePasswords.entrySet()) {
|
||||
entry.getValue().zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2838,12 +2834,17 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param savedCredential if the user is a managed 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(byte[] credential, int credentialType,
|
||||
byte[] savedCredential, int requestedQuality, int userId,
|
||||
private boolean spBasedSetLockCredentialInternalLocked(LockscreenCredential credential,
|
||||
LockscreenCredential savedCredential, int userId,
|
||||
boolean allowUntrustedChange, boolean isLockTiedToParent) {
|
||||
if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
|
||||
if (isManagedProfileWithUnifiedLock(userId)) {
|
||||
if (savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) {
|
||||
// get credential from keystore when managed profile has unified lock
|
||||
try {
|
||||
savedCredential = getDecryptedPasswordForTiedProfile(userId);
|
||||
@@ -2863,9 +2864,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
AuthenticationToken auth = authResult.authToken;
|
||||
|
||||
// If existing credential is provided, the existing credential must match.
|
||||
if (savedCredential != null && auth == null) {
|
||||
Slog.w(TAG, String.format("Failed to enroll %s: incorrect credential",
|
||||
credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
|
||||
if (!savedCredential.isNone() && auth == null) {
|
||||
Slog.w(TAG, "Failed to enroll: incorrect credential");
|
||||
return false;
|
||||
}
|
||||
boolean untrustedReset = false;
|
||||
@@ -2898,8 +2898,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
// setLockCredentialWithAuthTokenLocked next
|
||||
mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
|
||||
}
|
||||
setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality,
|
||||
userId);
|
||||
setLockCredentialWithAuthTokenLocked(credential, auth, userId);
|
||||
mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
@@ -2908,7 +2907,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
// requestedQuality, userId) instead if we still allow untrusted reset that changes
|
||||
// synthetic password. That would invalidate existing escrow tokens though.
|
||||
}
|
||||
sendCredentialsOnChangeIfRequired(credentialType, credential, userId, isLockTiedToParent);
|
||||
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2919,11 +2918,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
* If user is a managed profile with unified challenge, currentCredential is ignored.
|
||||
*/
|
||||
@Override
|
||||
public byte[] getHashFactor(byte[] currentCredential, int userId) {
|
||||
public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) {
|
||||
checkPasswordReadPermission(userId);
|
||||
if (currentCredential == null || currentCredential.length == 0) {
|
||||
currentCredential = null;
|
||||
}
|
||||
if (isManagedProfileWithUnifiedLock(userId)) {
|
||||
try {
|
||||
currentCredential = getDecryptedPasswordForTiedProfile(userId);
|
||||
@@ -2957,13 +2953,12 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
AuthenticationToken auth = null;
|
||||
if (!isUserSecure(userId)) {
|
||||
if (shouldMigrateToSyntheticPasswordLocked(userId)) {
|
||||
auth = initializeSyntheticPasswordLocked(null, null,
|
||||
CREDENTIAL_TYPE_NONE,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
|
||||
auth = initializeSyntheticPasswordLocked(
|
||||
/* credentialHash */ null, LockscreenCredential.createNone(), userId);
|
||||
} else /* isSyntheticPasswordBasedCredentialLocked(userId) */ {
|
||||
long pwdHandle = getSyntheticPasswordHandleLocked(userId);
|
||||
auth = mSpManager.unwrapPasswordBasedSyntheticPassword(getGateKeeperService(),
|
||||
pwdHandle, null, userId, null).authToken;
|
||||
pwdHandle, LockscreenCredential.createNone(), userId, null).authToken;
|
||||
}
|
||||
}
|
||||
if (isSyntheticPasswordBasedCredentialLocked(userId)) {
|
||||
@@ -3023,21 +3018,21 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setLockCredentialWithToken(byte[] credential, int type, long tokenHandle,
|
||||
byte[] token, int requestedQuality, int userId) {
|
||||
private boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
|
||||
byte[] token, int userId) {
|
||||
boolean result;
|
||||
synchronized (mSpManager) {
|
||||
if (!mSpManager.hasEscrowData(userId)) {
|
||||
throw new SecurityException("Escrow token is disabled on the current user");
|
||||
}
|
||||
result = setLockCredentialWithTokenInternalLocked(credential, type, tokenHandle, token,
|
||||
requestedQuality, userId);
|
||||
result = setLockCredentialWithTokenInternalLocked(
|
||||
credential, tokenHandle, token, userId);
|
||||
}
|
||||
if (result) {
|
||||
synchronized (mSeparateChallengeLock) {
|
||||
setSeparateProfileChallengeEnabledLocked(userId, true, null);
|
||||
setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
|
||||
}
|
||||
if (credential == null) {
|
||||
if (credential.isNone()) {
|
||||
// If clearing credential, unlock the user manually in order to progress user start
|
||||
// Call unlockUser() on a handler thread so no lock is held (either by LSS or by
|
||||
// the caller like DPMS), otherwise it can lead to deadlock.
|
||||
@@ -3050,8 +3045,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
|
||||
@GuardedBy("mSpManager")
|
||||
private boolean setLockCredentialWithTokenInternalLocked(byte[] credential, int type,
|
||||
long tokenHandle, byte[] token, int requestedQuality, int userId) {
|
||||
private boolean setLockCredentialWithTokenInternalLocked(LockscreenCredential credential,
|
||||
long tokenHandle, byte[] token, int userId) {
|
||||
final AuthenticationResult result;
|
||||
result = mSpManager.unwrapTokenBasedSyntheticPassword(
|
||||
getGateKeeperService(), tokenHandle, token, userId);
|
||||
@@ -3067,10 +3062,9 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return false;
|
||||
}
|
||||
// TODO: refactor usage of PASSWORD_TYPE_KEY b/65239740
|
||||
setLong(LockPatternUtils.PASSWORD_TYPE_KEY, requestedQuality, userId);
|
||||
setLong(LockPatternUtils.PASSWORD_TYPE_KEY, credential.getQuality(), userId);
|
||||
long oldHandle = getSyntheticPasswordHandleLocked(userId);
|
||||
setLockCredentialWithAuthTokenLocked(credential, type, result.authToken,
|
||||
requestedQuality, userId);
|
||||
setLockCredentialWithAuthTokenLocked(credential, result.authToken, userId);
|
||||
mSpManager.destroyPasswordBasedSyntheticPassword(oldHandle, userId);
|
||||
|
||||
onAuthTokenKnownForUser(userId, result.authToken);
|
||||
@@ -3298,14 +3292,14 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setLockCredentialWithToken(byte[] credential, int type,
|
||||
long tokenHandle, byte[] token, int requestedQuality, int userId) {
|
||||
public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
|
||||
byte[] token, int userId) {
|
||||
if (!mLockPatternUtils.hasSecureLockScreen()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This operation requires secure lock screen feature.");
|
||||
}
|
||||
return LockSettingsService.this.setLockCredentialWithToken(credential, type,
|
||||
tokenHandle, token, requestedQuality, userId);
|
||||
return LockSettingsService.this.setLockCredentialWithToken(
|
||||
credential, tokenHandle, token, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.widget.ICheckCredentialProgressCallback;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.internal.widget.VerifyCredentialResponse;
|
||||
import com.android.server.locksettings.LockSettingsStorage.PersistentData;
|
||||
|
||||
@@ -469,12 +470,13 @@ public class SyntheticPasswordManager {
|
||||
*
|
||||
*/
|
||||
public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
|
||||
byte[] hash, byte[] credential, int userId) {
|
||||
byte[] hash, LockscreenCredential credential, int userId) {
|
||||
AuthenticationToken result = AuthenticationToken.create();
|
||||
GateKeeperResponse response;
|
||||
if (hash != null) {
|
||||
try {
|
||||
response = gatekeeper.enroll(userId, hash, credential, result.deriveGkPassword());
|
||||
response = gatekeeper.enroll(userId, hash, credential.getCredential(),
|
||||
result.deriveGkPassword());
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException("Failed to enroll credential duing SP init", e);
|
||||
}
|
||||
@@ -638,15 +640,9 @@ public class SyntheticPasswordManager {
|
||||
* @throw IllegalStateException if creation fails.
|
||||
*/
|
||||
public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
|
||||
byte[] credential, int credentialType, AuthenticationToken authToken,
|
||||
int requestedQuality, int userId) {
|
||||
if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
|
||||
credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
|
||||
credential = DEFAULT_PASSWORD;
|
||||
}
|
||||
|
||||
LockscreenCredential credential, AuthenticationToken authToken, int userId) {
|
||||
long handle = generateHandle();
|
||||
PasswordData pwd = PasswordData.create(credentialType);
|
||||
PasswordData pwd = PasswordData.create(credential.getType());
|
||||
byte[] pwdToken = computePasswordToken(credential, pwd);
|
||||
final long sid;
|
||||
final byte[] applicationId;
|
||||
@@ -663,7 +659,7 @@ public class SyntheticPasswordManager {
|
||||
}
|
||||
saveWeaverSlot(weaverSlot, handle, userId);
|
||||
mPasswordSlotManager.markSlotInUse(weaverSlot);
|
||||
synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot);
|
||||
synchronizeWeaverFrpPassword(pwd, credential.getQuality(), userId, weaverSlot);
|
||||
|
||||
pwd.passwordHandle = null;
|
||||
sid = GateKeeper.INVALID_SECURE_USER_ID;
|
||||
@@ -692,7 +688,7 @@ public class SyntheticPasswordManager {
|
||||
sid = sidFromPasswordHandle(pwd.passwordHandle);
|
||||
applicationId = transformUnderSecdiscardable(pwdToken,
|
||||
createSecdiscardable(handle, userId));
|
||||
synchronizeFrpPassword(pwd, requestedQuality, userId);
|
||||
synchronizeFrpPassword(pwd, credential.getQuality(), userId);
|
||||
}
|
||||
saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
|
||||
|
||||
@@ -702,7 +698,7 @@ public class SyntheticPasswordManager {
|
||||
}
|
||||
|
||||
public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
|
||||
byte[] userCredential, int credentialType,
|
||||
LockscreenCredential userCredential,
|
||||
ICheckCredentialProgressCallback progressCallback) {
|
||||
PersistentData persistentData = mStorage.readPersistentDataBlock();
|
||||
if (persistentData.type == PersistentData.TYPE_SP) {
|
||||
@@ -885,11 +881,8 @@ public class SyntheticPasswordManager {
|
||||
* unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
|
||||
*/
|
||||
public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
|
||||
long handle, byte[] credential, int userId,
|
||||
long handle, @NonNull LockscreenCredential credential, int userId,
|
||||
ICheckCredentialProgressCallback progressCallback) {
|
||||
if (credential == null) {
|
||||
credential = DEFAULT_PASSWORD;
|
||||
}
|
||||
AuthenticationResult result = new AuthenticationResult();
|
||||
PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
|
||||
result.credentialType = pwd.passwordType;
|
||||
@@ -1225,7 +1218,8 @@ public class SyntheticPasswordManager {
|
||||
return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
|
||||
}
|
||||
|
||||
private byte[] computePasswordToken(byte[] password, PasswordData data) {
|
||||
private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) {
|
||||
final byte[] password = credential.isNone() ? DEFAULT_PASSWORD : credential.getCredential();
|
||||
return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
|
||||
PASSWORD_TOKEN_LENGTH);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import android.test.AndroidTestCase;
|
||||
import com.android.internal.widget.ILockSettings;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockSettingsInternal;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
|
||||
import com.android.server.wm.WindowManagerInternal;
|
||||
@@ -307,4 +308,22 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
|
||||
protected static void assertArrayNotEquals(byte[] expected, byte[] actual) {
|
||||
assertFalse(Arrays.equals(expected, actual));
|
||||
}
|
||||
|
||||
protected LockscreenCredential newPassword(String password) {
|
||||
return LockscreenCredential.createPasswordOrNone(password);
|
||||
}
|
||||
|
||||
protected LockscreenCredential newPin(String pin) {
|
||||
return LockscreenCredential.createPinOrNone(pin);
|
||||
}
|
||||
|
||||
protected LockscreenCredential newPattern(String pattern) {
|
||||
return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
|
||||
pattern.getBytes()));
|
||||
}
|
||||
|
||||
protected LockscreenCredential nonePassword() {
|
||||
return LockscreenCredential.createNone();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
*/
|
||||
package com.android.server.locksettings;
|
||||
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
|
||||
|
||||
import static com.android.server.testutils.TestUtils.assertExpectException;
|
||||
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
@@ -30,7 +27,7 @@ import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.internal.widget.VerifyCredentialResponse;
|
||||
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -59,56 +56,53 @@ public class CachedSyntheticPasswordTests extends SyntheticPasswordTests {
|
||||
}
|
||||
|
||||
public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException {
|
||||
final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes();
|
||||
final byte[] newPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes();
|
||||
final LockscreenCredential password = newPassword("password");
|
||||
final LockscreenCredential newPassword = newPassword("newpassword");
|
||||
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
|
||||
// clear password
|
||||
mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null,
|
||||
PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, true);
|
||||
mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, true);
|
||||
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
|
||||
// set a new password
|
||||
mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID,
|
||||
false);
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
newPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
}
|
||||
|
||||
public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException {
|
||||
final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes();
|
||||
final byte[] newPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes();
|
||||
final LockscreenCredential password = newPassword("password");
|
||||
final LockscreenCredential newPassword = newPassword("newpassword");
|
||||
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
|
||||
// Untrusted change password
|
||||
mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, true);
|
||||
mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID,
|
||||
true);
|
||||
assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
|
||||
// Verify the password
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
newPassword, 0, PRIMARY_USER_ID).getResponseCode());
|
||||
}
|
||||
|
||||
public void testUntrustedCredentialChangeMaintainsAuthSecret() throws RemoteException {
|
||||
final byte[] password =
|
||||
"testUntrustedCredentialChangeMaintainsAuthSecret-password".getBytes();
|
||||
final byte[] newPassword =
|
||||
"testUntrustedCredentialChangeMaintainsAuthSecret-newpassword".getBytes();
|
||||
final LockscreenCredential password = newPassword("password");
|
||||
final LockscreenCredential newPassword = newPassword("newpassword");
|
||||
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
// Untrusted change password
|
||||
mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, true);
|
||||
mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID,
|
||||
true);
|
||||
|
||||
// Verify the password
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
newPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
|
||||
// Ensure the same secret was passed each time
|
||||
@@ -118,31 +112,30 @@ public class CachedSyntheticPasswordTests extends SyntheticPasswordTests {
|
||||
}
|
||||
|
||||
public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException {
|
||||
final byte[] password =
|
||||
"testUntrustedCredentialChangeBlockedIfSpNotCached-password".getBytes();
|
||||
final byte[] newPassword =
|
||||
"testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword".getBytes();
|
||||
final LockscreenCredential password = newPassword("password");
|
||||
final LockscreenCredential newPassword = newPassword("newpassword");
|
||||
|
||||
// Disable caching for this test
|
||||
enableSpCaching(false);
|
||||
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
flushHandlerTasks();
|
||||
|
||||
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
|
||||
// Untrusted change password
|
||||
assertExpectException(
|
||||
IllegalStateException.class,
|
||||
/* messageRegex= */ "Untrusted credential reset not possible without cached SP",
|
||||
() -> mService.setLockCredential(newPassword,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, true));
|
||||
() -> mService.setLockCredential(newPassword, nonePassword(),
|
||||
PRIMARY_USER_ID, true));
|
||||
assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
|
||||
// Verify the new password doesn't work but the old one still does
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(newPassword,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(
|
||||
newPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
password, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.app.admin.DeviceStateCache;
|
||||
import android.content.Context;
|
||||
import android.hardware.authsecret.V1_0.IAuthSecret;
|
||||
import android.os.Handler;
|
||||
import android.os.Parcel;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserManagerInternal;
|
||||
@@ -31,6 +32,7 @@ import android.security.KeyStore;
|
||||
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.server.ServiceThread;
|
||||
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
|
||||
|
||||
@@ -158,13 +160,16 @@ public class LockSettingsServiceTestable extends LockSettingsService {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tieProfileLockToParent(int userId, byte[] password) {
|
||||
mStorage.writeChildProfileLock(userId, password);
|
||||
protected void tieProfileLockToParent(int userId, LockscreenCredential password) {
|
||||
Parcel parcel = Parcel.obtain();
|
||||
parcel.writeParcelable(password, 0);
|
||||
mStorage.writeChildProfileLock(userId, parcel.marshall());
|
||||
parcel.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getDecryptedPasswordForTiedProfile(int userId) throws FileNotFoundException,
|
||||
KeyPermanentlyInvalidatedException {
|
||||
protected LockscreenCredential getDecryptedPasswordForTiedProfile(int userId)
|
||||
throws FileNotFoundException, KeyPermanentlyInvalidatedException {
|
||||
byte[] storedData = mStorage.readChildProfileLock(userId);
|
||||
if (storedData == null) {
|
||||
throw new FileNotFoundException("Child profile lock file not found");
|
||||
@@ -176,6 +181,13 @@ public class LockSettingsServiceTestable extends LockSettingsService {
|
||||
} catch (RemoteException e) {
|
||||
// shouldn't happen.
|
||||
}
|
||||
return storedData;
|
||||
Parcel parcel = Parcel.obtain();
|
||||
try {
|
||||
parcel.unmarshall(storedData, 0, storedData.length);
|
||||
parcel.setDataPosition(0);
|
||||
return (LockscreenCredential) parcel.readParcelable(null);
|
||||
} finally {
|
||||
parcel.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,6 @@
|
||||
|
||||
package com.android.server.locksettings;
|
||||
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
|
||||
|
||||
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_PATTERN;
|
||||
@@ -39,6 +34,7 @@ import android.service.gatekeeper.GateKeeperResponse;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
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;
|
||||
@@ -61,60 +57,52 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testCreatePasswordPrimaryUser() throws RemoteException {
|
||||
testCreateCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD,
|
||||
PASSWORD_QUALITY_ALPHABETIC);
|
||||
testCreateCredential(PRIMARY_USER_ID, newPassword("password"));
|
||||
}
|
||||
|
||||
public void testCreatePasswordFailsWithoutLockScreen() throws RemoteException {
|
||||
testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "password",
|
||||
CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC);
|
||||
testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, newPassword("password"));
|
||||
}
|
||||
|
||||
public void testCreatePatternPrimaryUser() throws RemoteException {
|
||||
testCreateCredential(PRIMARY_USER_ID, "123456789", CREDENTIAL_TYPE_PATTERN,
|
||||
PASSWORD_QUALITY_SOMETHING);
|
||||
testCreateCredential(PRIMARY_USER_ID, newPattern("123456789"));
|
||||
}
|
||||
|
||||
public void testCreatePatternFailsWithoutLockScreen() throws RemoteException {
|
||||
testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "123456789",
|
||||
CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING);
|
||||
testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, newPattern("123456789"));
|
||||
}
|
||||
|
||||
public void testChangePasswordPrimaryUser() throws RemoteException {
|
||||
testChangeCredentials(PRIMARY_USER_ID, "78963214", CREDENTIAL_TYPE_PATTERN,
|
||||
"asdfghjk", CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC);
|
||||
testChangeCredentials(PRIMARY_USER_ID, newPattern("78963214"), newPassword("asdfghjk"));
|
||||
}
|
||||
|
||||
public void testChangePatternPrimaryUser() throws RemoteException {
|
||||
testChangeCredentials(PRIMARY_USER_ID, "!£$%^&*(())", CREDENTIAL_TYPE_PASSWORD,
|
||||
"1596321", CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING);
|
||||
testChangeCredentials(PRIMARY_USER_ID, newPassword("!£$%^&*(())"), newPattern("1596321"));
|
||||
}
|
||||
|
||||
public void testChangePasswordFailPrimaryUser() throws RemoteException {
|
||||
final long sid = 1234;
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid);
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"), sid);
|
||||
|
||||
assertFalse(mService.setLockCredential("newpwd".getBytes(), CREDENTIAL_TYPE_PASSWORD,
|
||||
"badpwd".getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false));
|
||||
assertVerifyCredentials(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid);
|
||||
assertFalse(mService.setLockCredential(newPassword("newpwd"), newPassword("badpwd"),
|
||||
PRIMARY_USER_ID, false));
|
||||
assertVerifyCredentials(PRIMARY_USER_ID, newPassword("password"), sid);
|
||||
}
|
||||
|
||||
public void testClearPasswordPrimaryUser() throws RemoteException {
|
||||
final String PASSWORD = "password";
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, PASSWORD, CREDENTIAL_TYPE_PASSWORD, 1234);
|
||||
assertTrue(mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD.getBytes(),
|
||||
PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false));
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"), 1234);
|
||||
assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"),
|
||||
PRIMARY_USER_ID, false));
|
||||
assertFalse(mService.havePassword(PRIMARY_USER_ID));
|
||||
assertFalse(mService.havePattern(PRIMARY_USER_ID));
|
||||
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
}
|
||||
|
||||
public void testManagedProfileUnifiedChallenge() throws RemoteException {
|
||||
final String firstUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-1";
|
||||
final String secondUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-2";
|
||||
assertTrue(mService.setLockCredential(firstUnifiedPassword.getBytes(),
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
|
||||
null, PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID, false));
|
||||
final LockscreenCredential firstUnifiedPassword = newPassword("pwd-1");
|
||||
final LockscreenCredential secondUnifiedPassword = newPassword("pwd-2");
|
||||
assertTrue(mService.setLockCredential(firstUnifiedPassword,
|
||||
nonePassword(), PRIMARY_USER_ID, false));
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
|
||||
final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
|
||||
@@ -132,8 +120,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
mGateKeeperService.clearAuthToken(TURNED_OFF_PROFILE_USER_ID);
|
||||
// verify credential
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
firstUnifiedPassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
|
||||
PRIMARY_USER_ID).getResponseCode());
|
||||
firstUnifiedPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
|
||||
// Verify that we have a new auth token for the profile
|
||||
assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
|
||||
@@ -148,16 +136,15 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
*/
|
||||
mStorageManager.setIgnoreBadUnlock(true);
|
||||
// Change primary password and verify that profile SID remains
|
||||
assertTrue(mService.setLockCredential(secondUnifiedPassword.getBytes(),
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, firstUnifiedPassword.getBytes(),
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false));
|
||||
assertTrue(mService.setLockCredential(
|
||||
secondUnifiedPassword, firstUnifiedPassword, PRIMARY_USER_ID, false));
|
||||
mStorageManager.setIgnoreBadUnlock(false);
|
||||
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
|
||||
assertNull(mGateKeeperService.getAuthToken(TURNED_OFF_PROFILE_USER_ID));
|
||||
|
||||
// Clear unified challenge
|
||||
assertTrue(mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
|
||||
secondUnifiedPassword.getBytes(), PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID,
|
||||
assertTrue(mService.setLockCredential(nonePassword(),
|
||||
secondUnifiedPassword, PRIMARY_USER_ID,
|
||||
false));
|
||||
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
|
||||
@@ -165,19 +152,19 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testManagedProfileSeparateChallenge() throws RemoteException {
|
||||
final String primaryPassword = "testManagedProfileSeparateChallenge-primary";
|
||||
final String profilePassword = "testManagedProfileSeparateChallenge-profile";
|
||||
assertTrue(mService.setLockCredential(primaryPassword.getBytes(),
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID, false));
|
||||
final LockscreenCredential primaryPassword = newPassword("primary");
|
||||
final LockscreenCredential profilePassword = newPassword("profile");
|
||||
assertTrue(mService.setLockCredential(primaryPassword,
|
||||
nonePassword(),
|
||||
PRIMARY_USER_ID, false));
|
||||
/* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new
|
||||
* credential as part of verifyCredential() before the new credential is committed in
|
||||
* StorageManager. So we relax the check in our mock StorageManager to allow that.
|
||||
*/
|
||||
mStorageManager.setIgnoreBadUnlock(true);
|
||||
assertTrue(mService.setLockCredential(profilePassword.getBytes(),
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_COMPLEX, MANAGED_PROFILE_USER_ID, false));
|
||||
assertTrue(mService.setLockCredential(profilePassword,
|
||||
nonePassword(),
|
||||
MANAGED_PROFILE_USER_ID, false));
|
||||
mStorageManager.setIgnoreBadUnlock(false);
|
||||
|
||||
final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
|
||||
@@ -190,81 +177,69 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
mGateKeeperService.clearAuthToken(MANAGED_PROFILE_USER_ID);
|
||||
// verify primary credential
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
primaryPassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
|
||||
PRIMARY_USER_ID).getResponseCode());
|
||||
primaryPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
|
||||
|
||||
// verify profile credential
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
profilePassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
|
||||
MANAGED_PROFILE_USER_ID).getResponseCode());
|
||||
profilePassword, 0, MANAGED_PROFILE_USER_ID)
|
||||
.getResponseCode());
|
||||
assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
|
||||
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
|
||||
|
||||
// Change primary credential and make sure we don't affect profile
|
||||
mStorageManager.setIgnoreBadUnlock(true);
|
||||
assertTrue(mService.setLockCredential("pwd".getBytes(),
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
|
||||
primaryPassword.getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false));
|
||||
assertTrue(mService.setLockCredential(
|
||||
newPassword("pwd"), primaryPassword, PRIMARY_USER_ID, false));
|
||||
mStorageManager.setIgnoreBadUnlock(false);
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
profilePassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
|
||||
MANAGED_PROFILE_USER_ID).getResponseCode());
|
||||
profilePassword, 0, MANAGED_PROFILE_USER_ID)
|
||||
.getResponseCode());
|
||||
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
|
||||
}
|
||||
|
||||
public void testSetLockCredential_forPrimaryUser_sendsCredentials() throws Exception {
|
||||
final byte[] password = "password".getBytes();
|
||||
|
||||
assertTrue(mService.setLockCredential(
|
||||
password,
|
||||
CREDENTIAL_TYPE_PASSWORD,
|
||||
null,
|
||||
PASSWORD_QUALITY_ALPHABETIC,
|
||||
newPassword("password"),
|
||||
nonePassword(),
|
||||
PRIMARY_USER_ID,
|
||||
false));
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, password, PRIMARY_USER_ID);
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, "password".getBytes(),
|
||||
PRIMARY_USER_ID);
|
||||
}
|
||||
|
||||
public void testSetLockCredential_forProfileWithSeparateChallenge_sendsCredentials()
|
||||
throws Exception {
|
||||
final byte[] pattern = "12345".getBytes();
|
||||
|
||||
assertTrue(mService.setLockCredential(
|
||||
pattern,
|
||||
CREDENTIAL_TYPE_PATTERN,
|
||||
null,
|
||||
PASSWORD_QUALITY_SOMETHING,
|
||||
newPattern("12345"),
|
||||
nonePassword(),
|
||||
MANAGED_PROFILE_USER_ID,
|
||||
false));
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PATTERN, pattern, MANAGED_PROFILE_USER_ID);
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PATTERN, "12345".getBytes(),
|
||||
MANAGED_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
public void testSetLockCredential_forProfileWithSeparateChallenge_updatesCredentials()
|
||||
throws Exception {
|
||||
final String oldCredential = "12345";
|
||||
final byte[] newCredential = "newPassword".getBytes();
|
||||
initializeStorageWithCredential(
|
||||
MANAGED_PROFILE_USER_ID,
|
||||
oldCredential,
|
||||
CREDENTIAL_TYPE_PATTERN,
|
||||
PASSWORD_QUALITY_SOMETHING);
|
||||
newPattern("12345"),
|
||||
1234);
|
||||
|
||||
assertTrue(mService.setLockCredential(
|
||||
newCredential,
|
||||
CREDENTIAL_TYPE_PASSWORD,
|
||||
oldCredential.getBytes(),
|
||||
PASSWORD_QUALITY_ALPHABETIC,
|
||||
newPassword("newPassword"),
|
||||
newPattern("12345"),
|
||||
MANAGED_PROFILE_USER_ID,
|
||||
false));
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(
|
||||
CREDENTIAL_TYPE_PASSWORD, newCredential, MANAGED_PROFILE_USER_ID);
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, "newPassword".getBytes(),
|
||||
MANAGED_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
public void testSetLockCredential_forProfileWithUnifiedChallenge_doesNotSendRandomCredential()
|
||||
@@ -272,10 +247,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
|
||||
assertTrue(mService.setLockCredential(
|
||||
"12345".getBytes(),
|
||||
CREDENTIAL_TYPE_PATTERN,
|
||||
null,
|
||||
PASSWORD_QUALITY_SOMETHING,
|
||||
newPattern("12345"),
|
||||
nonePassword(),
|
||||
PRIMARY_USER_ID,
|
||||
false));
|
||||
|
||||
@@ -287,40 +260,35 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
public void
|
||||
testSetLockCredential_forPrimaryUserWithUnifiedChallengeProfile_updatesBothCredentials()
|
||||
throws Exception {
|
||||
final String oldCredential = "oldPassword";
|
||||
final byte[] newCredential = "newPassword".getBytes();
|
||||
final LockscreenCredential oldCredential = newPassword("oldPassword");
|
||||
final LockscreenCredential newCredential = newPassword("newPassword");
|
||||
initializeStorageWithCredential(
|
||||
PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234);
|
||||
PRIMARY_USER_ID, oldCredential, 1234);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
|
||||
assertTrue(mService.setLockCredential(
|
||||
newCredential,
|
||||
CREDENTIAL_TYPE_PASSWORD,
|
||||
oldCredential.getBytes(),
|
||||
PASSWORD_QUALITY_ALPHABETIC,
|
||||
oldCredential,
|
||||
PRIMARY_USER_ID,
|
||||
false));
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential, PRIMARY_USER_ID);
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential.getCredential(),
|
||||
PRIMARY_USER_ID);
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(
|
||||
CREDENTIAL_TYPE_PASSWORD, newCredential, MANAGED_PROFILE_USER_ID);
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential.getCredential(),
|
||||
MANAGED_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
public void
|
||||
testSetLockCredential_forPrimaryUserWithUnifiedChallengeProfile_removesBothCredentials()
|
||||
throws Exception {
|
||||
final String oldCredential = "oldPassword";
|
||||
initializeStorageWithCredential(
|
||||
PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234);
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("oldPassword"), 1234);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
|
||||
assertTrue(mService.setLockCredential(
|
||||
null,
|
||||
CREDENTIAL_TYPE_NONE,
|
||||
oldCredential.getBytes(),
|
||||
PASSWORD_QUALITY_UNSPECIFIED,
|
||||
nonePassword(),
|
||||
newPassword("oldPassword"),
|
||||
PRIMARY_USER_ID,
|
||||
false));
|
||||
|
||||
@@ -331,17 +299,13 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testSetLockCredential_nullCredential_removeBiometrics() throws RemoteException {
|
||||
final String oldCredential = "oldPassword";
|
||||
|
||||
initializeStorageWithCredential(
|
||||
PRIMARY_USER_ID,
|
||||
oldCredential,
|
||||
CREDENTIAL_TYPE_PATTERN,
|
||||
PASSWORD_QUALITY_SOMETHING);
|
||||
newPattern("123654"),
|
||||
1234);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
|
||||
mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, oldCredential.getBytes(),
|
||||
PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(nonePassword(), newPattern("123654"), PRIMARY_USER_ID, false);
|
||||
|
||||
// Verify fingerprint is removed
|
||||
verify(mFingerprintManager).remove(any(), eq(PRIMARY_USER_ID), any());
|
||||
@@ -353,41 +317,33 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
|
||||
public void testSetLockCredential_forUnifiedToSeparateChallengeProfile_sendsNewCredentials()
|
||||
throws Exception {
|
||||
final String parentPassword = "parentPassword";
|
||||
final byte[] profilePassword = "profilePassword".getBytes();
|
||||
initializeStorageWithCredential(
|
||||
PRIMARY_USER_ID, parentPassword, CREDENTIAL_TYPE_PASSWORD, 1234);
|
||||
final LockscreenCredential parentPassword = newPassword("parentPassword");
|
||||
final LockscreenCredential profilePassword = newPassword("profilePassword");
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, parentPassword, 1234);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
|
||||
assertTrue(mService.setLockCredential(
|
||||
profilePassword,
|
||||
CREDENTIAL_TYPE_PASSWORD,
|
||||
null,
|
||||
PASSWORD_QUALITY_ALPHABETIC,
|
||||
nonePassword(),
|
||||
MANAGED_PROFILE_USER_ID,
|
||||
false));
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(
|
||||
CREDENTIAL_TYPE_PASSWORD, profilePassword, MANAGED_PROFILE_USER_ID);
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, profilePassword.getCredential(),
|
||||
MANAGED_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
public void
|
||||
testSetLockCredential_forSeparateToUnifiedChallengeProfile_doesNotSendRandomCredential()
|
||||
throws Exception {
|
||||
final String parentPassword = "parentPassword";
|
||||
final String profilePassword = "12345";
|
||||
initializeStorageWithCredential(
|
||||
PRIMARY_USER_ID, parentPassword, CREDENTIAL_TYPE_PASSWORD, 1234);
|
||||
final LockscreenCredential parentPassword = newPassword("parentPassword");
|
||||
final LockscreenCredential profilePassword = newPattern("12345");
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, parentPassword, 1234);
|
||||
// Create and verify separate profile credentials.
|
||||
testCreateCredential(
|
||||
MANAGED_PROFILE_USER_ID,
|
||||
profilePassword,
|
||||
CREDENTIAL_TYPE_PATTERN,
|
||||
PASSWORD_QUALITY_SOMETHING);
|
||||
testCreateCredential(MANAGED_PROFILE_USER_ID, profilePassword);
|
||||
|
||||
mService.setSeparateProfileChallengeEnabled(
|
||||
MANAGED_PROFILE_USER_ID, false, profilePassword.getBytes());
|
||||
MANAGED_PROFILE_USER_ID, false, profilePassword);
|
||||
|
||||
// Called once for setting the initial separate profile credentials and not again during
|
||||
// unification.
|
||||
@@ -396,74 +352,69 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testVerifyCredential_forPrimaryUser_sendsCredentials() throws Exception {
|
||||
final String password = "password";
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, password, CREDENTIAL_TYPE_PASSWORD, 1234);
|
||||
final LockscreenCredential password = newPassword("password");
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, password, 1234);
|
||||
reset(mRecoverableKeyStoreManager);
|
||||
|
||||
mService.verifyCredential(
|
||||
password.getBytes(), CREDENTIAL_TYPE_PASSWORD, 1, PRIMARY_USER_ID);
|
||||
mService.verifyCredential(password, 1, PRIMARY_USER_ID);
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretAvailable(
|
||||
CREDENTIAL_TYPE_PASSWORD, password.getBytes(), PRIMARY_USER_ID);
|
||||
CREDENTIAL_TYPE_PASSWORD, password.getCredential(), PRIMARY_USER_ID);
|
||||
}
|
||||
|
||||
public void testVerifyCredential_forProfileWithSeparateChallenge_sendsCredentials()
|
||||
throws Exception {
|
||||
final byte[] pattern = "12345".getBytes();
|
||||
final LockscreenCredential pattern = newPattern("12345");
|
||||
assertTrue(mService.setLockCredential(
|
||||
pattern,
|
||||
CREDENTIAL_TYPE_PATTERN,
|
||||
null,
|
||||
PASSWORD_QUALITY_SOMETHING,
|
||||
nonePassword(),
|
||||
MANAGED_PROFILE_USER_ID,
|
||||
false));
|
||||
reset(mRecoverableKeyStoreManager);
|
||||
|
||||
mService.verifyCredential(pattern, CREDENTIAL_TYPE_PATTERN, 1, MANAGED_PROFILE_USER_ID);
|
||||
mService.verifyCredential(pattern, 1, MANAGED_PROFILE_USER_ID);
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretAvailable(
|
||||
CREDENTIAL_TYPE_PATTERN, pattern, MANAGED_PROFILE_USER_ID);
|
||||
CREDENTIAL_TYPE_PATTERN, pattern.getCredential(), MANAGED_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
public void
|
||||
testVerifyCredential_forPrimaryUserWithUnifiedChallengeProfile_sendsCredentialsForBoth()
|
||||
throws Exception {
|
||||
final String pattern = "12345";
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, pattern, CREDENTIAL_TYPE_PATTERN, 1234);
|
||||
final LockscreenCredential pattern = newPattern("12345");
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, pattern, 1234);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
reset(mRecoverableKeyStoreManager);
|
||||
|
||||
mService.verifyCredential(pattern.getBytes(), CREDENTIAL_TYPE_PATTERN, 1, PRIMARY_USER_ID);
|
||||
mService.verifyCredential(pattern, 1, PRIMARY_USER_ID);
|
||||
|
||||
// Parent sends its credentials for both the parent and profile.
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretAvailable(
|
||||
CREDENTIAL_TYPE_PATTERN, pattern.getBytes(), PRIMARY_USER_ID);
|
||||
CREDENTIAL_TYPE_PATTERN, pattern.getCredential(), PRIMARY_USER_ID);
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretAvailable(
|
||||
CREDENTIAL_TYPE_PATTERN, pattern.getBytes(), MANAGED_PROFILE_USER_ID);
|
||||
CREDENTIAL_TYPE_PATTERN, pattern.getCredential(), MANAGED_PROFILE_USER_ID);
|
||||
// Profile doesn't send its own random credentials.
|
||||
verify(mRecoverableKeyStoreManager, never())
|
||||
.lockScreenSecretAvailable(
|
||||
eq(CREDENTIAL_TYPE_PASSWORD), any(), eq(MANAGED_PROFILE_USER_ID));
|
||||
}
|
||||
|
||||
private void testCreateCredential(int userId, String credential, int type, int quality)
|
||||
private void testCreateCredential(int userId, LockscreenCredential credential)
|
||||
throws RemoteException {
|
||||
assertTrue(mService.setLockCredential(credential.getBytes(), type, null, quality,
|
||||
userId, false));
|
||||
assertVerifyCredentials(userId, credential, type, -1);
|
||||
assertTrue(mService.setLockCredential(credential, nonePassword(), userId, false));
|
||||
assertVerifyCredentials(userId, credential, -1);
|
||||
}
|
||||
|
||||
private void testCreateCredentialFailsWithoutLockScreen(
|
||||
int userId, String credential, int type, int quality) throws RemoteException {
|
||||
int userId, LockscreenCredential credential) throws RemoteException {
|
||||
mHasSecureLockScreen = false;
|
||||
|
||||
try {
|
||||
mService.setLockCredential(credential.getBytes(), type, null, quality,
|
||||
userId, false);
|
||||
mService.setLockCredential(credential, null, userId, false);
|
||||
fail("An exception should have been thrown.");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Success - the exception was expected.
|
||||
@@ -473,55 +424,51 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
assertFalse(mService.havePattern(userId));
|
||||
}
|
||||
|
||||
private void testChangeCredentials(int userId, String newCredential, int newType,
|
||||
String oldCredential, int oldType, int quality) throws RemoteException {
|
||||
private void testChangeCredentials(int userId, LockscreenCredential newCredential,
|
||||
LockscreenCredential oldCredential) throws RemoteException {
|
||||
final long sid = 1234;
|
||||
initializeStorageWithCredential(userId, oldCredential, oldType, sid);
|
||||
assertTrue(mService.setLockCredential(newCredential.getBytes(), newType,
|
||||
oldCredential.getBytes(), quality, userId, false));
|
||||
assertVerifyCredentials(userId, newCredential, newType, sid);
|
||||
initializeStorageWithCredential(userId, oldCredential, sid);
|
||||
assertTrue(mService.setLockCredential(newCredential, oldCredential, userId, false));
|
||||
assertVerifyCredentials(userId, newCredential, sid);
|
||||
}
|
||||
|
||||
private void assertVerifyCredentials(int userId, String credential, int type, long sid)
|
||||
private void assertVerifyCredentials(int userId, LockscreenCredential credential, long sid)
|
||||
throws RemoteException{
|
||||
final long challenge = 54321;
|
||||
VerifyCredentialResponse response = mService.verifyCredential(credential.getBytes(),
|
||||
type, challenge, userId);
|
||||
VerifyCredentialResponse response = mService.verifyCredential(credential,
|
||||
challenge, userId);
|
||||
|
||||
assertEquals(GateKeeperResponse.RESPONSE_OK, response.getResponseCode());
|
||||
if (sid != -1) assertEquals(sid, mGateKeeperService.getSecureUserId(userId));
|
||||
final int incorrectType;
|
||||
if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
|
||||
if (credential.isPassword()) {
|
||||
assertTrue(mService.havePassword(userId));
|
||||
assertFalse(mService.havePattern(userId));
|
||||
incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
|
||||
} else if (type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN){
|
||||
} else if (credential.isPattern()) {
|
||||
assertFalse(mService.havePassword(userId));
|
||||
assertTrue(mService.havePattern(userId));
|
||||
incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
|
||||
} else {
|
||||
assertFalse(mService.havePassword(userId));
|
||||
assertFalse(mService.havePassword(userId));
|
||||
incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
|
||||
}
|
||||
// check for bad type
|
||||
assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential(
|
||||
credential.getBytes(), incorrectType, challenge, userId).getResponseCode());
|
||||
// check for bad credential
|
||||
final LockscreenCredential badCredential;
|
||||
if (!credential.isNone()) {
|
||||
badCredential = credential.duplicate();
|
||||
badCredential.getCredential()[0] ^= 1;
|
||||
} else {
|
||||
badCredential = LockscreenCredential.createPin("0");
|
||||
}
|
||||
assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential(
|
||||
("0" + credential).getBytes(), type, challenge, userId).getResponseCode());
|
||||
badCredential, challenge, userId).getResponseCode());
|
||||
}
|
||||
|
||||
private void initializeStorageWithCredential(int userId, String credential, int type, long sid)
|
||||
throws RemoteException {
|
||||
byte[] credentialBytes = credential == null ? null : credential.getBytes();
|
||||
byte[] oldHash = new VerifyHandle(credential.getBytes(), sid).toBytes();
|
||||
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, credentialBytes, type,
|
||||
type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? PASSWORD_QUALITY_ALPHABETIC
|
||||
: PASSWORD_QUALITY_SOMETHING, userId);
|
||||
mService.initializeSyntheticPasswordLocked(oldHash, credential, userId);
|
||||
} else {
|
||||
if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
|
||||
if (credential.isPassword()) {
|
||||
mStorage.writeCredentialHash(CredentialHash.create(oldHash,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId);
|
||||
} else {
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
package com.android.server.locksettings;
|
||||
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
|
||||
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
|
||||
|
||||
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
|
||||
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
|
||||
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
|
||||
@@ -38,7 +34,6 @@ import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockscreenCredential;
|
||||
import com.android.internal.widget.VerifyCredentialResponse;
|
||||
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
|
||||
@@ -72,15 +67,14 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
|
||||
public void testPasswordBasedSyntheticPassword() throws RemoteException {
|
||||
final int USER_ID = 10;
|
||||
final byte[] password = "user-password".getBytes();
|
||||
final byte[] badPassword = "bad-password".getBytes();
|
||||
final LockscreenCredential password = newPassword("user-password");
|
||||
final LockscreenCredential badPassword = newPassword("bad-password");
|
||||
MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage,
|
||||
mGateKeeperService, mUserManager, mPasswordSlotManager);
|
||||
AuthenticationToken authToken = manager.newSyntheticPasswordAndSid(mGateKeeperService, null,
|
||||
null, USER_ID);
|
||||
long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService,
|
||||
password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, authToken,
|
||||
PASSWORD_QUALITY_ALPHABETIC, USER_ID);
|
||||
password, authToken, USER_ID);
|
||||
|
||||
AuthenticationResult result = manager.unwrapPasswordBasedSyntheticPassword(
|
||||
mGateKeeperService, handle, password, USER_ID, null);
|
||||
@@ -105,97 +99,88 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testPasswordMigration() throws RemoteException {
|
||||
final byte[] password = "testPasswordMigration-password".getBytes();
|
||||
final LockscreenCredential password = newPassword("testPasswordMigration-password");
|
||||
|
||||
disableSyntheticPassword();
|
||||
mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false);
|
||||
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
|
||||
final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
|
||||
enableSyntheticPassword();
|
||||
// Performs migration
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
password, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
|
||||
|
||||
// SP-based verification
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
password, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertArrayNotEquals(primaryStorageKey,
|
||||
mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
|
||||
}
|
||||
|
||||
protected void initializeCredentialUnderSP(byte[] password, int userId) throws RemoteException {
|
||||
protected void initializeCredentialUnderSP(LockscreenCredential password, int userId)
|
||||
throws RemoteException {
|
||||
enableSyntheticPassword();
|
||||
int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC
|
||||
: PASSWORD_QUALITY_UNSPECIFIED;
|
||||
int type = password != null ? LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
|
||||
: LockPatternUtils.CREDENTIAL_TYPE_NONE;
|
||||
mService.setLockCredential(password, type, null, quality, userId, false);
|
||||
mService.setLockCredential(password, nonePassword(), userId, false);
|
||||
}
|
||||
|
||||
public void testSyntheticPasswordChangeCredential() throws RemoteException {
|
||||
final byte[] password = "testSyntheticPasswordChangeCredential-password".getBytes();
|
||||
final byte[] newPassword = "testSyntheticPasswordChangeCredential-newpassword".getBytes();
|
||||
final LockscreenCredential password = newPassword("password");
|
||||
final LockscreenCredential newPassword = newPassword("newpassword");
|
||||
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
|
||||
mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, password,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(newPassword, password, PRIMARY_USER_ID, false);
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
newPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
}
|
||||
|
||||
public void testSyntheticPasswordVerifyCredential() throws RemoteException {
|
||||
final byte[] password = "testSyntheticPasswordVerifyCredential-password".getBytes();
|
||||
final byte[] badPassword = "testSyntheticPasswordVerifyCredential-badpassword".getBytes();
|
||||
LockscreenCredential password = newPassword("password");
|
||||
LockscreenCredential badPassword = newPassword("badpassword");
|
||||
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
password, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(
|
||||
badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
badPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
}
|
||||
|
||||
public void testSyntheticPasswordClearCredential() throws RemoteException {
|
||||
final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes();
|
||||
final byte[] badPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes();
|
||||
LockscreenCredential password = newPassword("password");
|
||||
LockscreenCredential badPassword = newPassword("newpassword");
|
||||
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
|
||||
// clear password
|
||||
mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password,
|
||||
PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false);
|
||||
assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
|
||||
// set a new password
|
||||
mService.setLockCredential(badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(badPassword, nonePassword(),
|
||||
PRIMARY_USER_ID, false);
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
badPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
}
|
||||
|
||||
public void testSyntheticPasswordChangeCredentialKeepsAuthSecret() throws RemoteException {
|
||||
final byte[] password =
|
||||
"testSyntheticPasswordChangeCredentialKeepsAuthSecret-password".getBytes();
|
||||
final byte[] badPassword =
|
||||
"testSyntheticPasswordChangeCredentialKeepsAuthSecret-new".getBytes();
|
||||
LockscreenCredential password = newPassword("password");
|
||||
LockscreenCredential badPassword = newPassword("new");
|
||||
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
mService.setLockCredential(badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, password,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(badPassword, password, PRIMARY_USER_ID, false);
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
badPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
|
||||
// Check the same secret was passed each time
|
||||
@@ -205,33 +190,28 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret() throws RemoteException {
|
||||
final byte[] password =
|
||||
"testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-password".getBytes();
|
||||
final byte[] newPassword =
|
||||
"testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-new".getBytes();
|
||||
LockscreenCredential password = newPassword("password");
|
||||
LockscreenCredential newPassword = newPassword("new");
|
||||
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
reset(mAuthSecretService);
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
password, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class));
|
||||
}
|
||||
|
||||
public void testSecondaryUserDoesNotPassAuthSecret() throws RemoteException {
|
||||
final byte[] password = "testSecondaryUserDoesNotPassAuthSecret-password".getBytes();
|
||||
LockscreenCredential password = newPassword("password");
|
||||
|
||||
initializeCredentialUnderSP(password, SECONDARY_USER_ID);
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, SECONDARY_USER_ID)
|
||||
password, 0, SECONDARY_USER_ID)
|
||||
.getResponseCode());
|
||||
verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
|
||||
}
|
||||
|
||||
public void testNoSyntheticPasswordOrCredentialDoesNotPassAuthSecret() throws RemoteException {
|
||||
// Setting null doesn't create a synthetic password
|
||||
initializeCredentialUnderSP(null, PRIMARY_USER_ID);
|
||||
|
||||
reset(mAuthSecretService);
|
||||
mService.onUnlockUser(PRIMARY_USER_ID);
|
||||
flushHandlerTasks();
|
||||
@@ -239,7 +219,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testSyntheticPasswordAndCredentialDoesNotPassAuthSecret() throws RemoteException {
|
||||
final byte[] password = "passwordForASyntheticPassword".getBytes();
|
||||
LockscreenCredential password = newPassword("passwordForASyntheticPassword");
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
|
||||
reset(mAuthSecretService);
|
||||
@@ -249,10 +229,9 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testSyntheticPasswordButNoCredentialPassesAuthSecret() throws RemoteException {
|
||||
final byte[] password = "getASyntheticPassword".getBytes();
|
||||
LockscreenCredential password = newPassword("getASyntheticPassword");
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password,
|
||||
PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false);
|
||||
|
||||
reset(mAuthSecretService);
|
||||
mService.onUnlockUser(PRIMARY_USER_ID);
|
||||
@@ -261,15 +240,14 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {
|
||||
final byte[] UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd".getBytes();
|
||||
LockscreenCredential UnifiedPassword = newPassword("unified-pwd");
|
||||
disableSyntheticPassword();
|
||||
mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(UnifiedPassword, nonePassword(), PRIMARY_USER_ID, false);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
|
||||
final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
|
||||
final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
|
||||
final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
|
||||
byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
|
||||
byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
|
||||
assertTrue(primarySid != 0);
|
||||
assertTrue(profileSid != 0);
|
||||
assertTrue(profileSid != primarySid);
|
||||
@@ -277,12 +255,12 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
// do migration
|
||||
enableSyntheticPassword();
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
UnifiedPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
|
||||
// verify
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
UnifiedPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
|
||||
@@ -295,19 +273,16 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testManagedProfileSeparateChallengeMigration() throws RemoteException {
|
||||
final byte[] primaryPassword =
|
||||
"testManagedProfileSeparateChallengeMigration-primary".getBytes();
|
||||
final byte[] profilePassword =
|
||||
"testManagedProfileSeparateChallengeMigration-profile".getBytes();
|
||||
LockscreenCredential primaryPassword = newPassword("primary");
|
||||
LockscreenCredential profilePassword = newPassword("profile");
|
||||
disableSyntheticPassword();
|
||||
mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, MANAGED_PROFILE_USER_ID, false);
|
||||
mService.setLockCredential(primaryPassword, nonePassword(), PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(profilePassword, nonePassword(),
|
||||
MANAGED_PROFILE_USER_ID, false);
|
||||
final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
|
||||
final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
|
||||
final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
|
||||
final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
|
||||
byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
|
||||
byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
|
||||
assertTrue(primarySid != 0);
|
||||
assertTrue(profileSid != 0);
|
||||
assertTrue(profileSid != primarySid);
|
||||
@@ -315,19 +290,19 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
// do migration
|
||||
enableSyntheticPassword();
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
primaryPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
|
||||
0, MANAGED_PROFILE_USER_ID).getResponseCode());
|
||||
profilePassword, 0, MANAGED_PROFILE_USER_ID)
|
||||
.getResponseCode());
|
||||
|
||||
// verify
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
primaryPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
|
||||
0, MANAGED_PROFILE_USER_ID).getResponseCode());
|
||||
profilePassword, 0, MANAGED_PROFILE_USER_ID)
|
||||
.getResponseCode());
|
||||
assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
|
||||
assertArrayNotEquals(primaryStorageKey,
|
||||
@@ -339,101 +314,92 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testTokenBasedResetPassword() throws RemoteException {
|
||||
final byte[] password = "password".getBytes();
|
||||
final byte[] pattern = "123654".getBytes();
|
||||
final byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
LockscreenCredential password = newPassword("password");
|
||||
LockscreenCredential pattern = newPattern("123654");
|
||||
byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
// Disregard any reportPasswordChanged() invocations as part of credential setup.
|
||||
flushHandlerTasks();
|
||||
reset(mDevicePolicyManager);
|
||||
|
||||
final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
|
||||
byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
|
||||
|
||||
assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
|
||||
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
|
||||
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
|
||||
assertTrue(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
|
||||
|
||||
mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
|
||||
PRIMARY_USER_ID).getResponseCode();
|
||||
mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode();
|
||||
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
|
||||
assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
|
||||
|
||||
mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
|
||||
handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
|
||||
mLocalService.setLockCredentialWithToken(pattern, handle, token, PRIMARY_USER_ID);
|
||||
|
||||
// Verify DPM gets notified about new device lock
|
||||
flushHandlerTasks();
|
||||
final PasswordMetrics metric = PasswordMetrics.computeForCredential(
|
||||
LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(pattern)));
|
||||
final PasswordMetrics metric = PasswordMetrics.computeForCredential(pattern);
|
||||
assertEquals(metric, mService.getUserPasswordMetrics(PRIMARY_USER_ID));
|
||||
verify(mDevicePolicyManager).reportPasswordChanged(PRIMARY_USER_ID);
|
||||
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID)
|
||||
pattern, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
|
||||
}
|
||||
|
||||
public void testTokenBasedClearPassword() throws RemoteException {
|
||||
final byte[] password = "password".getBytes();
|
||||
final byte[] pattern = "123654".getBytes();
|
||||
final byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
LockscreenCredential password = newPassword("password");
|
||||
LockscreenCredential pattern = newPattern("123654");
|
||||
byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
|
||||
byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
|
||||
|
||||
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
|
||||
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
|
||||
|
||||
mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
|
||||
0, PRIMARY_USER_ID).getResponseCode();
|
||||
mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode();
|
||||
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
|
||||
|
||||
mLocalService.setLockCredentialWithToken(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
|
||||
handle, token, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
|
||||
mLocalService.setLockCredentialWithToken(nonePassword(), handle, token, PRIMARY_USER_ID);
|
||||
flushHandlerTasks(); // flush the unlockUser() call before changing password again
|
||||
mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
|
||||
handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
|
||||
mLocalService.setLockCredentialWithToken(pattern, handle, token,
|
||||
PRIMARY_USER_ID);
|
||||
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID)
|
||||
pattern, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
|
||||
}
|
||||
|
||||
public void testTokenBasedResetPasswordAfterCredentialChanges() throws RemoteException {
|
||||
final byte[] password = "password".getBytes();
|
||||
final byte[] pattern = "123654".getBytes();
|
||||
final byte[] newPassword = "password".getBytes();
|
||||
final byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
LockscreenCredential password = newPassword("password");
|
||||
LockscreenCredential pattern = newPattern("123654");
|
||||
LockscreenCredential newPassword = newPassword("password");
|
||||
byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
|
||||
byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
|
||||
|
||||
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
|
||||
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
|
||||
|
||||
mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
|
||||
0, PRIMARY_USER_ID).getResponseCode();
|
||||
mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode();
|
||||
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
|
||||
|
||||
mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, password,
|
||||
PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(pattern, password, PRIMARY_USER_ID, false);
|
||||
|
||||
mLocalService.setLockCredentialWithToken(newPassword,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
|
||||
mLocalService.setLockCredentialWithToken(newPassword, handle, token, PRIMARY_USER_ID);
|
||||
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
newPassword, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
|
||||
}
|
||||
|
||||
public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNeedsMigration()
|
||||
throws RemoteException {
|
||||
final String token = "some-high-entropy-secure-token";
|
||||
final byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
enableSyntheticPassword();
|
||||
long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID, null);
|
||||
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
|
||||
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
|
||||
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
|
||||
@@ -441,9 +407,14 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
|
||||
public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNoMigration()
|
||||
throws RemoteException {
|
||||
final String token = "some-high-entropy-secure-token";
|
||||
initializeCredentialUnderSP(null, PRIMARY_USER_ID);
|
||||
long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID, null);
|
||||
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);
|
||||
assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"),
|
||||
PRIMARY_USER_ID, false));
|
||||
|
||||
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
|
||||
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
|
||||
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
|
||||
assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
|
||||
@@ -451,12 +422,11 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
|
||||
public void testEscrowTokenActivatedLaterWithUserPasswordNeedsMigration()
|
||||
throws RemoteException {
|
||||
final byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
final byte[] password = "password".getBytes();
|
||||
byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
LockscreenCredential password = newPassword("password");
|
||||
// Set up pre-SP user password
|
||||
disableSyntheticPassword();
|
||||
mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false);
|
||||
enableSyntheticPassword();
|
||||
|
||||
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
|
||||
@@ -464,14 +434,14 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
|
||||
// Activate token (password gets migrated to SP at the same time)
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
password, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
// Verify token is activated
|
||||
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
|
||||
}
|
||||
|
||||
public void testEscrowTokenCannotBeActivatedOnUnmanagedUser() {
|
||||
final byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
when(mUserManagerInternal.isDeviceManaged()).thenReturn(false);
|
||||
when(mUserManagerInternal.isUserManaged(PRIMARY_USER_ID)).thenReturn(false);
|
||||
when(mDeviceStateCache.isDeviceProvisioned()).thenReturn(true);
|
||||
@@ -483,9 +453,9 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception {
|
||||
final byte[] password = "password".getBytes();
|
||||
final byte[] pattern = "123654".getBytes();
|
||||
final byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
LockscreenCredential password = newPassword("password");
|
||||
LockscreenCredential pattern = newPattern("123654");
|
||||
byte[] token = "some-high-entropy-secure-token".getBytes();
|
||||
|
||||
mHasSecureLockScreen = false;
|
||||
enableSyntheticPassword();
|
||||
@@ -493,9 +463,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
|
||||
|
||||
try {
|
||||
mLocalService.setLockCredentialWithToken(password,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
|
||||
mLocalService.setLockCredentialWithToken(password, handle, token, PRIMARY_USER_ID);
|
||||
fail("An exception should have been thrown.");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Success - the exception was expected.
|
||||
@@ -503,9 +471,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
assertFalse(mService.havePassword(PRIMARY_USER_ID));
|
||||
|
||||
try {
|
||||
mLocalService.setLockCredentialWithToken(pattern,
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
|
||||
mLocalService.setLockCredentialWithToken(pattern, handle, token, PRIMARY_USER_ID);
|
||||
fail("An exception should have been thrown.");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Success - the exception was expected.
|
||||
@@ -514,36 +480,33 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
}
|
||||
|
||||
public void testGetHashFactorPrimaryUser() throws RemoteException {
|
||||
final byte[] password = "password".getBytes();
|
||||
mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
|
||||
final byte[] hashFactor = mService.getHashFactor(password, PRIMARY_USER_ID);
|
||||
LockscreenCredential password = newPassword("password");
|
||||
mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false);
|
||||
byte[] hashFactor = mService.getHashFactor(password, PRIMARY_USER_ID);
|
||||
assertNotNull(hashFactor);
|
||||
|
||||
mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
|
||||
password, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false);
|
||||
final byte[] newHashFactor = mService.getHashFactor(null, PRIMARY_USER_ID);
|
||||
mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false);
|
||||
byte[] newHashFactor = mService.getHashFactor(nonePassword(), PRIMARY_USER_ID);
|
||||
assertNotNull(newHashFactor);
|
||||
// Hash factor should never change after password change/removal
|
||||
assertArrayEquals(hashFactor, newHashFactor);
|
||||
}
|
||||
|
||||
public void testGetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
|
||||
final byte[] pattern = "1236".getBytes();
|
||||
mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
|
||||
null, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID, false);
|
||||
LockscreenCredential pattern = newPattern("1236");
|
||||
mService.setLockCredential(pattern, nonePassword(), PRIMARY_USER_ID, false);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID));
|
||||
}
|
||||
|
||||
public void testGetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
|
||||
final byte[] primaryPassword = "primary".getBytes();
|
||||
final byte[] profilePassword = "profile".getBytes();
|
||||
mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
|
||||
PASSWORD_QUALITY_ALPHABETIC, MANAGED_PROFILE_USER_ID, false);
|
||||
assertNotNull(mService.getHashFactor(profilePassword, MANAGED_PROFILE_USER_ID));
|
||||
LockscreenCredential primaryPassword = newPassword("primary");
|
||||
LockscreenCredential profilePassword = newPassword("profile");
|
||||
mService.setLockCredential(primaryPassword, nonePassword(), PRIMARY_USER_ID, false);
|
||||
mService.setLockCredential(profilePassword, nonePassword(),
|
||||
MANAGED_PROFILE_USER_ID, false);
|
||||
assertNotNull(
|
||||
mService.getHashFactor(profilePassword, MANAGED_PROFILE_USER_ID));
|
||||
}
|
||||
|
||||
public void testPasswordData_serializeDeserialize() {
|
||||
@@ -591,11 +554,11 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
|
||||
public void testGsiDisablesAuthSecret() throws RemoteException {
|
||||
mGsiService.setIsGsiRunning(true);
|
||||
|
||||
final byte[] password = "testGsiDisablesAuthSecret-password".getBytes();
|
||||
LockscreenCredential password = newPassword("testGsiDisablesAuthSecret-password");
|
||||
|
||||
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
|
||||
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
|
||||
password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
|
||||
password, 0, PRIMARY_USER_ID)
|
||||
.getResponseCode());
|
||||
verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user