Merge "Dont sync keys using the unified challenge profile random credential"

This commit is contained in:
Annie Meng
2019-04-03 17:18:34 +00:00
committed by Android (Google) Code Review
5 changed files with 355 additions and 29 deletions

View File

@@ -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);
}
/**

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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(