Loading core/java/com/android/internal/widget/VerifyCredentialResponse.java +32 −8 Original line number Diff line number Diff line Loading @@ -45,9 +45,14 @@ public final class VerifyCredentialResponse implements Parcelable { private static final int RESPONSE_OK = 0; /** * 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. * {@link #getTimeout()} gives the timeout. * The credential could not be verified because a timeout is still active. {@link #getTimeout()} * gives the currently active timeout. * * <p>Alternatively, a timeout was not active, the credential was incorrect, and there is a * timeout before the <em>next</em> attempt will be allowed. {@link #getTimeout()} gives the * newly active timeout. The preferred response code in this case is {@link * #RESPONSE_CRED_INCORRECT}, but some devices use a rate-limiting HAL implementation that does * not differentiate this case from the "timeout is still active" case. */ private static final int RESPONSE_RETRY = 1; Loading @@ -60,6 +65,8 @@ public final class VerifyCredentialResponse implements Parcelable { /** * Credential was incorrect and none of {@link #RESPONSE_RETRY}, {@link * #RESPONSE_CRED_TOO_SHORT}, or {@link #RESPONSE_CRED_ALREADY_TRIED} applies. * * <p>{@link #getTimeout()} gives the newly active timeout, if any. */ private static final int RESPONSE_CRED_INCORRECT = 4; Loading Loading @@ -136,10 +143,8 @@ public final class VerifyCredentialResponse implements Parcelable { } /** * Since timeouts are always an error, provide a way to create the VerifyCredentialResponse * object directly. None of the other fields (Gatekeeper HAT, Gatekeeper Password, etc) * are valid in this case. Similarly, the response code will always be * {@link #RESPONSE_RETRY}. * Builds a {@link VerifyCredentialResponse} with {@link #RESPONSE_RETRY} and the given timeout * in milliseconds. */ public static VerifyCredentialResponse fromTimeout(int timeout) { return new VerifyCredentialResponse(RESPONSE_RETRY, Loading @@ -149,7 +154,7 @@ public final class VerifyCredentialResponse implements Parcelable { } /** * Like {@link #fromTimeout(int)}, but takes a Duration instead of a raw milliseconds value. * Builds a {@link VerifyCredentialResponse} with {@link #RESPONSE_RETRY} and the given timeout. * * <p>The timeout is clamped to fit in an int. See {@link #timeoutToClampedMillis(Duration)}. */ Loading @@ -157,6 +162,20 @@ public final class VerifyCredentialResponse implements Parcelable { return fromTimeout(timeoutToClampedMillis(timeout)); } /** * Builds a {@link VerifyCredentialResponse} with {@link #RESPONSE_CRED_INCORRECT} and the given * timeout. * * <p>The timeout is clamped to fit in an int. See {@link #timeoutToClampedMillis(Duration)}. */ public static VerifyCredentialResponse credIncorrect(Duration timeout) { return new VerifyCredentialResponse( VerifyCredentialResponse.RESPONSE_CRED_INCORRECT, timeoutToClampedMillis(timeout), /* gatekeeperHAT= */ null, /* gatekeeperPasswordHandle= */ 0L); } /** * Clamps the given timeout to fit in an int that holds a non-negative milliseconds value. * Loading Loading @@ -260,6 +279,11 @@ public final class VerifyCredentialResponse implements Parcelable { * will be allowed. */ public boolean hasTimeout() { if (android.security.Flags.softwareRatelimiter()) { // Check mTimeout directly. It can be nonzero for either RESPONSE_RETRY or // RESPONSE_CRED_INCORRECT. return mTimeout != 0; } return mResponseCode == RESPONSE_RETRY; } Loading services/core/java/com/android/server/locksettings/LockSettingsService.java +4 −1 Original line number Diff line number Diff line Loading @@ -2542,7 +2542,10 @@ public class LockSettingsService extends ILockSettings.Stub { // lock screen doesn't allow another attempt until both rate-limiters allow it. Duration hwTimeout = response.getTimeoutAsDuration(); if (swTimeout.compareTo(hwTimeout) > 0) { response = VerifyCredentialResponse.fromTimeout(swTimeout); response = isCertainlyWrongGuess ? VerifyCredentialResponse.credIncorrect(swTimeout) : VerifyCredentialResponse.fromTimeout(swTimeout); } } } Loading services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +7 −9 Original line number Diff line number Diff line Loading @@ -719,19 +719,17 @@ class SyntheticPasswordManager { // THROTTLE for both cases. VerifyCredentialResponse.fromTimeout(Duration.ofMillis(weaverResponse.timeout)); case WeaverReadStatus.INCORRECT_KEY -> { // The credential was incorrect. There may be a timeout until the next attempt is // allowed; that occurs when the Weaver implementation returns INCORRECT_KEY in this // case instead of THROTTLE. if (android.security.Flags.softwareRatelimiter()) { yield VerifyCredentialResponse.credIncorrect( Duration.ofMillis(weaverResponse.timeout)); } 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. yield VerifyCredentialResponse.fromTimeout( Duration.ofMillis(weaverResponse.timeout)); } if (android.security.Flags.softwareRatelimiter()) { yield VerifyCredentialResponse.credIncorrect(); } yield VerifyCredentialResponse.OTHER_ERROR; } default -> VerifyCredentialResponse.OTHER_ERROR; Loading services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +3 −5 Original line number Diff line number Diff line Loading @@ -58,7 +58,6 @@ import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; Loading Loading @@ -724,6 +723,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertTrue(response.isCredCertainlyIncorrect()); assertFalse(response.isCredAlreadyTried()); assertFalse(response.hasTimeout()); assertEquals(Duration.ZERO, response.getTimeoutAsDuration()); response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); Loading @@ -732,10 +732,6 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } // Same as preceding test case, but uses a nonzero timeout. // // TODO(b/395976735): currently the behavior in this scenario is wrong, so currently this test // case is ignored. Fix the behavior and remove @Ignore. @Ignore @Test @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER) public void testRepeatOfWrongGuessRejectedAsDuplicate_afterWeaverIncorrectKeyWithTimeout() Loading @@ -751,6 +747,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mSpManager.injectWeaverReadResponse(WeaverReadStatus.INCORRECT_KEY, timeout); VerifyCredentialResponse response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertTrue(response.isCredCertainlyIncorrect()); assertFalse(response.isCredAlreadyTried()); assertTrue(response.hasTimeout()); assertEquals(timeout, response.getTimeoutAsDuration()); Loading Loading
core/java/com/android/internal/widget/VerifyCredentialResponse.java +32 −8 Original line number Diff line number Diff line Loading @@ -45,9 +45,14 @@ public final class VerifyCredentialResponse implements Parcelable { private static final int RESPONSE_OK = 0; /** * 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. * {@link #getTimeout()} gives the timeout. * The credential could not be verified because a timeout is still active. {@link #getTimeout()} * gives the currently active timeout. * * <p>Alternatively, a timeout was not active, the credential was incorrect, and there is a * timeout before the <em>next</em> attempt will be allowed. {@link #getTimeout()} gives the * newly active timeout. The preferred response code in this case is {@link * #RESPONSE_CRED_INCORRECT}, but some devices use a rate-limiting HAL implementation that does * not differentiate this case from the "timeout is still active" case. */ private static final int RESPONSE_RETRY = 1; Loading @@ -60,6 +65,8 @@ public final class VerifyCredentialResponse implements Parcelable { /** * Credential was incorrect and none of {@link #RESPONSE_RETRY}, {@link * #RESPONSE_CRED_TOO_SHORT}, or {@link #RESPONSE_CRED_ALREADY_TRIED} applies. * * <p>{@link #getTimeout()} gives the newly active timeout, if any. */ private static final int RESPONSE_CRED_INCORRECT = 4; Loading Loading @@ -136,10 +143,8 @@ public final class VerifyCredentialResponse implements Parcelable { } /** * Since timeouts are always an error, provide a way to create the VerifyCredentialResponse * object directly. None of the other fields (Gatekeeper HAT, Gatekeeper Password, etc) * are valid in this case. Similarly, the response code will always be * {@link #RESPONSE_RETRY}. * Builds a {@link VerifyCredentialResponse} with {@link #RESPONSE_RETRY} and the given timeout * in milliseconds. */ public static VerifyCredentialResponse fromTimeout(int timeout) { return new VerifyCredentialResponse(RESPONSE_RETRY, Loading @@ -149,7 +154,7 @@ public final class VerifyCredentialResponse implements Parcelable { } /** * Like {@link #fromTimeout(int)}, but takes a Duration instead of a raw milliseconds value. * Builds a {@link VerifyCredentialResponse} with {@link #RESPONSE_RETRY} and the given timeout. * * <p>The timeout is clamped to fit in an int. See {@link #timeoutToClampedMillis(Duration)}. */ Loading @@ -157,6 +162,20 @@ public final class VerifyCredentialResponse implements Parcelable { return fromTimeout(timeoutToClampedMillis(timeout)); } /** * Builds a {@link VerifyCredentialResponse} with {@link #RESPONSE_CRED_INCORRECT} and the given * timeout. * * <p>The timeout is clamped to fit in an int. See {@link #timeoutToClampedMillis(Duration)}. */ public static VerifyCredentialResponse credIncorrect(Duration timeout) { return new VerifyCredentialResponse( VerifyCredentialResponse.RESPONSE_CRED_INCORRECT, timeoutToClampedMillis(timeout), /* gatekeeperHAT= */ null, /* gatekeeperPasswordHandle= */ 0L); } /** * Clamps the given timeout to fit in an int that holds a non-negative milliseconds value. * Loading Loading @@ -260,6 +279,11 @@ public final class VerifyCredentialResponse implements Parcelable { * will be allowed. */ public boolean hasTimeout() { if (android.security.Flags.softwareRatelimiter()) { // Check mTimeout directly. It can be nonzero for either RESPONSE_RETRY or // RESPONSE_CRED_INCORRECT. return mTimeout != 0; } return mResponseCode == RESPONSE_RETRY; } Loading
services/core/java/com/android/server/locksettings/LockSettingsService.java +4 −1 Original line number Diff line number Diff line Loading @@ -2542,7 +2542,10 @@ public class LockSettingsService extends ILockSettings.Stub { // lock screen doesn't allow another attempt until both rate-limiters allow it. Duration hwTimeout = response.getTimeoutAsDuration(); if (swTimeout.compareTo(hwTimeout) > 0) { response = VerifyCredentialResponse.fromTimeout(swTimeout); response = isCertainlyWrongGuess ? VerifyCredentialResponse.credIncorrect(swTimeout) : VerifyCredentialResponse.fromTimeout(swTimeout); } } } Loading
services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +7 −9 Original line number Diff line number Diff line Loading @@ -719,19 +719,17 @@ class SyntheticPasswordManager { // THROTTLE for both cases. VerifyCredentialResponse.fromTimeout(Duration.ofMillis(weaverResponse.timeout)); case WeaverReadStatus.INCORRECT_KEY -> { // The credential was incorrect. There may be a timeout until the next attempt is // allowed; that occurs when the Weaver implementation returns INCORRECT_KEY in this // case instead of THROTTLE. if (android.security.Flags.softwareRatelimiter()) { yield VerifyCredentialResponse.credIncorrect( Duration.ofMillis(weaverResponse.timeout)); } 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. yield VerifyCredentialResponse.fromTimeout( Duration.ofMillis(weaverResponse.timeout)); } if (android.security.Flags.softwareRatelimiter()) { yield VerifyCredentialResponse.credIncorrect(); } yield VerifyCredentialResponse.OTHER_ERROR; } default -> VerifyCredentialResponse.OTHER_ERROR; Loading
services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +3 −5 Original line number Diff line number Diff line Loading @@ -58,7 +58,6 @@ import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; Loading Loading @@ -724,6 +723,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertTrue(response.isCredCertainlyIncorrect()); assertFalse(response.isCredAlreadyTried()); assertFalse(response.hasTimeout()); assertEquals(Duration.ZERO, response.getTimeoutAsDuration()); response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); Loading @@ -732,10 +732,6 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } // Same as preceding test case, but uses a nonzero timeout. // // TODO(b/395976735): currently the behavior in this scenario is wrong, so currently this test // case is ignored. Fix the behavior and remove @Ignore. @Ignore @Test @EnableFlags(android.security.Flags.FLAG_SOFTWARE_RATELIMITER) public void testRepeatOfWrongGuessRejectedAsDuplicate_afterWeaverIncorrectKeyWithTimeout() Loading @@ -751,6 +747,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mSpManager.injectWeaverReadResponse(WeaverReadStatus.INCORRECT_KEY, timeout); VerifyCredentialResponse response = mService.verifyCredential(wrongGuess, userId, /* flags= */ 0); assertTrue(response.isCredCertainlyIncorrect()); assertFalse(response.isCredAlreadyTried()); assertTrue(response.hasTimeout()); assertEquals(timeout, response.getTimeoutAsDuration()); Loading