Merge "Dont sync keys using the unified challenge profile random credential"
This commit is contained in:
@@ -94,6 +94,7 @@ import android.service.gatekeeper.GateKeeperResponse;
|
||||
import android.service.gatekeeper.IGateKeeperService;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
@@ -141,6 +142,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -324,7 +326,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
Arrays.fill(newPasswordChars, '\u0000');
|
||||
final int quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
|
||||
setLockCredentialInternal(newPassword, CREDENTIAL_TYPE_PASSWORD, managedUserPassword,
|
||||
quality, managedUserId, false);
|
||||
quality, 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.
|
||||
@@ -1303,13 +1305,13 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE,
|
||||
profilePasswordMap.get(managedUserId),
|
||||
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId,
|
||||
false);
|
||||
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,
|
||||
true);
|
||||
true, /* isLockTiedToParent= */ true);
|
||||
}
|
||||
mStorage.removeChildProfileLock(managedUserId);
|
||||
removeKeystoreProfileKey(managedUserId);
|
||||
@@ -1328,6 +1330,67 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
&& mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send credentials for user {@code userId} to {@link RecoverableKeyStoreManager} during an
|
||||
* unlock operation.
|
||||
*/
|
||||
private void sendCredentialsOnUnlockIfRequired(
|
||||
int credentialType, @NonNull byte[] credential, int userId) {
|
||||
// Don't send credentials during the factory reset protection flow.
|
||||
if (userId == USER_FRP) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A profile with a unified lock screen stores a randomly generated credential, so skip it.
|
||||
// Its parent will send credentials for the profile, as it stores the unified lock
|
||||
// credential.
|
||||
if (isManagedProfileWithUnifiedLock(userId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send credentials for the user and any child profiles that share its lock screen.
|
||||
for (int profileId : getProfilesWithSameLockScreen(userId)) {
|
||||
mRecoverableKeyStoreManager.lockScreenSecretAvailable(
|
||||
credentialType, credential, profileId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send credentials for user {@code userId} to {@link RecoverableKeyStoreManager} when its
|
||||
* credentials are set/changed.
|
||||
*/
|
||||
private void sendCredentialsOnChangeIfRequired(
|
||||
int credentialType, byte[] 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.
|
||||
if (isLockTiedToParent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send credentials for the user and any child profiles that share its lock screen.
|
||||
for (int profileId : getProfilesWithSameLockScreen(userId)) {
|
||||
mRecoverableKeyStoreManager.lockScreenSecretChanged(
|
||||
credentialType, credential, profileId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all profiles of {@code userId}, including itself, that have the same lock screen
|
||||
* challenge.
|
||||
*/
|
||||
private Set<Integer> getProfilesWithSameLockScreen(int userId) {
|
||||
Set<Integer> profiles = new ArraySet<>();
|
||||
for (UserInfo profile : mUserManager.getProfiles(userId)) {
|
||||
if (profile.id == userId
|
||||
|| (profile.profileGroupId == userId
|
||||
&& isManagedProfileWithUnifiedLock(profile.id))) {
|
||||
profiles.add(profile.id);
|
||||
}
|
||||
}
|
||||
return profiles;
|
||||
}
|
||||
|
||||
// This method should be called by LockPatternUtil only, all internal methods in this class
|
||||
// should call setLockCredentialInternal.
|
||||
@Override
|
||||
@@ -1342,7 +1405,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
checkWritePermission(userId);
|
||||
synchronized (mSeparateChallengeLock) {
|
||||
setLockCredentialInternal(credential, type, savedCredential, requestedQuality, userId,
|
||||
allowUntrustedChange);
|
||||
allowUntrustedChange, /* isLockTiedToParent= */ false);
|
||||
setSeparateProfileChallengeEnabledLocked(userId, true, null);
|
||||
notifyPasswordChanged(userId);
|
||||
}
|
||||
@@ -1353,9 +1416,13 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
notifySeparateProfileChallengeChanged(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isLockTiedToParent is {@code true} if {@code userId} is a profile and its new
|
||||
* credentials are being tied to its parent's credentials.
|
||||
*/
|
||||
private void setLockCredentialInternal(byte[] credential, @CredentialType int credentialType,
|
||||
byte[] savedCredential, int requestedQuality, int userId,
|
||||
boolean allowUntrustedChange) throws RemoteException {
|
||||
byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange,
|
||||
boolean isLockTiedToParent) throws RemoteException {
|
||||
// Normalize savedCredential and credential such that empty string is always represented
|
||||
// as null.
|
||||
if (savedCredential == null || savedCredential.length == 0) {
|
||||
@@ -1367,7 +1434,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
synchronized (mSpManager) {
|
||||
if (isSyntheticPasswordBasedCredentialLocked(userId)) {
|
||||
spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
|
||||
requestedQuality, userId, allowUntrustedChange);
|
||||
requestedQuality, userId, allowUntrustedChange, isLockTiedToParent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1383,7 +1450,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
fixateNewestUserKeyAuth(userId);
|
||||
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
|
||||
notifyActivePasswordMetricsAvailable(CREDENTIAL_TYPE_NONE, null, userId);
|
||||
mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential, userId);
|
||||
sendCredentialsOnChangeIfRequired(
|
||||
credentialType, credential, userId, isLockTiedToParent);
|
||||
return;
|
||||
}
|
||||
if (credential == null) {
|
||||
@@ -1418,7 +1486,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential,
|
||||
currentHandle.type, requestedQuality, userId);
|
||||
spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
|
||||
requestedQuality, userId, allowUntrustedChange);
|
||||
requestedQuality, userId, allowUntrustedChange, isLockTiedToParent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1436,8 +1504,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
// Refresh the auth token
|
||||
doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */);
|
||||
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
|
||||
mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential,
|
||||
userId);
|
||||
sendCredentialsOnChangeIfRequired(
|
||||
credentialType, credential, userId, isLockTiedToParent);
|
||||
} else {
|
||||
throw new RemoteException("Failed to enroll " +
|
||||
(credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
|
||||
@@ -1678,8 +1746,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
// The user employs synthetic password based credential.
|
||||
if (response != null) {
|
||||
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
|
||||
mRecoverableKeyStoreManager.lockScreenSecretAvailable(credentialType, credential,
|
||||
userId);
|
||||
sendCredentialsOnUnlockIfRequired(credentialType, credential, userId);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -1713,7 +1780,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
|
||||
if (shouldReEnrollBaseZero) {
|
||||
setLockCredentialInternal(credential, storedHash.type, credentialToVerify,
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId, false);
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId, false,
|
||||
/* isLockTiedToParent= */ false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1804,12 +1872,12 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
storedHash.type == CREDENTIAL_TYPE_PATTERN
|
||||
? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
|
||||
: DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
|
||||
/* TODO(roosa): keep the same password quality */, userId, false);
|
||||
/* TODO(roosa): keep the same password quality */,
|
||||
userId, false, /* isLockTiedToParent= */ false);
|
||||
if (!hasChallenge) {
|
||||
notifyActivePasswordMetricsAvailable(storedHash.type, credential, userId);
|
||||
// Use credentials to create recoverable keystore snapshot.
|
||||
mRecoverableKeyStoreManager.lockScreenSecretAvailable(
|
||||
storedHash.type, credential, userId);
|
||||
sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId);
|
||||
return VerifyCredentialResponse.OK;
|
||||
}
|
||||
// Fall through to get the auth token. Technically this should never happen,
|
||||
@@ -1847,7 +1915,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
/* TODO(roosa): keep the same password quality */;
|
||||
if (shouldReEnroll) {
|
||||
setLockCredentialInternal(credential, storedHash.type, credential,
|
||||
reEnrollQuality, userId, false);
|
||||
reEnrollQuality, userId, false, /* isLockTiedToParent= */ false);
|
||||
} else {
|
||||
// Now that we've cleared of all required GK migration, let's do the final
|
||||
// migration to synthetic password.
|
||||
@@ -1861,8 +1929,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
}
|
||||
// Use credentials to create recoverable keystore snapshot.
|
||||
mRecoverableKeyStoreManager.lockScreenSecretAvailable(storedHash.type, credential,
|
||||
userId);
|
||||
sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId);
|
||||
|
||||
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
|
||||
if (response.getTimeout() > 0) {
|
||||
@@ -2554,7 +2621,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
@GuardedBy("mSpManager")
|
||||
private void spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType,
|
||||
byte[] savedCredential, int requestedQuality, int userId,
|
||||
boolean allowUntrustedChange) throws RemoteException {
|
||||
boolean allowUntrustedChange, boolean isLockTiedToParent) throws RemoteException {
|
||||
if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
|
||||
if (isManagedProfileWithUnifiedLock(userId)) {
|
||||
// get credential from keystore when managed profile has unified lock
|
||||
@@ -2620,7 +2687,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.
|
||||
}
|
||||
mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential, userId);
|
||||
sendCredentialsOnChangeIfRequired(credentialType, credential, userId, isLockTiedToParent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.android.internal.widget.ILockSettings;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockSettingsInternal;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
|
||||
import com.android.server.wm.WindowManagerInternal;
|
||||
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
@@ -89,6 +90,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
|
||||
WindowManagerInternal mMockWindowManager;
|
||||
FakeGsiService mGsiService;
|
||||
PasswordSlotManagerTestable mPasswordSlotManager;
|
||||
RecoverableKeyStoreManager mRecoverableKeyStoreManager;
|
||||
protected boolean mHasSecureLockScreen;
|
||||
|
||||
@Override
|
||||
@@ -105,6 +107,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
|
||||
mMockWindowManager = mock(WindowManagerInternal.class);
|
||||
mGsiService = new FakeGsiService();
|
||||
mPasswordSlotManager = new PasswordSlotManagerTestable();
|
||||
mRecoverableKeyStoreManager = mock(RecoverableKeyStoreManager.class);
|
||||
|
||||
LocalServices.removeServiceForTest(LockSettingsInternal.class);
|
||||
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
|
||||
@@ -141,12 +144,14 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
|
||||
mAuthSecretService = mock(IAuthSecret.class);
|
||||
mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
|
||||
mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
|
||||
mSpManager, mAuthSecretService, mGsiService);
|
||||
mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager);
|
||||
when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
|
||||
mPrimaryUserProfiles.add(PRIMARY_USER_INFO);
|
||||
installChildProfile(MANAGED_PROFILE_USER_ID);
|
||||
installAndTurnOffChildProfile(TURNED_OFF_PROFILE_USER_ID);
|
||||
when(mUserManager.getProfiles(eq(PRIMARY_USER_ID))).thenReturn(mPrimaryUserProfiles);
|
||||
for (UserInfo profile : mPrimaryUserProfiles) {
|
||||
when(mUserManager.getProfiles(eq(profile.id))).thenReturn(mPrimaryUserProfiles);
|
||||
}
|
||||
when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(SECONDARY_USER_INFO);
|
||||
|
||||
final ArrayList<UserInfo> allUsers = new ArrayList<>(mPrimaryUserProfiles);
|
||||
@@ -173,6 +178,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
|
||||
private UserInfo installChildProfile(int profileId) {
|
||||
final UserInfo userInfo = new UserInfo(
|
||||
profileId, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MANAGED_PROFILE);
|
||||
userInfo.profileGroupId = PRIMARY_USER_ID;
|
||||
mPrimaryUserProfiles.add(userInfo);
|
||||
when(mUserManager.getUserInfo(eq(profileId))).thenReturn(userInfo);
|
||||
when(mUserManager.getProfileParent(eq(profileId))).thenReturn(PRIMARY_USER_INFO);
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.security.KeyStore;
|
||||
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
@@ -45,11 +46,13 @@ public class LockSettingsServiceTestable extends LockSettingsService {
|
||||
private SyntheticPasswordManager mSpManager;
|
||||
private IAuthSecret mAuthSecretService;
|
||||
private FakeGsiService mGsiService;
|
||||
private RecoverableKeyStoreManager mRecoverableKeyStoreManager;
|
||||
|
||||
public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
|
||||
IActivityManager activityManager, LockPatternUtils lockPatternUtils,
|
||||
IStorageManager storageManager, SyntheticPasswordManager spManager,
|
||||
IAuthSecret authSecretService, FakeGsiService gsiService) {
|
||||
IAuthSecret authSecretService, FakeGsiService gsiService,
|
||||
RecoverableKeyStoreManager recoverableKeyStoreManager) {
|
||||
super(context);
|
||||
mLockSettingsStorage = storage;
|
||||
mKeyStore = keyStore;
|
||||
@@ -58,6 +61,7 @@ public class LockSettingsServiceTestable extends LockSettingsService {
|
||||
mStorageManager = storageManager;
|
||||
mSpManager = spManager;
|
||||
mGsiService = gsiService;
|
||||
mRecoverableKeyStoreManager = recoverableKeyStoreManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,15 +123,21 @@ public class LockSettingsServiceTestable extends LockSettingsService {
|
||||
public boolean isGsiRunning() {
|
||||
return mGsiService.isGsiRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) {
|
||||
return mRecoverableKeyStoreManager;
|
||||
}
|
||||
}
|
||||
|
||||
protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
|
||||
LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
|
||||
IStorageManager storageManager, IActivityManager mActivityManager,
|
||||
SyntheticPasswordManager spManager, IAuthSecret authSecretService,
|
||||
FakeGsiService gsiService) {
|
||||
FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager) {
|
||||
super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
|
||||
storageManager, spManager, authSecretService, gsiService));
|
||||
storageManager, spManager, authSecretService, gsiService,
|
||||
recoverableKeyStoreManager));
|
||||
mGateKeeperService = gatekeeper;
|
||||
mAuthSecretService = authSecretService;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,13 @@ 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;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.service.gatekeeper.GateKeeperResponse;
|
||||
@@ -211,6 +218,222 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
|
||||
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
|
||||
}
|
||||
|
||||
public void testSetLockCredential_forPrimaryUser_sendsCredentials() throws Exception {
|
||||
final byte[] password = "password".getBytes();
|
||||
|
||||
mService.setLockCredential(
|
||||
password,
|
||||
CREDENTIAL_TYPE_PASSWORD,
|
||||
null,
|
||||
PASSWORD_QUALITY_ALPHABETIC,
|
||||
PRIMARY_USER_ID,
|
||||
false);
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, password, PRIMARY_USER_ID);
|
||||
}
|
||||
|
||||
public void testSetLockCredential_forProfileWithSeparateChallenge_sendsCredentials()
|
||||
throws Exception {
|
||||
final byte[] pattern = "12345".getBytes();
|
||||
|
||||
mService.setLockCredential(
|
||||
pattern,
|
||||
CREDENTIAL_TYPE_PATTERN,
|
||||
null,
|
||||
PASSWORD_QUALITY_SOMETHING,
|
||||
MANAGED_PROFILE_USER_ID,
|
||||
false);
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PATTERN, pattern, 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);
|
||||
|
||||
mService.setLockCredential(
|
||||
newCredential,
|
||||
CREDENTIAL_TYPE_PASSWORD,
|
||||
oldCredential.getBytes(),
|
||||
PASSWORD_QUALITY_ALPHABETIC,
|
||||
MANAGED_PROFILE_USER_ID,
|
||||
false);
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(
|
||||
CREDENTIAL_TYPE_PASSWORD, newCredential, MANAGED_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
public void testSetLockCredential_forProfileWithUnifiedChallenge_doesNotSendRandomCredential()
|
||||
throws Exception {
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
|
||||
mService.setLockCredential(
|
||||
"12345".getBytes(),
|
||||
CREDENTIAL_TYPE_PATTERN,
|
||||
null,
|
||||
PASSWORD_QUALITY_SOMETHING,
|
||||
PRIMARY_USER_ID,
|
||||
false);
|
||||
|
||||
verify(mRecoverableKeyStoreManager, never())
|
||||
.lockScreenSecretChanged(
|
||||
eq(CREDENTIAL_TYPE_PASSWORD), any(), eq(MANAGED_PROFILE_USER_ID));
|
||||
}
|
||||
|
||||
public void
|
||||
testSetLockCredential_forPrimaryUserWithUnifiedChallengeProfile_updatesBothCredentials()
|
||||
throws Exception {
|
||||
final String oldCredential = "oldPassword";
|
||||
final byte[] newCredential = "newPassword".getBytes();
|
||||
initializeStorageWithCredential(
|
||||
PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
|
||||
mService.setLockCredential(
|
||||
newCredential,
|
||||
CREDENTIAL_TYPE_PASSWORD,
|
||||
oldCredential.getBytes(),
|
||||
PASSWORD_QUALITY_ALPHABETIC,
|
||||
PRIMARY_USER_ID,
|
||||
false);
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential, PRIMARY_USER_ID);
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(
|
||||
CREDENTIAL_TYPE_PASSWORD, newCredential, MANAGED_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
public void
|
||||
testSetLockCredential_forPrimaryUserWithUnifiedChallengeProfile_removesBothCredentials()
|
||||
throws Exception {
|
||||
final String oldCredential = "oldPassword";
|
||||
initializeStorageWithCredential(
|
||||
PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
|
||||
mService.setLockCredential(
|
||||
null,
|
||||
CREDENTIAL_TYPE_NONE,
|
||||
oldCredential.getBytes(),
|
||||
PASSWORD_QUALITY_UNSPECIFIED,
|
||||
PRIMARY_USER_ID,
|
||||
false);
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_NONE, null, PRIMARY_USER_ID);
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(CREDENTIAL_TYPE_NONE, null, MANAGED_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
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);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
|
||||
mService.setLockCredential(
|
||||
profilePassword,
|
||||
CREDENTIAL_TYPE_PASSWORD,
|
||||
null,
|
||||
PASSWORD_QUALITY_ALPHABETIC,
|
||||
MANAGED_PROFILE_USER_ID,
|
||||
false);
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(
|
||||
CREDENTIAL_TYPE_PASSWORD, profilePassword, 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);
|
||||
// Create and verify separate profile credentials.
|
||||
testCreateCredential(
|
||||
MANAGED_PROFILE_USER_ID,
|
||||
profilePassword,
|
||||
CREDENTIAL_TYPE_PATTERN,
|
||||
PASSWORD_QUALITY_SOMETHING);
|
||||
|
||||
mService.setSeparateProfileChallengeEnabled(
|
||||
MANAGED_PROFILE_USER_ID, false, profilePassword.getBytes());
|
||||
|
||||
// Called once for setting the initial separate profile credentials and not again during
|
||||
// unification.
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretChanged(anyInt(), any(), eq(MANAGED_PROFILE_USER_ID));
|
||||
}
|
||||
|
||||
public void testVerifyCredential_forPrimaryUser_sendsCredentials() throws Exception {
|
||||
final String password = "password";
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, password, CREDENTIAL_TYPE_PASSWORD, 1234);
|
||||
reset(mRecoverableKeyStoreManager);
|
||||
|
||||
mService.verifyCredential(
|
||||
password.getBytes(), CREDENTIAL_TYPE_PASSWORD, 1, PRIMARY_USER_ID);
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretAvailable(
|
||||
CREDENTIAL_TYPE_PASSWORD, password.getBytes(), PRIMARY_USER_ID);
|
||||
}
|
||||
|
||||
public void testVerifyCredential_forProfileWithSeparateChallenge_sendsCredentials()
|
||||
throws Exception {
|
||||
final byte[] pattern = "12345".getBytes();
|
||||
mService.setLockCredential(
|
||||
pattern,
|
||||
CREDENTIAL_TYPE_PATTERN,
|
||||
null,
|
||||
PASSWORD_QUALITY_SOMETHING,
|
||||
MANAGED_PROFILE_USER_ID,
|
||||
false);
|
||||
reset(mRecoverableKeyStoreManager);
|
||||
|
||||
mService.verifyCredential(pattern, CREDENTIAL_TYPE_PATTERN, 1, MANAGED_PROFILE_USER_ID);
|
||||
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretAvailable(
|
||||
CREDENTIAL_TYPE_PATTERN, pattern, MANAGED_PROFILE_USER_ID);
|
||||
}
|
||||
|
||||
public void
|
||||
testVerifyCredential_forPrimaryUserWithUnifiedChallengeProfile_sendsCredentialsForBoth()
|
||||
throws Exception {
|
||||
final String pattern = "12345";
|
||||
initializeStorageWithCredential(PRIMARY_USER_ID, pattern, CREDENTIAL_TYPE_PATTERN, 1234);
|
||||
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
|
||||
reset(mRecoverableKeyStoreManager);
|
||||
|
||||
mService.verifyCredential(pattern.getBytes(), CREDENTIAL_TYPE_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);
|
||||
verify(mRecoverableKeyStoreManager)
|
||||
.lockScreenSecretAvailable(
|
||||
CREDENTIAL_TYPE_PATTERN, pattern.getBytes(), 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)
|
||||
throws RemoteException {
|
||||
mService.setLockCredential(credential.getBytes(), type, null, quality,
|
||||
|
||||
@@ -58,6 +58,7 @@ import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
|
||||
import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager;
|
||||
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
|
||||
@@ -83,7 +84,7 @@ import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
@@ -156,6 +157,7 @@ public class RecoverableKeyStoreManagerTest {
|
||||
@Mock private PlatformKeyManager mPlatformKeyManager;
|
||||
@Mock private ApplicationKeyStorage mApplicationKeyStorage;
|
||||
@Mock private CleanupManager mCleanupManager;
|
||||
@Mock private ExecutorService mExecutorService;
|
||||
@Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
|
||||
|
||||
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
|
||||
@@ -188,7 +190,7 @@ public class RecoverableKeyStoreManagerTest {
|
||||
mMockContext,
|
||||
mRecoverableKeyStoreDb,
|
||||
mRecoverySessionStorage,
|
||||
Executors.newSingleThreadExecutor(),
|
||||
mExecutorService,
|
||||
mRecoverySnapshotStorage,
|
||||
mMockListenersStorage,
|
||||
mPlatformKeyManager,
|
||||
@@ -1246,6 +1248,24 @@ public class RecoverableKeyStoreManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lockScreenSecretAvailable_syncsKeysForUser() throws Exception {
|
||||
mRecoverableKeyStoreManager.lockScreenSecretAvailable(
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PATTERN, "password".getBytes(), 11);
|
||||
|
||||
verify(mExecutorService).execute(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lockScreenSecretChanged_syncsKeysForUser() throws Exception {
|
||||
mRecoverableKeyStoreManager.lockScreenSecretChanged(
|
||||
LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
|
||||
"password".getBytes(),
|
||||
11);
|
||||
|
||||
verify(mExecutorService).execute(any());
|
||||
}
|
||||
|
||||
private static byte[] encryptedApplicationKey(
|
||||
SecretKey recoveryKey, byte[] applicationKey) throws Exception {
|
||||
return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
|
||||
|
||||
Reference in New Issue
Block a user