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

Commit 8066a758 authored by Eric Biggers's avatar Eric Biggers
Browse files

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
parent b63430f2
Loading
Loading
Loading
Loading
+12 −6
Original line number Diff line number Diff line
@@ -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;
+26 −9
Original line number Diff line number Diff line
@@ -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(),