Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 086ddc81 authored by Annie Meng's avatar Annie Meng
Browse files

Dont sync keys using the unified challenge profile random credential

When the work profile has a tied screen lock to its parent, its lock
credentials are set to a random password. This CL adds logic to prevent
syncing keys with this random credential.

On set/update lock:
- If creating the work profile or going from separate -> unified lock
screen: don't sync keys (random password case)
- If going from unified -> separate lock screen: sync keys
- If removing the parent lock: invalidate unified profile keys

On unlock:
- If unlocking a work profile with a unified lock: don't sync keys
(random password case).
- If unlocking a work profile with a separate lock: sync keys
- If unlocking a parent profile that has work profiles with a unified
lock: sync keys for the work profiles.

Design: https://docs.google.com/document/d/1y6LXcf-Rk3TMG-Ka4pJ5fpinDaK4fnlCyGi3kuGWWNg/edit?usp=sharing

Bug: 128834006
Test: 1) atest frameworks/base/services/tests/servicestests/src/com/android/server/locksettings/
2) Manual testing of the following cases by verifying key sync on the
backup device and being able to unencrypt the backup set on the restore
device:
a) Work profile unified lock screen: all 3 types (pin/password/pattern).
b) Changing parent lock screen in the unified case -> updates keys for
profile.
c) Unified lock screen -> separate lock screen: updates keys.
d) Separate lock screen and change credentials: updates keys.
e) Separate lock screen -> unified lock screen: does not update keys
with random password.
f) Unified lock screen -> remove lock screen: invalidates keys.

Change-Id: Ie2249f4c32fd6c48aae7f791e2d1e353b4ef9939
parent 0787f0ce
Loading
Loading
Loading
Loading
+89 −22
Original line number Original line Diff line number Diff line
@@ -94,6 +94,7 @@ import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.EventLog;
import android.util.Log;
import android.util.Log;
import android.util.Slog;
import android.util.Slog;
@@ -141,6 +142,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeUnit;


@@ -324,7 +326,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            Arrays.fill(newPasswordChars, '\u0000');
            Arrays.fill(newPasswordChars, '\u0000');
            final int quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
            final int quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
            setLockCredentialInternal(newPassword, CREDENTIAL_TYPE_PASSWORD, managedUserPassword,
            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
            // 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
            // account holder's credential. As such, the user will never be prompted to enter this
            // password directly, so we always store a password.
            // password directly, so we always store a password.
@@ -1303,13 +1305,13 @@ public class LockSettingsService extends ILockSettings.Stub {
                        setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE,
                        setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE,
                                profilePasswordMap.get(managedUserId),
                                profilePasswordMap.get(managedUserId),
                                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId,
                                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId,
                                false);
                                false, /* isLockTiedToParent= */ true);
                    } else {
                    } else {
                        Slog.wtf(TAG, "clear tied profile challenges, but no password supplied.");
                        Slog.wtf(TAG, "clear tied profile challenges, but no password supplied.");
                        // Supplying null here would lead to untrusted credential change
                        // Supplying null here would lead to untrusted credential change
                        setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE, null,
                        setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE, null,
                                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId,
                                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId,
                                true);
                                true, /* isLockTiedToParent= */ true);
                    }
                    }
                    mStorage.removeChildProfileLock(managedUserId);
                    mStorage.removeChildProfileLock(managedUserId);
                    removeKeystoreProfileKey(managedUserId);
                    removeKeystoreProfileKey(managedUserId);
@@ -1328,6 +1330,67 @@ public class LockSettingsService extends ILockSettings.Stub {
                && mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
                && 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
    // This method should be called by LockPatternUtil only, all internal methods in this class
    // should call setLockCredentialInternal.
    // should call setLockCredentialInternal.
    @Override
    @Override
@@ -1342,16 +1405,20 @@ public class LockSettingsService extends ILockSettings.Stub {
        checkWritePermission(userId);
        checkWritePermission(userId);
        synchronized (mSeparateChallengeLock) {
        synchronized (mSeparateChallengeLock) {
            setLockCredentialInternal(credential, type, savedCredential, requestedQuality, userId,
            setLockCredentialInternal(credential, type, savedCredential, requestedQuality, userId,
                    allowUntrustedChange);
                    allowUntrustedChange, /* isLockTiedToParent= */ false);
            setSeparateProfileChallengeEnabledLocked(userId, true, null);
            setSeparateProfileChallengeEnabledLocked(userId, true, null);
            notifyPasswordChanged(userId);
            notifyPasswordChanged(userId);
        }
        }
        notifySeparateProfileChallengeChanged(userId);
        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,
    private void setLockCredentialInternal(byte[] credential, @CredentialType int credentialType,
            byte[] savedCredential, int requestedQuality, int userId,
            byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange,
            boolean allowUntrustedChange) throws RemoteException {
            boolean isLockTiedToParent) throws RemoteException {
        // Normalize savedCredential and credential such that empty string is always represented
        // Normalize savedCredential and credential such that empty string is always represented
        // as null.
        // as null.
        if (savedCredential == null || savedCredential.length == 0) {
        if (savedCredential == null || savedCredential.length == 0) {
@@ -1363,7 +1430,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        synchronized (mSpManager) {
        synchronized (mSpManager) {
            if (isSyntheticPasswordBasedCredentialLocked(userId)) {
            if (isSyntheticPasswordBasedCredentialLocked(userId)) {
                spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
                spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
                        requestedQuality, userId, allowUntrustedChange);
                        requestedQuality, userId, allowUntrustedChange, isLockTiedToParent);
                return;
                return;
            }
            }
        }
        }
@@ -1379,7 +1446,8 @@ public class LockSettingsService extends ILockSettings.Stub {
            fixateNewestUserKeyAuth(userId);
            fixateNewestUserKeyAuth(userId);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
            notifyActivePasswordMetricsAvailable(CREDENTIAL_TYPE_NONE, null, userId);
            notifyActivePasswordMetricsAvailable(CREDENTIAL_TYPE_NONE, null, userId);
            mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential, userId);
            sendCredentialsOnChangeIfRequired(
                    credentialType, credential, userId, isLockTiedToParent);
            return;
            return;
        }
        }
        if (credential == null) {
        if (credential == null) {
@@ -1414,7 +1482,7 @@ public class LockSettingsService extends ILockSettings.Stub {
                initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential,
                initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential,
                        currentHandle.type, requestedQuality, userId);
                        currentHandle.type, requestedQuality, userId);
                spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
                spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
                        requestedQuality, userId, allowUntrustedChange);
                        requestedQuality, userId, allowUntrustedChange, isLockTiedToParent);
                return;
                return;
            }
            }
        }
        }
@@ -1432,8 +1500,8 @@ public class LockSettingsService extends ILockSettings.Stub {
            // Refresh the auth token
            // Refresh the auth token
            doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */);
            doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
            mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential,
            sendCredentialsOnChangeIfRequired(
                userId);
                    credentialType, credential, userId, isLockTiedToParent);
        } else {
        } else {
            throw new RemoteException("Failed to enroll " +
            throw new RemoteException("Failed to enroll " +
                    (credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
                    (credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
@@ -1674,8 +1742,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        // The user employs synthetic password based credential.
        // The user employs synthetic password based credential.
        if (response != null) {
        if (response != null) {
            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
                mRecoverableKeyStoreManager.lockScreenSecretAvailable(credentialType, credential,
                sendCredentialsOnUnlockIfRequired(credentialType, credential, userId);
                        userId);
            }
            }
            return response;
            return response;
        }
        }
@@ -1709,7 +1776,8 @@ public class LockSettingsService extends ILockSettings.Stub {
            mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
            mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
            if (shouldReEnrollBaseZero) {
            if (shouldReEnrollBaseZero) {
                setLockCredentialInternal(credential, storedHash.type, credentialToVerify,
                setLockCredentialInternal(credential, storedHash.type, credentialToVerify,
                        DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId, false);
                        DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId, false,
                        /* isLockTiedToParent= */ false);
            }
            }
        }
        }


@@ -1800,12 +1868,12 @@ public class LockSettingsService extends ILockSettings.Stub {
                        storedHash.type == CREDENTIAL_TYPE_PATTERN
                        storedHash.type == CREDENTIAL_TYPE_PATTERN
                                ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
                                ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
                                : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
                                : 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) {
                if (!hasChallenge) {
                    notifyActivePasswordMetricsAvailable(storedHash.type, credential, userId);
                    notifyActivePasswordMetricsAvailable(storedHash.type, credential, userId);
                    // Use credentials to create recoverable keystore snapshot.
                    // Use credentials to create recoverable keystore snapshot.
                    mRecoverableKeyStoreManager.lockScreenSecretAvailable(
                    sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId);
                            storedHash.type, credential, userId);
                    return VerifyCredentialResponse.OK;
                    return VerifyCredentialResponse.OK;
                }
                }
                // Fall through to get the auth token. Technically this should never happen,
                // Fall through to get the auth token. Technically this should never happen,
@@ -1845,7 +1913,7 @@ public class LockSettingsService extends ILockSettings.Stub {
                    /* TODO(roosa): keep the same password quality */;
                    /* TODO(roosa): keep the same password quality */;
            if (shouldReEnroll) {
            if (shouldReEnroll) {
                setLockCredentialInternal(credential, storedHash.type, credential,
                setLockCredentialInternal(credential, storedHash.type, credential,
                        reEnrollQuality, userId, false);
                        reEnrollQuality, userId, false, /* isLockTiedToParent= */ false);
            } else {
            } else {
                // Now that we've cleared of all required GK migration, let's do the final
                // Now that we've cleared of all required GK migration, let's do the final
                // migration to synthetic password.
                // migration to synthetic password.
@@ -1859,8 +1927,7 @@ public class LockSettingsService extends ILockSettings.Stub {
                }
                }
            }
            }
            // Use credentials to create recoverable keystore snapshot.
            // Use credentials to create recoverable keystore snapshot.
            mRecoverableKeyStoreManager.lockScreenSecretAvailable(storedHash.type, credential,
            sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId);
                userId);


        } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
        } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
            if (response.getTimeout() > 0) {
            if (response.getTimeout() > 0) {
@@ -2549,7 +2616,7 @@ public class LockSettingsService extends ILockSettings.Stub {
    @GuardedBy("mSpManager")
    @GuardedBy("mSpManager")
    private void spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType,
    private void spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType,
            byte[] savedCredential, int requestedQuality, int userId,
            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 (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
        if (isManagedProfileWithUnifiedLock(userId)) {
        if (isManagedProfileWithUnifiedLock(userId)) {
            // get credential from keystore when managed profile has unified lock
            // get credential from keystore when managed profile has unified lock
@@ -2615,7 +2682,7 @@ public class LockSettingsService extends ILockSettings.Stub {
            // requestedQuality, userId) instead if we still allow untrusted reset that changes
            // requestedQuality, userId) instead if we still allow untrusted reset that changes
            // synthetic password. That would invalidate existing escrow tokens though.
            // synthetic password. That would invalidate existing escrow tokens though.
        }
        }
        mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential, userId);
        sendCredentialsOnChangeIfRequired(credentialType, credential, userId, isLockTiedToParent);
    }
    }


    /**
    /**
+8 −2
Original line number Original line Diff line number Diff line
@@ -46,6 +46,7 @@ import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
import com.android.internal.widget.LockSettingsInternal;
import com.android.server.LocalServices;
import com.android.server.LocalServices;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal;


import org.mockito.invocation.InvocationOnMock;
import org.mockito.invocation.InvocationOnMock;
@@ -89,6 +90,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
    WindowManagerInternal mMockWindowManager;
    WindowManagerInternal mMockWindowManager;
    FakeGsiService mGsiService;
    FakeGsiService mGsiService;
    PasswordSlotManagerTestable mPasswordSlotManager;
    PasswordSlotManagerTestable mPasswordSlotManager;
    RecoverableKeyStoreManager mRecoverableKeyStoreManager;
    protected boolean mHasSecureLockScreen;
    protected boolean mHasSecureLockScreen;


    @Override
    @Override
@@ -105,6 +107,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
        mMockWindowManager = mock(WindowManagerInternal.class);
        mMockWindowManager = mock(WindowManagerInternal.class);
        mGsiService = new FakeGsiService();
        mGsiService = new FakeGsiService();
        mPasswordSlotManager = new PasswordSlotManagerTestable();
        mPasswordSlotManager = new PasswordSlotManagerTestable();
        mRecoverableKeyStoreManager = mock(RecoverableKeyStoreManager.class);


        LocalServices.removeServiceForTest(LockSettingsInternal.class);
        LocalServices.removeServiceForTest(LockSettingsInternal.class);
        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
@@ -141,12 +144,14 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
        mAuthSecretService = mock(IAuthSecret.class);
        mAuthSecretService = mock(IAuthSecret.class);
        mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
        mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
                mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
                mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
                mSpManager, mAuthSecretService, mGsiService);
                mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager);
        when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
        when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
        mPrimaryUserProfiles.add(PRIMARY_USER_INFO);
        mPrimaryUserProfiles.add(PRIMARY_USER_INFO);
        installChildProfile(MANAGED_PROFILE_USER_ID);
        installChildProfile(MANAGED_PROFILE_USER_ID);
        installAndTurnOffChildProfile(TURNED_OFF_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);
        when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(SECONDARY_USER_INFO);


        final ArrayList<UserInfo> allUsers = new ArrayList<>(mPrimaryUserProfiles);
        final ArrayList<UserInfo> allUsers = new ArrayList<>(mPrimaryUserProfiles);
@@ -173,6 +178,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
    private UserInfo installChildProfile(int profileId) {
    private UserInfo installChildProfile(int profileId) {
        final UserInfo userInfo = new UserInfo(
        final UserInfo userInfo = new UserInfo(
            profileId, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MANAGED_PROFILE);
            profileId, null, null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MANAGED_PROFILE);
        userInfo.profileGroupId = PRIMARY_USER_ID;
        mPrimaryUserProfiles.add(userInfo);
        mPrimaryUserProfiles.add(userInfo);
        when(mUserManager.getUserInfo(eq(profileId))).thenReturn(userInfo);
        when(mUserManager.getUserInfo(eq(profileId))).thenReturn(userInfo);
        when(mUserManager.getProfileParent(eq(profileId))).thenReturn(PRIMARY_USER_INFO);
        when(mUserManager.getProfileParent(eq(profileId))).thenReturn(PRIMARY_USER_INFO);
+13 −3
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ import android.security.KeyStore;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyPermanentlyInvalidatedException;


import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;


import java.io.FileNotFoundException;
import java.io.FileNotFoundException;


@@ -45,11 +46,13 @@ public class LockSettingsServiceTestable extends LockSettingsService {
        private SyntheticPasswordManager mSpManager;
        private SyntheticPasswordManager mSpManager;
        private IAuthSecret mAuthSecretService;
        private IAuthSecret mAuthSecretService;
        private FakeGsiService mGsiService;
        private FakeGsiService mGsiService;
        private RecoverableKeyStoreManager mRecoverableKeyStoreManager;


        public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
        public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
                IActivityManager activityManager, LockPatternUtils lockPatternUtils,
                IActivityManager activityManager, LockPatternUtils lockPatternUtils,
                IStorageManager storageManager, SyntheticPasswordManager spManager,
                IStorageManager storageManager, SyntheticPasswordManager spManager,
                IAuthSecret authSecretService, FakeGsiService gsiService) {
                IAuthSecret authSecretService, FakeGsiService gsiService,
                RecoverableKeyStoreManager recoverableKeyStoreManager) {
            super(context);
            super(context);
            mLockSettingsStorage = storage;
            mLockSettingsStorage = storage;
            mKeyStore = keyStore;
            mKeyStore = keyStore;
@@ -58,6 +61,7 @@ public class LockSettingsServiceTestable extends LockSettingsService {
            mStorageManager = storageManager;
            mStorageManager = storageManager;
            mSpManager = spManager;
            mSpManager = spManager;
            mGsiService = gsiService;
            mGsiService = gsiService;
            mRecoverableKeyStoreManager = recoverableKeyStoreManager;
        }
        }


        @Override
        @Override
@@ -119,15 +123,21 @@ public class LockSettingsServiceTestable extends LockSettingsService {
        public boolean isGsiRunning() {
        public boolean isGsiRunning() {
            return mGsiService.isGsiRunning();
            return mGsiService.isGsiRunning();
        }
        }

        @Override
        public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) {
            return mRecoverableKeyStoreManager;
        }
    }
    }


    protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
    protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
            LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
            LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
            IStorageManager storageManager, IActivityManager mActivityManager,
            IStorageManager storageManager, IActivityManager mActivityManager,
            SyntheticPasswordManager spManager, IAuthSecret authSecretService,
            SyntheticPasswordManager spManager, IAuthSecret authSecretService,
            FakeGsiService gsiService) {
            FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager) {
        super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
        super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
                storageManager, spManager, authSecretService, gsiService));
                storageManager, spManager, authSecretService, gsiService,
                recoverableKeyStoreManager));
        mGateKeeperService = gatekeeper;
        mGateKeeperService = gatekeeper;
        mAuthSecretService = authSecretService;
        mAuthSecretService = authSecretService;
    }
    }
+223 −0

File changed.

Preview size limit exceeded, changes collapsed.

+22 −2
Original line number Original line Diff line number Diff line
@@ -58,6 +58,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
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.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager;
import com.android.server.locksettings.recoverablekeystore.storage.CleanupManager;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
@@ -83,7 +84,7 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Map;
import java.util.Map;
import java.util.Random;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;


import javax.crypto.KeyGenerator;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKey;
@@ -156,6 +157,7 @@ public class RecoverableKeyStoreManagerTest {
    @Mock private PlatformKeyManager mPlatformKeyManager;
    @Mock private PlatformKeyManager mPlatformKeyManager;
    @Mock private ApplicationKeyStorage mApplicationKeyStorage;
    @Mock private ApplicationKeyStorage mApplicationKeyStorage;
    @Mock private CleanupManager mCleanupManager;
    @Mock private CleanupManager mCleanupManager;
    @Mock private ExecutorService mExecutorService;
    @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
    @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;


    private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
    private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
@@ -188,7 +190,7 @@ public class RecoverableKeyStoreManagerTest {
                mMockContext,
                mMockContext,
                mRecoverableKeyStoreDb,
                mRecoverableKeyStoreDb,
                mRecoverySessionStorage,
                mRecoverySessionStorage,
                Executors.newSingleThreadExecutor(),
                mExecutorService,
                mRecoverySnapshotStorage,
                mRecoverySnapshotStorage,
                mMockListenersStorage,
                mMockListenersStorage,
                mPlatformKeyManager,
                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(
    private static byte[] encryptedApplicationKey(
            SecretKey recoveryKey, byte[] applicationKey) throws Exception {
            SecretKey recoveryKey, byte[] applicationKey) throws Exception {
        return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
        return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(