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

Commit b99e9ff1 authored by Eric Biggers's avatar Eric Biggers Committed by Android (Google) Code Review
Browse files

Merge "Return new error codes in VerifyCredentialResponse" into main

parents b90ef45b 4014800f
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -2479,9 +2479,14 @@ public class LockSettingsService extends ILockSettings.Stub {
                    case SoftwareRateLimiterResult.RATE_LIMITED:
                        return VerifyCredentialResponse.fromTimeout(res.remainingDelay);
                    case SoftwareRateLimiterResult.CREDENTIAL_TOO_SHORT:
                        return VerifyCredentialResponse.fromError(
                                VerifyCredentialResponse.RESPONSE_CRED_TOO_SHORT);
                    case SoftwareRateLimiterResult.DUPLICATE_WRONG_GUESS:
                        return VerifyCredentialResponse.fromError(
                                VerifyCredentialResponse.RESPONSE_CRED_ALREADY_TRIED);
                    default:
                        return VerifyCredentialResponse.fromError();
                        return VerifyCredentialResponse.fromError(
                                VerifyCredentialResponse.RESPONSE_OTHER_ERROR);
                }
            }
            if (isSpecialUserId(userId)) {
+17 −0
Original line number Diff line number Diff line
@@ -719,11 +719,28 @@ class SyntheticPasswordManager {
            case WeaverReadStatus.OK:
                return VerifyCredentialResponse.OK;
            case WeaverReadStatus.THROTTLE:
                // Either the credential could not be verified because a timeout is still active, or
                // the credential was incorrect and there is a timeout before the next attempt will
                // be allowed. INCORRECT_KEY is preferred in the latter case to avoid the ambiguity,
                // but we still have to support implementations that use THROTTLE for both cases.
                //
                // TODO(b/395976735): needs unit testing via MockWeaverService
                return responseFromTimeout(weaverResponse);
            case WeaverReadStatus.INCORRECT_KEY:
                if (weaverResponse.timeout != 0) {
                    // The credential was incorrect, and there is a timeout until the next attempt
                    // will be allowed. This is reached if the Weaver implementation returns
                    // INCORRECT_KEY in this case instead of THROTTLE.
                    //
                    // TODO(b/395976735): use RESPONSE_CRED_INCORRECT in this case, and update users
                    // of VerifyCredentialResponse to be compatible with that.
                    // TODO(b/395976735): needs unit testing via MockWeaverService
                    return responseFromTimeout(weaverResponse);
                }
                if (android.security.Flags.softwareRatelimiter()) {
                    return VerifyCredentialResponse.fromError(
                            VerifyCredentialResponse.RESPONSE_CRED_INCORRECT);
                }
                break;
        }
        return VerifyCredentialResponse.OTHER_ERROR;
+26 −0
Original line number Diff line number Diff line
@@ -656,6 +656,11 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
            VerifyCredentialResponse response =
                    mService.verifyCredential(wrongPin, userId, 0 /* flags */);
            assertFalse(response.isMatched());
            assertEquals(
                    i == 0
                            ? VerifyCredentialResponse.RESPONSE_CRED_INCORRECT
                            : VerifyCredentialResponse.RESPONSE_CRED_ALREADY_TRIED,
                    response.getResponseCode());
            assertEquals(0, response.getTimeout());
        }
        // The software and hardware counters should now be 1, for 1 unique guess.
@@ -685,6 +690,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
            VerifyCredentialResponse response =
                    mService.verifyCredential(wrongPin, userId, 0 /* flags */);
            assertFalse(response.isMatched());
            assertEquals(VerifyCredentialResponse.RESPONSE_OTHER_ERROR, response.getResponseCode());
        }
        // The software counter should still be 0, since the software rate-limiter is fully disabled
        // and thus it should have never been told about the guesses at all. The hardware counter
@@ -693,6 +699,26 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
        assertEquals(numGuesses, mSpManager.getSumOfWeaverFailureCounters());
    }

    @Test
    @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER)
    public void testVerifyCredentialTooShort() throws Exception {
        final int userId = PRIMARY_USER_ID;
        setCredential(userId, newPassword("password"));
        VerifyCredentialResponse response =
                mService.verifyCredential(newPassword("a"), userId, /* flags= */ 0);
        assertEquals(VerifyCredentialResponse.RESPONSE_CRED_TOO_SHORT, response.getResponseCode());
    }

    @Test
    @DisableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER)
    public void testVerifyCredentialTooShort_softwareRateLimiterFlagDisabled() throws Exception {
        final int userId = PRIMARY_USER_ID;
        setCredential(userId, newPassword("password"));
        VerifyCredentialResponse response =
                mService.verifyCredential(newPassword("a"), userId, /* flags= */ 0);
        assertEquals(VerifyCredentialResponse.RESPONSE_OTHER_ERROR, response.getResponseCode());
    }

    private void checkRecordedFrpNotificationIntent() {
        if (android.security.Flags.frpEnforcement()) {
            Intent savedNotificationIntent = mService.getSavedFrpNotificationIntent();
+4 −0
Original line number Diff line number Diff line
@@ -145,6 +145,10 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
        return mWeaverHidl;
    }

    public boolean isWeaverEnabled() {
        return mWeaverService != null;
    }

    public int getSumOfWeaverFailureCounters() {
        return mWeaverService.getSumOfFailureCounters();
    }
+30 −0
Original line number Diff line number Diff line
@@ -48,7 +48,10 @@ import static org.mockito.Mockito.when;
import android.app.admin.PasswordMetrics;
import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -61,6 +64,7 @@ import com.android.server.locksettings.SyntheticPasswordManager.SyntheticPasswor

import libcore.util.HexEncoding;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -76,6 +80,8 @@ import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {

    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    public static final byte[] PAYLOAD = new byte[] {1, 2, -1, -2, 55};
    public static final byte[] PAYLOAD2 = new byte[] {2, 3, -2, -3, 44, 1};

@@ -179,6 +185,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
    }

    @Test
    @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER)
    public void testVerifyCredential() throws RemoteException {
        LockscreenCredential password = newPassword("password");
        LockscreenCredential badPassword = newPassword("badpassword");
@@ -188,6 +195,29 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
                password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
        verify(mActivityManager).unlockUser2(eq(PRIMARY_USER_ID), any());

        int expectedResponseCode =
                mSpManager.isWeaverEnabled()
                        ? VerifyCredentialResponse.RESPONSE_CRED_INCORRECT
                        : VerifyCredentialResponse.RESPONSE_OTHER_ERROR;
        assertEquals(
                expectedResponseCode,
                mService.verifyCredential(badPassword, PRIMARY_USER_ID, 0 /* flags */)
                        .getResponseCode());
    }

    @Test
    @DisableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER)
    public void testVerifyCredential_softwareRateLimiterFlagDisabled() throws RemoteException {
        LockscreenCredential password = newPassword("password");
        LockscreenCredential badPassword = newPassword("badpassword");

        initSpAndSetCredential(PRIMARY_USER_ID, password);
        assertEquals(
                VerifyCredentialResponse.RESPONSE_OK,
                mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
                        .getResponseCode());
        verify(mActivityManager).unlockUser2(eq(PRIMARY_USER_ID), any());

        assertEquals(
                VerifyCredentialResponse.RESPONSE_OTHER_ERROR,
                mService.verifyCredential(badPassword, PRIMARY_USER_ID, 0 /* flags */)