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

Commit d04b43de authored by Kevin Chyn's avatar Kevin Chyn
Browse files

Enforce that only public authenticator combinations are accepted

Bug: 141025588

Test: atest com.android.server.biometrics
Test: BiometricPromptDemo
Change-Id: Ia3c332b59ce0a04717a2f03c26c1e1e661c2785e
parent 5c7ed9af
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -644,6 +644,10 @@ public class BiometricService extends SystemService {
                return;
            }

            if (!Utils.isValidAuthenticatorConfig(bundle)) {
                throw new SecurityException("Invalid authenticator configuration");
            }

            Utils.combineAuthenticatorBundles(bundle);

            // Check the usage of this in system server. Need to remove this check if it becomes a
@@ -688,7 +692,8 @@ public class BiometricService extends SystemService {
        public int canAuthenticate(String opPackageName, int userId,
                @Authenticators.Types int authenticators) {
            Slog.d(TAG, "canAuthenticate: User=" + userId
                    + ", Caller=" + UserHandle.getCallingUserId());
                    + ", Caller=" + UserHandle.getCallingUserId()
                    + ", Authenticators=" + authenticators);

            if (userId != UserHandle.getCallingUserId()) {
                checkInternalPermission();
@@ -696,6 +701,11 @@ public class BiometricService extends SystemService {
                checkPermission();
            }


            if (!Utils.isValidAuthenticatorConfig(authenticators)) {
                throw new SecurityException("Invalid authenticator configuration");
            }

            final Bundle bundle = new Bundle();
            bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);

@@ -969,7 +979,7 @@ public class BiometricService extends SystemService {
        int firstHwAvailable = TYPE_NONE;
        for (AuthenticatorWrapper authenticator : mAuthenticators) {
            final int actualStrength = authenticator.getActualStrength();
            final int requestedStrength = Utils.getBiometricStrength(bundle);
            final int requestedStrength = Utils.getPublicBiometricStrength(bundle);
            if (Utils.isAtLeastStrength(actualStrength, requestedStrength)) {
                hasSufficientStrength = true;
                modality = authenticator.modality;
+60 −4
Original line number Diff line number Diff line
@@ -87,28 +87,35 @@ public class Utils {
    }

    /**
     * Checks if any of the publicly defined strengths are set.
     *
     * @param authenticators composed of one or more values from {@link Authenticators}
     * @return minimal allowed biometric strength or 0 if biometric authentication is not allowed.
     */
    public static int getBiometricStrength(@Authenticators.Types int authenticators) {
    public static int getPublicBiometricStrength(@Authenticators.Types int authenticators) {
        // Only biometrics WEAK and above are allowed to integrate with the public APIs.
        return authenticators & Authenticators.BIOMETRIC_WEAK;
    }

    /**
     * Checks if any of the publicly defined strengths are set.
     *
     * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
     * @return minimal allowed biometric strength or 0 if biometric authentication is not allowed.
     */
    public static int getBiometricStrength(Bundle bundle) {
        return getBiometricStrength(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
    public static int getPublicBiometricStrength(Bundle bundle) {
        return getPublicBiometricStrength(
                bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
    }

    /**
     * Checks if any of the publicly defined strengths are set.
     *
     * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
     * @return true if biometric authentication is allowed.
     */
    public static boolean isBiometricAllowed(Bundle bundle) {
        return getBiometricStrength(bundle) != 0;
        return getPublicBiometricStrength(bundle) != 0;
    }

    /**
@@ -121,6 +128,55 @@ public class Utils {
        return (~requestedStrength & sensorStrength) == 0;
    }

    /**
     * Checks if the authenticator configuration is a valid combination of the public APIs
     * @param bundle
     * @return
     */
    public static boolean isValidAuthenticatorConfig(Bundle bundle) {
        final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
        return isValidAuthenticatorConfig(authenticators);
    }

    /**
     * Checks if the authenticator configuration is a valid combination of the public APIs
     * @param authenticators
     * @return
     */
    public static boolean isValidAuthenticatorConfig(int authenticators) {
        // The caller is not required to set the authenticators. But if they do, check the below.
        if (authenticators == 0) {
            return true;
        }

        // Check if any of the non-biometric and non-credential bits are set. If so, this is
        // invalid.
        final int testBits = ~(Authenticators.DEVICE_CREDENTIAL
                | Authenticators.BIOMETRIC_MIN_STRENGTH);
        if ((authenticators & testBits) != 0) {
            Slog.e(BiometricService.TAG, "Non-biometric, non-credential bits found."
                    + " Authenticators: " + authenticators);
            return false;
        }

        // Check that biometrics bits are either NONE, WEAK, or STRONG. If NONE, DEVICE_CREDENTIAL
        // should be set.
        final int biometricBits = authenticators & Authenticators.BIOMETRIC_MIN_STRENGTH;
        if (biometricBits == Authenticators.EMPTY_SET
                && isDeviceCredentialAllowed(authenticators)) {
            return true;
        } else if (biometricBits == Authenticators.BIOMETRIC_STRONG) {
            return true;
        } else if (biometricBits == Authenticators.BIOMETRIC_WEAK) {
            return true;
        }

        Slog.e(BiometricService.TAG, "Unsupported biometric flags. Authenticators: "
                + authenticators);
        // Non-supported biometric flags are being used
        return false;
    }

    /**
     * Converts error codes from BiometricConstants, which are used in most of the internal plumbing
     * and eventually returned to {@link BiometricPrompt.AuthenticationCallback} to public
+38 −4
Original line number Diff line number Diff line
@@ -125,18 +125,17 @@ public class UtilsTest {

        int authenticators = Integer.MAX_VALUE;
        assertEquals(Authenticators.BIOMETRIC_WEAK,
                Utils.getBiometricStrength(authenticators));
                Utils.getPublicBiometricStrength(authenticators));

        Bundle bundle = new Bundle();
        bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
        assertEquals(Authenticators.BIOMETRIC_WEAK, Utils.getBiometricStrength(bundle));
        assertEquals(Authenticators.BIOMETRIC_WEAK, Utils.getPublicBiometricStrength(bundle));
    }

    @Test
    public void testIsBiometricAllowed() {
        // Only the lowest 8 bits (BIOMETRIC_WEAK mask) are allowed to integrate with the
        // Biometric APIs
        final int lastBiometricPosition = 10;
        Bundle bundle = new Bundle();
        for (int i = 0; i <= 7; i++) {
            int authenticators = 1 << i;
@@ -144,7 +143,7 @@ public class UtilsTest {
            assertTrue(Utils.isBiometricAllowed(bundle));
        }

        // The rest of the bits are not allowed to integrate with BiometricPrompt
        // The rest of the bits are not allowed to integrate with the public APIs
        for (int i = 8; i < 32; i++) {
            int authenticators = 1 << i;
            bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
@@ -152,6 +151,41 @@ public class UtilsTest {
        }
    }

    @Test
    public void testIsValidAuthenticatorConfig() {
        assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.EMPTY_SET));

        assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_STRONG));

        assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_WEAK));

        assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.DEVICE_CREDENTIAL));

        assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.DEVICE_CREDENTIAL
                | Authenticators.BIOMETRIC_STRONG));

        assertTrue(Utils.isValidAuthenticatorConfig(Authenticators.DEVICE_CREDENTIAL
                | Authenticators.BIOMETRIC_WEAK));

        assertFalse(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_CONVENIENCE));

        assertFalse(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_CONVENIENCE
                | Authenticators.DEVICE_CREDENTIAL));

        assertFalse(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_MAX_STRENGTH));

        assertFalse(Utils.isValidAuthenticatorConfig(Authenticators.BIOMETRIC_MIN_STRENGTH));

        // The rest of the bits are not allowed to integrate with the public APIs
        for (int i = 8; i < 32; i++) {
            final int authenticator = 1 << i;
            if (authenticator == Authenticators.DEVICE_CREDENTIAL) {
                continue;
            }
            assertFalse(Utils.isValidAuthenticatorConfig(1 << i));
        }
    }

    @Test
    public void testIsAtLeastStrength() {
        int sensorStrength = Authenticators.BIOMETRIC_STRONG;