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

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

Enforce authenticator registration

1) Authenticator ID must be known to config.xml
2) Authenticator ID must have not been already registered

Bug: 141025588

Test: BiometricPromptDemo
Test: atest BiometricServiceTest
Change-Id: I994c25f79e3b55a7261048ad13081f3b7c3df58b
parent d04b43de
Loading
Loading
Loading
Loading
+9 −16
Original line number Diff line number Diff line
@@ -181,18 +181,15 @@ public class AuthService extends SystemService {
        final PackageManager pm = context.getPackageManager();
    }

    /**
     * @param id must be unique per device
     * @param modality one of {@link BiometricAuthenticator} types
     * @param strength as defined in {@link Authenticators}
     */
    private void registerAuthenticator(int id, int modality, int strength) throws RemoteException {
    private void registerAuthenticator(SensorConfig config) throws RemoteException {

        Slog.d(TAG, "Registering ID: " + id + " Modality: " + modality + " Strength: " + strength);
        Slog.d(TAG, "Registering ID: " + config.mId
                + " Modality: " + config.mModality
                + " Strength: " + config.mStrength);

        final IBiometricAuthenticator.Stub authenticator;

        switch (modality) {
        switch (config.mModality) {
            case TYPE_FINGERPRINT:
                authenticator = new FingerprintAuthenticator(IFingerprintService.Stub.asInterface(
                        ServiceManager.getService(Context.FINGERPRINT_SERVICE)));
@@ -209,11 +206,12 @@ public class AuthService extends SystemService {
                break;

            default:
                Slog.e(TAG, "Unknown modality: " + modality);
                Slog.e(TAG, "Unknown modality: " + config.mModality);
                return;
        }

        mBiometricService.registerAuthenticator(id, modality, strength, authenticator);
        mBiometricService.registerAuthenticator(config.mId, config.mModality, config.mStrength,
                authenticator);
    }

    @Override
@@ -223,13 +221,8 @@ public class AuthService extends SystemService {
        final String[] configs = mInjector.getConfiguration(getContext());

        for (int i = 0; i < configs.length; i++) {
            String[] elems = configs[i].split(":");
            final int id = Integer.parseInt(elems[0]);
            final int modality = Integer.parseInt(elems[1]);
            final int strength = Integer.parseInt(elems[2]);

            try {
                registerAuthenticator(id, modality, strength);
                registerAuthenticator(new SensorConfig(configs[i]));
            } catch (RemoteException e) {
                Slog.e(TAG, "Remote exception", e);
            }
+35 −0
Original line number Diff line number Diff line
@@ -764,6 +764,31 @@ public class BiometricService extends SystemService {
                    + " Modality: " + modality
                    + " Strength: " + strength);

            if (strength != Authenticators.BIOMETRIC_STRONG
                    && strength != Authenticators.BIOMETRIC_WEAK) {
                throw new IllegalStateException("Unsupported strength");
            }

            for (AuthenticatorWrapper wrapper : mAuthenticators) {
                if (wrapper.id == id) {
                    throw new IllegalStateException("Cannot register duplicate authenticator");
                }
            }

            // This happens infrequently enough, not worth caching.
            final String[] configs = mInjector.getConfiguration(getContext());
            boolean idFound = false;
            for (int i = 0; i < configs.length; i++) {
                SensorConfig config = new SensorConfig(configs[i]);
                if (config.mId == id) {
                    idFound = true;
                    break;
                }
            }
            if (!idFound) {
                throw new IllegalStateException("Cannot register unknown id");
            }

            mAuthenticators.add(new AuthenticatorWrapper(id, modality, strength, authenticator));
        }

@@ -886,6 +911,16 @@ public class BiometricService extends SystemService {
                BiometricService service) {
            return new BiometricStrengthController(service);
        }

        /**
         * Allows to test with various device sensor configurations.
         * @param context System Server context
         * @return the sensor configuration from core/res/res/values/config.xml
         */
        @VisibleForTesting
        public String[] getConfiguration(Context context) {
            return context.getResources().getStringArray(R.array.config_biometric_sensors);
        }
    }

    /**
+33 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.biometrics;

/**
 * Parsed sensor config. See core/res/res/values/config.xml config_biometric_sensors
 */
class SensorConfig {
    final int mId;
    final int mModality;
    final int mStrength;

    public SensorConfig(String config) {
        String[] elems = config.split(":");
        mId = Integer.parseInt(elems[0]);
        mModality = Integer.parseInt(elems[1]);
        mStrength = Integer.parseInt(elems[2]);
    }
}
+61 −2
Original line number Diff line number Diff line
@@ -128,6 +128,14 @@ public class BiometricServiceTest {
                .thenReturn(ERROR_NOT_RECOGNIZED);
        when(mResources.getString(R.string.biometric_error_user_canceled))
                .thenReturn(ERROR_USER_CANCELED);

        final String[] config = {
                "0:2:15",  // ID0:Fingerprint:Strong
                "1:8:15",  // ID1:Face:Strong
                "2:4:255", // ID2:Iris:Weak
        };

        when(mInjector.getConfiguration(any())).thenReturn(config);
    }

    @Test
@@ -1152,6 +1160,56 @@ public class BiometricServiceTest {
                eq(TEST_PACKAGE_NAME));
    }

    @Test(expected = IllegalStateException.class)
    public void testRegistrationWithDuplicateId_throwsIllegalStateException() throws Exception {
        mBiometricService = new BiometricService(mContext, mInjector);
        mBiometricService.onStart();

        mBiometricService.mImpl.registerAuthenticator(
                0 /* id */, 2 /* modality */, 15 /* strength */,
                mFingerprintAuthenticator);
        mBiometricService.mImpl.registerAuthenticator(
                0 /* id */, 2 /* modality */, 15 /* strength */,
                mFingerprintAuthenticator);
    }

    @Test(expected = IllegalStateException.class)
    public void testRegistrationWithUnknownId_throwsIllegalStateException() throws Exception {
        mBiometricService = new BiometricService(mContext, mInjector);
        mBiometricService.onStart();

        mBiometricService.mImpl.registerAuthenticator(
                100 /* id */, 2 /* modality */, 15 /* strength */,
                mFingerprintAuthenticator);
    }

    @Test(expected = IllegalStateException.class)
    public void testRegistrationWithUnsupportedStrength_throwsIllegalStateException()
            throws Exception {
        mBiometricService = new BiometricService(mContext, mInjector);
        mBiometricService.onStart();

        // Only STRONG and WEAK are supported. Let's enforce that CONVENIENCE cannot be
        // registered. If there is a compelling reason, we can remove this constraint.
        mBiometricService.mImpl.registerAuthenticator(
                0 /* id */, 2 /* modality */,
                Authenticators.BIOMETRIC_CONVENIENCE /* strength */,
                mFingerprintAuthenticator);
    }

    @Test
    public void testRegistrationHappyPath_isOk() throws Exception {
        // This is being tested in many of the other cases, but here's the base case.
        mBiometricService = new BiometricService(mContext, mInjector);
        mBiometricService.onStart();

        for (String s : mInjector.getConfiguration(null)) {
            SensorConfig config = new SensorConfig(s);
            mBiometricService.mImpl.registerAuthenticator(config.mId, config.mModality,
                    config.mStrength, mFingerprintAuthenticator);
        }
    }

    // Helper methods

    private int invokeCanAuthenticate(BiometricService service, int authenticators)
@@ -1163,6 +1221,7 @@ public class BiometricServiceTest {
        setupAuthForOnly(modality, strength, true /* enrolled */);
    }

    // TODO: Reconcile the registration strength with the injector
    private void setupAuthForOnly(int modality, int strength, boolean enrolled) throws Exception {
        mBiometricService = new BiometricService(mContext, mInjector);
        mBiometricService.onStart();
@@ -1180,7 +1239,7 @@ public class BiometricServiceTest {
        if ((modality & BiometricAuthenticator.TYPE_FACE) != 0) {
            when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(enrolled);
            when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
            mBiometricService.mImpl.registerAuthenticator(0 /* id */, modality, strength,
            mBiometricService.mImpl.registerAuthenticator(1 /* id */, modality, strength,
                    mFaceAuthenticator);
        }
    }
@@ -1210,7 +1269,7 @@ public class BiometricServiceTest {
            if ((modality & BiometricAuthenticator.TYPE_FACE) != 0) {
                when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
                when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
                mBiometricService.mImpl.registerAuthenticator(0 /* id */, modality, strength,
                mBiometricService.mImpl.registerAuthenticator(1 /* id */, modality, strength,
                        mFaceAuthenticator);
            }
        }