Loading services/core/java/com/android/server/locksettings/LockSettingsService.java +6 −1 Original line number Diff line number Diff line Loading @@ -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)) { Loading services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +17 −0 Original line number Diff line number Diff line Loading @@ -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; Loading services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +26 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 Loading @@ -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(); Loading services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java +4 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,10 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager { return mWeaverHidl; } public boolean isWeaverEnabled() { return mWeaverService != null; } public int getSumOfWeaverFailureCounters() { return mWeaverService.getSumOfFailureCounters(); } Loading services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +30 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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}; Loading Loading @@ -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"); Loading @@ -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 */) Loading Loading
services/core/java/com/android/server/locksettings/LockSettingsService.java +6 −1 Original line number Diff line number Diff line Loading @@ -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)) { Loading
services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +17 −0 Original line number Diff line number Diff line Loading @@ -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; Loading
services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +26 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 Loading @@ -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(); Loading
services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java +4 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,10 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager { return mWeaverHidl; } public boolean isWeaverEnabled() { return mWeaverService != null; } public int getSumOfWeaverFailureCounters() { return mWeaverService.getSumOfFailureCounters(); } Loading
services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +30 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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}; Loading Loading @@ -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"); Loading @@ -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 */) Loading