From 8066a758fefa847b1a6fdb874ea63c7814e05abc Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 21 Jun 2023 05:28:09 +0000 Subject: [PATCH] Allow LockscreenCredential to represent any proposed credential Currently the ChooseLockPassword activity in Settings creates a LockscreenCredential object for the text the user has entered, every time a character is added or deleted to/from the text view. It then uses this LockscreenCredential to validate that requirements such as minimum length have been met. Thus, LockscreenCredential is expected to be able to represent any proposed credential, even an invalid one. Yet, currently the constructor of LockscreenCredential throws an exception if the length is 0 and the credential type is not NONE. For the empty text case, ChooseLockPassword currently works around this by constructing a LockscreenCredential of type NONE and then actually treating it as a PIN or PASSWORD when validating it. To make it possible to fix the bug where invalid characters are being allowed in passwords, I'm adding a new validation method that operates on LockscreenCredential. For ChooseLockPassword to be converted to this, though, it needs to start constructing a LockscreenCredential with the correct type for the proposed credential. Therefore, this CL allows non-none LockscreenCredential objects to be constructed with length 0. Bug: 219511761 Bug: 232900169 Bug: 243881358 Test: atest LockscreenCredentialTest Test: atest com.android.server.locksettings Change-Id: I6b951759aa7fabacc46c20bbfd31d3a0dafa4c1c --- .../internal/widget/LockscreenCredential.java | 18 ++++++---- .../widget/LockscreenCredentialTest.java | 35 ++++++++++++++----- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java index 03e7fd1c74035..6e63f790ab982 100644 --- a/core/java/com/android/internal/widget/LockscreenCredential.java +++ b/core/java/com/android/internal/widget/LockscreenCredential.java @@ -60,7 +60,7 @@ import java.util.Objects; public class LockscreenCredential implements Parcelable, AutoCloseable { private final int mType; - // Stores raw credential bytes, or null if credential has been zeroized. An empty password + // Stores raw credential bytes, or null if credential has been zeroized. A none credential // is represented as a byte array of length 0. private byte[] mCredential; @@ -80,14 +80,20 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { Preconditions.checkArgument(type == CREDENTIAL_TYPE_PIN || type == CREDENTIAL_TYPE_PASSWORD || type == CREDENTIAL_TYPE_PATTERN); - Preconditions.checkArgument(credential.length > 0); + // Do not validate credential.length yet. All non-none credentials have a minimum + // length requirement; however, one of the uses of LockscreenCredential is to represent + // a proposed credential that might be too short. For example, a LockscreenCredential + // with type CREDENTIAL_TYPE_PIN and length 0 represents an attempt to set an empty PIN. + // This differs from an actual attempt to set a none credential. We have to allow the + // LockscreenCredential object to be constructed so that the validation logic can run, + // even though the validation logic will ultimately reject the credential as too short. } mType = type; mCredential = credential; } /** - * Creates a LockscreenCredential object representing empty password. + * Creates a LockscreenCredential object representing a none credential. */ public static LockscreenCredential createNone() { return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0]); @@ -130,7 +136,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { /** * Creates a LockscreenCredential object representing the given alphabetic password. - * If the supplied password is empty, create an empty credential object. + * If the supplied password is empty, create a none credential object. */ public static LockscreenCredential createPasswordOrNone(@Nullable CharSequence password) { if (TextUtils.isEmpty(password)) { @@ -142,7 +148,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { /** * Creates a LockscreenCredential object representing the given numeric PIN. - * If the supplied password is empty, create an empty credential object. + * If the supplied password is empty, create a none credential object. */ public static LockscreenCredential createPinOrNone(@Nullable CharSequence pin) { if (TextUtils.isEmpty(pin)) { @@ -175,7 +181,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { return mCredential; } - /** Returns whether this is an empty credential */ + /** Returns whether this is a none credential */ public boolean isNone() { ensureNotZeroized(); return mType == CREDENTIAL_TYPE_NONE; diff --git a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java index 63d1a876bf40e..434a8953b19f1 100644 --- a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java @@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -37,16 +36,16 @@ import java.util.Arrays; public class LockscreenCredentialTest { @Test - public void testEmptyCredential() { - LockscreenCredential empty = LockscreenCredential.createNone(); + public void testNoneCredential() { + LockscreenCredential none = LockscreenCredential.createNone(); - assertTrue(empty.isNone()); - assertEquals(0, empty.size()); - assertNotNull(empty.getCredential()); + assertTrue(none.isNone()); + assertEquals(0, none.size()); + assertArrayEquals(new byte[0], none.getCredential()); - assertFalse(empty.isPin()); - assertFalse(empty.isPassword()); - assertFalse(empty.isPattern()); + assertFalse(none.isPin()); + assertFalse(none.isPassword()); + assertFalse(none.isPattern()); } @Test @@ -94,6 +93,24 @@ public class LockscreenCredentialTest { assertFalse(pattern.isPassword()); } + // Constructing a LockscreenCredential with a too-short length, even 0, should not throw an + // exception. This is because LockscreenCredential needs to be able to represent a request to + // set a credential that is too short. + @Test + public void testZeroLengthCredential() { + LockscreenCredential credential = LockscreenCredential.createPin(""); + assertTrue(credential.isPin()); + assertEquals(0, credential.size()); + + credential = createPattern(""); + assertTrue(credential.isPattern()); + assertEquals(0, credential.size()); + + credential = LockscreenCredential.createPassword(""); + assertTrue(credential.isPassword()); + assertEquals(0, credential.size()); + } + @Test public void testPasswordOrNoneCredential() { assertEquals(LockscreenCredential.createNone(),