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

Commit 68a84962 authored by Dmitry Dementyev's avatar Dmitry Dementyev
Browse files

Implement validateRemoteLockscreen.

Bug: 254335492
Test: atest com.android.server.locksettings.recoverablekeystore
Change-Id: Iedcf1bece979cb277b8ccf43b2d4f92ce6c4bfee
parent 90ce83a6
Loading
Loading
Loading
Loading
+97 −12
Original line number Original line Diff line number Diff line
@@ -49,6 +49,9 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
import com.android.internal.util.HexDump;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.security.SecureBox;
import com.android.security.SecureBox;
import com.android.server.locksettings.LockSettingsService;
import com.android.server.locksettings.LockSettingsService;
import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
@@ -65,6 +68,7 @@ import com.android.server.locksettings.recoverablekeystore.storage.RemoteLockscr
import com.android.server.locksettings.recoverablekeystore.storage.RemoteLockscreenValidationSessionStorage.LockscreenVerificationSession;
import com.android.server.locksettings.recoverablekeystore.storage.RemoteLockscreenValidationSessionStorage.LockscreenVerificationSession;


import java.io.IOException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchAlgorithmException;
@@ -97,6 +101,9 @@ import javax.crypto.AEADBadTagException;
public class RecoverableKeyStoreManager {
public class RecoverableKeyStoreManager {
    private static final String TAG = "RecoverableKeyStoreMgr";
    private static final String TAG = "RecoverableKeyStoreMgr";
    private static final long SYNC_DELAY_MILLIS = 2000;
    private static final long SYNC_DELAY_MILLIS = 2000;
    private static final int INVALID_REMOTE_GUESS_LIMIT = 5;
    public static final byte[] ENCRYPTED_REMOTE_CREDENTIALS_HEADER =
            "encrypted_remote_credentials".getBytes(StandardCharsets.UTF_8);


    private static RecoverableKeyStoreManager mInstance;
    private static RecoverableKeyStoreManager mInstance;


@@ -995,7 +1002,7 @@ public class RecoverableKeyStoreManager {
     * Starts a session to verify lock screen credentials provided by a remote device.
     * Starts a session to verify lock screen credentials provided by a remote device.
     */
     */
    public StartLockscreenValidationRequest startRemoteLockscreenValidation(
    public StartLockscreenValidationRequest startRemoteLockscreenValidation(
            LockSettingsService lockSettingService) {
            LockSettingsService lockSettingsService) {
        if (mRemoteLockscreenValidationSessionStorage == null) {
        if (mRemoteLockscreenValidationSessionStorage == null) {
            throw new UnsupportedOperationException("Under development");
            throw new UnsupportedOperationException("Under development");
        }
        }
@@ -1004,40 +1011,118 @@ public class RecoverableKeyStoreManager {
        int savedCredentialType;
        int savedCredentialType;
        final long token = Binder.clearCallingIdentity();
        final long token = Binder.clearCallingIdentity();
        try {
        try {
            savedCredentialType = lockSettingService.getCredentialType(userId);
            savedCredentialType = lockSettingsService.getCredentialType(userId);
        } finally {
        } finally {
            Binder.restoreCallingIdentity(token);
            Binder.restoreCallingIdentity(token);
        }
        }
        int keyguardCredentailsType = lockPatternUtilsToKeyguardType(savedCredentialType);
        int keyguardCredentialsType = lockPatternUtilsToKeyguardType(savedCredentialType);
        LockscreenVerificationSession session =
        LockscreenVerificationSession session =
                mRemoteLockscreenValidationSessionStorage.startSession(userId);
                mRemoteLockscreenValidationSessionStorage.startSession(userId);
        PublicKey publicKey = session.getKeyPair().getPublic();
        PublicKey publicKey = session.getKeyPair().getPublic();
        byte[] encodedPublicKey = SecureBox.encodePublicKey(publicKey);
        byte[] encodedPublicKey = SecureBox.encodePublicKey(publicKey);
        int badGuesses = mDatabase.getBadRemoteGuessCounter(userId);
        int badGuesses = mDatabase.getBadRemoteGuessCounter(userId);
        int remainingAttempts = Math.max(INVALID_REMOTE_GUESS_LIMIT - badGuesses, 0);
        // TODO(b/254335492): Schedule task to remove inactive session
        return new StartLockscreenValidationRequest.Builder()
        return new StartLockscreenValidationRequest.Builder()
                .setLockscreenUiType(keyguardCredentailsType)
                .setLockscreenUiType(keyguardCredentialsType)
                .setSourcePublicKey(new byte[]{})
                .setRemainingAttempts(remainingAttempts)
                .setSourcePublicKey(encodedPublicKey)
                .build();
                .build();
    }
    }


    /**
    /**
     * Verifies encrypted credentials guess from a remote device.
     * Verifies encrypted credentials guess from a remote device.
     */
     */
    public RemoteLockscreenValidationResult validateRemoteLockscreen(
    public synchronized RemoteLockscreenValidationResult validateRemoteLockscreen(
            @NonNull byte[] encryptedCredential,
            @NonNull byte[] encryptedCredential,
            LockSettingsService lockSettingService) {
            LockSettingsService lockSettingsService) {
        if (mRemoteLockscreenValidationSessionStorage == null) {
            throw new UnsupportedOperationException("Under development");
        }
        checkVerifyRemoteLockscreenPermission();
        checkVerifyRemoteLockscreenPermission();
        int userId = UserHandle.getCallingUserId();
        int userId = UserHandle.getCallingUserId();
        LockscreenVerificationSession session =
        LockscreenVerificationSession session =
                mRemoteLockscreenValidationSessionStorage.get(userId);
                mRemoteLockscreenValidationSessionStorage.get(userId);
        int badGuesses = mDatabase.getBadRemoteGuessCounter(userId);
        int remainingAttempts = INVALID_REMOTE_GUESS_LIMIT - badGuesses;
        if (remainingAttempts <= 0) {
            return new RemoteLockscreenValidationResult.Builder()
                .setResultCode(RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS)
                .build();
        }
        if (session == null) {
        if (session == null) {
            throw new IllegalStateException("There is no active lock screen check session");
            throw new IllegalStateException("There is no active lock screen check session");
        }
        }
        // TODO(b/254335492): Call lockSettingService.verifyCredential
        byte[] decryptedCredentials;
        return new RemoteLockscreenValidationResult.Builder().build();
        try {
            decryptedCredentials = SecureBox.decrypt(
                session.getKeyPair().getPrivate(),
                /* sharedSecret= */ null,
                ENCRYPTED_REMOTE_CREDENTIALS_HEADER,
                encryptedCredential);
        } catch (NoSuchAlgorithmException e) {
            Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e);
            throw new IllegalStateException(e);
        } catch (InvalidKeyException e) {
            Log.e(TAG, "Got InvalidKeyException during lock screen credentials decryption");
            throw new IllegalStateException(e);
        } catch (AEADBadTagException e) {
            throw new IllegalStateException("Could not decrypt credentials guess", e);
        }
        int savedCredentialType;
        final long token = Binder.clearCallingIdentity();
        try {
            savedCredentialType = lockSettingsService.getCredentialType(userId);
            int keyguardCredentialsType = lockPatternUtilsToKeyguardType(savedCredentialType);
            try (LockscreenCredential credential =
                    createLockscreenCredential(keyguardCredentialsType, decryptedCredentials)) {
                // TODO(b/254335492): remove decryptedCredentials
                VerifyCredentialResponse verifyResponse =
                        lockSettingsService.verifyCredential(credential, userId, 0);
                return handleVerifyCredentialResponse(verifyResponse, userId);
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private RemoteLockscreenValidationResult handleVerifyCredentialResponse(
            VerifyCredentialResponse response, int userId) {
        if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
            mDatabase.setBadRemoteGuessCounter(userId, 0);
            mRemoteLockscreenValidationSessionStorage.finishSession(userId);
            return new RemoteLockscreenValidationResult.Builder()
                    .setResultCode(RemoteLockscreenValidationResult.RESULT_GUESS_VALID)
                    .build();
        }
        if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
            long timeout = (long) response.getTimeout();
            return new RemoteLockscreenValidationResult.Builder()
                    .setResultCode(RemoteLockscreenValidationResult.RESULT_LOCKOUT)
                    .setTimeoutMillis(timeout)
                    .build();
        }
        // Invalid guess
        int badGuesses = mDatabase.getBadRemoteGuessCounter(userId);
        mDatabase.setBadRemoteGuessCounter(userId, badGuesses + 1);
        return new RemoteLockscreenValidationResult.Builder()
                .setResultCode(RemoteLockscreenValidationResult.RESULT_GUESS_INVALID)
                .build();
    }

    private LockscreenCredential createLockscreenCredential(
            int lockType, byte[] password) {
        switch (lockType) {
            case KeyguardManager.PASSWORD:
                CharSequence passwordStr = new String(password, StandardCharsets.UTF_8);
                return LockscreenCredential.createPassword(passwordStr);
            case KeyguardManager.PIN:
                CharSequence pinStr = new String(password);
                return LockscreenCredential.createPin(pinStr);
            case KeyguardManager.PATTERN:
                List<LockPatternView.Cell> pattern =
                        LockPatternUtils.byteArrayToPattern(password);
                return LockscreenCredential.createPattern(pattern);
            default:
                throw new IllegalStateException("Lockscreen is not set");
        }
    }
    }


    private void checkVerifyRemoteLockscreenPermission() {
    private void checkVerifyRemoteLockscreenPermission() {
+125 −16
Original line number Original line Diff line number Diff line
@@ -40,6 +40,7 @@ import static org.mockito.Mockito.when;
import android.Manifest;
import android.Manifest;
import android.app.KeyguardManager;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.PendingIntent;
import android.app.RemoteLockscreenValidationResult;
import android.app.StartLockscreenValidationRequest;
import android.app.StartLockscreenValidationRequest;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
@@ -61,6 +62,8 @@ import androidx.test.runner.AndroidJUnit4;


import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.security.SecureBox;
import com.android.security.SecureBox;
import com.android.server.locksettings.LockSettingsService;
import com.android.server.locksettings.LockSettingsService;
import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
@@ -84,10 +87,12 @@ import org.mockito.Spy;


import java.io.File;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.security.cert.CertPath;
import java.security.cert.CertPath;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.Map;
import java.util.Random;
import java.util.Random;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledExecutorService;
@@ -155,6 +160,10 @@ public class RecoverableKeyStoreManagerTest {
            .setKeyDerivationParams(KeyDerivationParams.createSha256Params(TEST_SALT))
            .setKeyDerivationParams(KeyDerivationParams.createSha256Params(TEST_SALT))
            .setSecret(TEST_SECRET)
            .setSecret(TEST_SECRET)
            .build();
            .build();
    private static final byte[] VALID_GUESS = getUtf8Bytes("password123");
    private static final byte[] INVALID_GUESS = getUtf8Bytes("not_password");
    private static final byte[] GUESS_LOCKOUT = getUtf8Bytes("need_to_wait");
    private static final int TIMEOUT_MILLIS = 30 * 1000;


    @Mock private Context mMockContext;
    @Mock private Context mMockContext;
    @Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
    @Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
@@ -208,10 +217,20 @@ public class RecoverableKeyStoreManagerTest {
                mTestOnlyInsecureCertificateHelper,
                mTestOnlyInsecureCertificateHelper,
                mCleanupManager,
                mCleanupManager,
                mRemoteLockscreenValidationSessionStorage);
                mRemoteLockscreenValidationSessionStorage);
        when(mLockSettingsService.verifyCredential(
                any(LockscreenCredential.class), anyInt(), anyInt())).thenAnswer(args -> {
                    LockscreenCredential argument = (LockscreenCredential) args.getArguments()[0];
                    if (Arrays.equals(argument.getCredential(), VALID_GUESS)) {
                        return VerifyCredentialResponse.OK;
                    } else if (Arrays.equals(argument.getCredential(), INVALID_GUESS)) {
                        return VerifyCredentialResponse.ERROR;
                    } else return VerifyCredentialResponse.fromTimeout(TIMEOUT_MILLIS);
                });
    }
    }


    @After
    @After
    public void tearDown() {
    public void tearDown() {
        mRemoteLockscreenValidationSessionStorage.finishSession(mUserId);
        mRecoverableKeyStoreDb.close();
        mRecoverableKeyStoreDb.close();
        mDatabaseFile.delete();
        mDatabaseFile.delete();
    }
    }
@@ -1289,73 +1308,163 @@ public class RecoverableKeyStoreManagerTest {
            assertThat(e.getMessage()).contains("not set");
            assertThat(e.getMessage()).contains("not set");
        }
        }
        verify(mLockSettingsService).getCredentialType(mUserId);
        verify(mLockSettingsService).getCredentialType(mUserId);
        mRemoteLockscreenValidationSessionStorage.finishSession(mUserId);
    }
    }

    @Test
    @Test
    public void startRemoteLockscreenValidation_checksPermission() throws Exception {
    public void startRemoteLockscreenValidation_checksPermission() throws Exception {
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                LockPatternUtils.CREDENTIAL_TYPE_PIN);
                LockPatternUtils.CREDENTIAL_TYPE_PIN);

        mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
        mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);

        // TODO(b/254335492): Check new system permission
        // TODO(b/254335492): Check new system permission
        verify(mMockContext, times(1))
        verify(mMockContext, times(1))
                .enforceCallingOrSelfPermission(
                .enforceCallingOrSelfPermission(
                        eq(Manifest.permission.RECOVER_KEYSTORE), any());
                        eq(Manifest.permission.RECOVER_KEYSTORE), any());
        mRemoteLockscreenValidationSessionStorage.finishSession(mUserId);
    }
    }

    @Test
    @Test
    public void startRemoteLockscreenValidation_returnsCredentailsType() throws Exception {
    public void startRemoteLockscreenValidation_returnsCredentailsType() throws Exception {
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                LockPatternUtils.CREDENTIAL_TYPE_PIN);
                LockPatternUtils.CREDENTIAL_TYPE_PIN);

        StartLockscreenValidationRequest request =
        StartLockscreenValidationRequest request =
                mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
                mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
        int credetialsType = request.getLockscreenUiType();


        int credetialsType = request.getLockscreenUiType();
        assertThat(credetialsType).isEqualTo(KeyguardManager.PIN);
        assertThat(credetialsType).isEqualTo(KeyguardManager.PIN);

        assertThat(request.getRemainingAttempts()).isEqualTo(5);
        verify(mLockSettingsService).getCredentialType(anyInt());
        verify(mLockSettingsService).getCredentialType(anyInt());
        mRemoteLockscreenValidationSessionStorage.finishSession(mUserId);
    }
    }

    @Test
    @Test
    public void startRemoteLockscreenValidation_returnsRemainingAttempts() throws Exception {
    public void startRemoteLockscreenValidation_returnsRemainingAttempts() throws Exception {
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
                LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
        mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 3);
        mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 3);

        StartLockscreenValidationRequest request =
        StartLockscreenValidationRequest request =
                mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
                mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);

        int credetialsType = request.getLockscreenUiType();
        int credetialsType = request.getLockscreenUiType();
        assertThat(credetialsType).isEqualTo(KeyguardManager.PATTERN);
        assertThat(credetialsType).isEqualTo(KeyguardManager.PATTERN);
        // TODO(b/254335492): Verify returned value
        assertThat(request.getRemainingAttempts()).isEqualTo(2);
        mRemoteLockscreenValidationSessionStorage.finishSession(mUserId);
    }
    }

    @Test
    @Test
    public void startRemoteLockscreenValidation_password() throws Exception {
    public void startRemoteLockscreenValidation_password() throws Exception {
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
        mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 3);
        mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 7);

        StartLockscreenValidationRequest request =
        StartLockscreenValidationRequest request =
                mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
                mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);

        int credetialsType = request.getLockscreenUiType();
        int credetialsType = request.getLockscreenUiType();
        assertThat(request.getRemainingAttempts()).isEqualTo(0);
        assertThat(credetialsType).isEqualTo(KeyguardManager.PASSWORD);
        assertThat(credetialsType).isEqualTo(KeyguardManager.PASSWORD);
        mRemoteLockscreenValidationSessionStorage.finishSession(mUserId);
    }
    }

    @Test
    @Test
    public void validateRemoteLockscreen_noActiveSession() throws Exception {
    public void validateRemoteLockscreen_noActiveSession() throws Exception {
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                LockPatternUtils.CREDENTIAL_TYPE_NONE);
                LockPatternUtils.CREDENTIAL_TYPE_NONE);
        byte[] invalidGuess = new byte[]{1, 2, 3};
        try {
        try {
            mRecoverableKeyStoreManager.validateRemoteLockscreen(invalidGuess,
            mRecoverableKeyStoreManager.validateRemoteLockscreen(INVALID_GUESS,
                    mLockSettingsService);
                    mLockSettingsService);
            fail("should have thrown");
            fail("should have thrown");
        } catch (IllegalStateException e) {
        } catch (IllegalStateException e) {
            assertThat(e.getMessage()).contains("session");
            assertThat(e.getMessage()).contains("session");
        }
        }
    }
    }
    @Test
    public void validateRemoteLockscreen_decryptionError() throws Exception {
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
        mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 4);

        mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);

        try {
            mRecoverableKeyStoreManager.validateRemoteLockscreen(
                        new byte[] {1, 2, 3},
                        mLockSettingsService);
            fail("should have thrown");
        } catch (IllegalStateException e) {
            // Decryption error
        }
    }
    @Test
    public void validateRemoteLockscreen_zeroRemainingAttempts() throws Exception {
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
        mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 5);
        mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);

        RemoteLockscreenValidationResult result =
                    mRecoverableKeyStoreManager.validateRemoteLockscreen(
                    encryptCredentialsForNewSession(VALID_GUESS),
                        mLockSettingsService);

        assertThat(result.getResultCode()).isEqualTo(
                RemoteLockscreenValidationResult.RESULT_NO_REMAINING_ATTEMPTS);
    }
    @Test
    public void validateRemoteLockscreen_guessValid() throws Exception {
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
        mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 4);
        mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);

        RemoteLockscreenValidationResult result =
                mRecoverableKeyStoreManager.validateRemoteLockscreen(
                        encryptCredentialsForNewSession(VALID_GUESS),
                        mLockSettingsService);

        assertThat(result.getResultCode()).isEqualTo(
                RemoteLockscreenValidationResult.RESULT_GUESS_VALID);
        // Valid guess resets counter
        assertThat(mRecoverableKeyStoreDb.getBadRemoteGuessCounter(mUserId)).isEqualTo(0);
    }
    @Test
    public void validateRemoteLockscreen_timeout() throws Exception {
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
        mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 4);

        RemoteLockscreenValidationResult result =
                mRecoverableKeyStoreManager.validateRemoteLockscreen(
                        encryptCredentialsForNewSession(GUESS_LOCKOUT),
                        mLockSettingsService);

        assertThat(result.getResultCode()).isEqualTo(
                RemoteLockscreenValidationResult.RESULT_LOCKOUT);
        assertThat(result.getTimeoutMillis()).isEqualTo((long) TIMEOUT_MILLIS);
        // Counter was not changed
        assertThat(mRecoverableKeyStoreDb.getBadRemoteGuessCounter(mUserId)).isEqualTo(4);
    }
    @Test
    public void validateRemoteLockscreen_guessInvalid() throws Exception {
        when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
        mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 4);
        mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);

        RemoteLockscreenValidationResult result =
                mRecoverableKeyStoreManager.validateRemoteLockscreen(
                        encryptCredentialsForNewSession(INVALID_GUESS),
                        mLockSettingsService);

        assertThat(result.getResultCode()).isEqualTo(
                RemoteLockscreenValidationResult.RESULT_GUESS_INVALID);
        assertThat(mRecoverableKeyStoreDb.getBadRemoteGuessCounter(mUserId)).isEqualTo(5);
    }

    private byte[] encryptCredentialsForNewSession(byte[] credentials) throws Exception {
        StartLockscreenValidationRequest request =
                mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
        PublicKey publicKey = SecureBox.decodePublicKey(request.getSourcePublicKey());
        return SecureBox.encrypt(
              publicKey,
              /* sharedSecret= */ null,
              RecoverableKeyStoreManager.ENCRYPTED_REMOTE_CREDENTIALS_HEADER,
              credentials);
    }


    private static byte[] encryptedApplicationKey(
    private static byte[] encryptedApplicationKey(
            SecretKey recoveryKey, byte[] applicationKey) throws Exception {
            SecretKey recoveryKey, byte[] applicationKey) throws Exception {