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

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

Merge changes from topic "lskf-chars-fix-2" into main

* changes:
  Remove PasswordMetrics#validatePassword()
  Enforce minimum pattern length in PasswordMetrics
parents 36e5f061 25053e20
Loading
Loading
Loading
Loading
+18 −48
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSW
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
import static com.android.internal.widget.LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
import static com.android.internal.widget.LockPatternUtils.MIN_LOCK_PATTERN_SIZE;
import static com.android.internal.widget.PasswordValidationError.CONTAINS_INVALID_CHARACTERS;
import static com.android.internal.widget.PasswordValidationError.CONTAINS_INVALID_CHARACTERS;
import static com.android.internal.widget.PasswordValidationError.CONTAINS_SEQUENCE;
import static com.android.internal.widget.PasswordValidationError.CONTAINS_SEQUENCE;
import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_DIGITS;
import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_DIGITS;
@@ -134,17 +135,6 @@ public final class PasswordMetrics implements Parcelable {
        }
        }
    }
    }


    private static boolean hasInvalidCharacters(byte[] password) {
        // Allow non-control Latin-1 characters only.
        for (byte b : password) {
            char c = (char) b;
            if (c < 32 || c > 127) {
                return true;
            }
        }
        return false;
    }

    @Override
    @Override
    public int describeContents() {
    public int describeContents() {
        return 0;
        return 0;
@@ -201,7 +191,9 @@ public final class PasswordMetrics implements Parcelable {
            return PasswordMetrics.computeForPasswordOrPin(credential.getCredential(),
            return PasswordMetrics.computeForPasswordOrPin(credential.getCredential(),
                    credential.isPin());
                    credential.isPin());
        } else if (credential.isPattern())  {
        } else if (credential.isPattern())  {
            return new PasswordMetrics(CREDENTIAL_TYPE_PATTERN);
            PasswordMetrics metrics = new PasswordMetrics(CREDENTIAL_TYPE_PATTERN);
            metrics.length = credential.size();
            return metrics;
        } else if (credential.isNone()) {
        } else if (credential.isNone()) {
            return new PasswordMetrics(CREDENTIAL_TYPE_NONE);
            return new PasswordMetrics(CREDENTIAL_TYPE_NONE);
        } else {
        } else {
@@ -529,39 +521,8 @@ public final class PasswordMetrics implements Parcelable {
            return Collections.singletonList(
            return Collections.singletonList(
                    new PasswordValidationError(CONTAINS_INVALID_CHARACTERS, 0));
                    new PasswordValidationError(CONTAINS_INVALID_CHARACTERS, 0));
        }
        }
        if (credential.isPassword() || credential.isPin()) {
        PasswordMetrics actualMetrics = computeForCredential(credential);
            return validatePassword(adminMetrics, minComplexity, credential.isPin(),
        return validatePasswordMetrics(adminMetrics, minComplexity, actualMetrics);
                    credential.getCredential());
        } else {
            return validatePasswordMetrics(adminMetrics, minComplexity,
                    new PasswordMetrics(credential.getType()));
        }
    }

    /**
     * Validates a proposed lockscreen credential against minimum metrics and complexity.
     *
     * @param adminMetrics minimum metrics to satisfy admin requirements
     * @param minComplexity minimum complexity imposed by the requester
     * @param isPin whether to validate as a PIN (true) or password (false)
     * @param password the proposed lockscreen credential as a byte[].  Must be the value from
     *                 {@link LockscreenCredential#getCredential()}.
     *
     * @return a list of validation errors. An empty list means the credential is OK.
     *
     * TODO: merge this into validateCredential() and remove the redundant hasInvalidCharacters(),
     *       once all external callers are removed
     */
    public static List<PasswordValidationError> validatePassword(
            PasswordMetrics adminMetrics, int minComplexity, boolean isPin, byte[] password) {

        if (hasInvalidCharacters(password)) {
            return Collections.singletonList(
                    new PasswordValidationError(CONTAINS_INVALID_CHARACTERS, 0));
        }

        final PasswordMetrics enteredMetrics = computeForPasswordOrPin(password, isPin);
        return validatePasswordMetrics(adminMetrics, minComplexity, enteredMetrics);
    }
    }


    /**
    /**
@@ -584,9 +545,18 @@ public final class PasswordMetrics implements Parcelable {
                || !bucket.allowsCredType(actualMetrics.credType)) {
                || !bucket.allowsCredType(actualMetrics.credType)) {
            return Collections.singletonList(new PasswordValidationError(WEAK_CREDENTIAL_TYPE, 0));
            return Collections.singletonList(new PasswordValidationError(WEAK_CREDENTIAL_TYPE, 0));
        }
        }
        if (actualMetrics.credType != CREDENTIAL_TYPE_PASSWORD
        if (actualMetrics.credType == CREDENTIAL_TYPE_PATTERN) {
                && actualMetrics.credType != CREDENTIAL_TYPE_PIN) {
            // For pattern, only need to check the length against the hardcoded minimum.  If the
            return Collections.emptyList(); // Nothing to check for pattern or none.
            // pattern length is unavailable (e.g., PasswordMetrics that was stored on-disk before
            // the pattern length started being included in it), assume it is okay.
            if (actualMetrics.length != 0 && actualMetrics.length < MIN_LOCK_PATTERN_SIZE) {
                return Collections.singletonList(new PasswordValidationError(TOO_SHORT,
                            MIN_LOCK_PATTERN_SIZE));
            }
            return Collections.emptyList();
        }
        if (actualMetrics.credType == CREDENTIAL_TYPE_NONE) {
            return Collections.emptyList(); // Nothing to check for none.
        }
        }


        if (actualMetrics.credType == CREDENTIAL_TYPE_PIN && actualMetrics.nonNumeric > 0) {
        if (actualMetrics.credType == CREDENTIAL_TYPE_PIN && actualMetrics.nonNumeric > 0) {
+16 −0
Original line number Original line Diff line number Diff line
@@ -42,6 +42,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;


import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.PasswordValidationError;
import com.android.internal.widget.PasswordValidationError;


@@ -419,6 +420,21 @@ public class PasswordMetricsTest {
                PasswordValidationError.TOO_SHORT, 6);
                PasswordValidationError.TOO_SHORT, 6);
    }
    }


    private LockscreenCredential createPattern(String patternString) {
        return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
                patternString.getBytes()));
    }

    @Test
    public void testValidateCredential_pattern() {
        PasswordMetrics adminMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE);
        assertValidationErrors(
                validateCredential(adminMetrics, PASSWORD_COMPLEXITY_NONE, createPattern("123")),
                PasswordValidationError.TOO_SHORT, 4);
        assertValidationErrors(
                validateCredential(adminMetrics, PASSWORD_COMPLEXITY_NONE, createPattern("1234")));
    }

    /**
    /**
     * @param expected sequence of validation error codes followed by requirement values, must have
     * @param expected sequence of validation error codes followed by requirement values, must have
     *                 even number of elements. Empty means no errors.
     *                 even number of elements. Empty means no errors.