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

Commit cb71190b authored by Mike Schneider's avatar Mike Schneider
Browse files

Make sure pins up to the max length of 16 digits are supported.

Bug: 284405869
Test: unit tests
Change-Id: I9b6ebadde4aa430c78438b6f029b656edf7ebaa0
parent 4136968d
Loading
Loading
Loading
Loading
+12 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.authentication.domain.interactor

import android.app.admin.DevicePolicyManager
import com.android.systemui.authentication.data.repository.AuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.dagger.SysUISingleton
@@ -177,15 +178,21 @@ constructor(

        /**
         * Returns a PIN code from the given list. It's assumed the given list elements are all
         * [Int].
         * [Int] in the range [0-9].
         */
        private fun List<Any>.asCode(): Int? {
            if (isEmpty()) {
        private fun List<Any>.asCode(): Long? {
            if (isEmpty() || size > DevicePolicyManager.MAX_PASSWORD_LENGTH) {
                return null
            }

            var code = 0
            map { it as Int }.forEach { integer -> code = code * 10 + integer }
            var code = 0L
            map {
                    require(it is Int && it in 0..9) {
                        "Pin is required to be Int in range [0..9], but got $it"
                    }
                    it
                }
                .forEach { integer -> code = code * 10 + integer }

            return code
        }
+7 −1
Original line number Diff line number Diff line
@@ -32,7 +32,13 @@ sealed class AuthenticationMethodModel(
    /** The most basic authentication method. The lock screen can be swiped away when displayed. */
    object Swipe : AuthenticationMethodModel(isSecure = false)

    data class Pin(val code: Int) : AuthenticationMethodModel(isSecure = true)
    /**
     * Authentication method using a PIN.
     *
     * In practice, a pin is restricted to 16 decimal digits , see
     * [android.app.admin.DevicePolicyManager.MAX_PASSWORD_LENGTH]
     */
    data class Pin(val code: Long) : AuthenticationMethodModel(isSecure = true)

    data class Password(val password: String) : AuthenticationMethodModel(isSecure = true)

+46 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.authentication.domain.interactor

import android.app.admin.DevicePolicyManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.AuthenticationRepository
@@ -168,6 +169,51 @@ class AuthenticationInteractorTest : SysuiTestCase() {
            assertThat(failedAttemptCount).isEqualTo(1)
        }

    @Test
    fun authenticate_withEmptyPin_returnsFalseAndDoesNotUnlockDevice() =
        testScope.runTest {
            val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
            val isUnlocked by collectLastValue(underTest.isUnlocked)
            underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
            assertThat(isUnlocked).isFalse()

            assertThat(underTest.authenticate(listOf())).isFalse()
            assertThat(isUnlocked).isFalse()
            assertThat(failedAttemptCount).isEqualTo(1)
        }

    @Test
    fun authenticate_withCorrectMaxLengthPin_returnsTrueAndUnlocksDevice() =
        testScope.runTest {
            val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
            val isUnlocked by collectLastValue(underTest.isUnlocked)
            underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(9999999999999999))
            assertThat(isUnlocked).isFalse()

            assertThat(underTest.authenticate(List(16) { 9 })).isTrue()
            assertThat(isUnlocked).isTrue()
            assertThat(failedAttemptCount).isEqualTo(0)
        }

    @Test
    fun authenticate_withCorrectTooLongPin_returnsFalseAndDoesNotUnlockDevice() =
        testScope.runTest {
            // Max pin length is 16 digits. To avoid issues with overflows, this test ensures
            // that all pins > 16 decimal digits are rejected.

            // If the policy changes, there is work to do in SysUI.
            assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17)

            val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
            val isUnlocked by collectLastValue(underTest.isUnlocked)
            underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(99999999999999999))
            assertThat(isUnlocked).isFalse()

            assertThat(underTest.authenticate(List(17) { 9 })).isFalse()
            assertThat(isUnlocked).isFalse()
            assertThat(failedAttemptCount).isEqualTo(1)
        }

    @Test
    fun authenticate_withCorrectPassword_returnsTrueAndUnlocksDevice() =
        testScope.runTest {