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

Commit 63e2c21f authored by Ilya Matyukhin's avatar Ilya Matyukhin Committed by Android (Google) Code Review
Browse files

Merge changes from topic "biometrics.face@1.1"

* changes:
  Add VTS tests for biometrics.face@1.1
  Define biometrics.face@1.1 with remote enrollment
parents 4b229a44 f21a4f0a
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
// This file is autogenerated by hidl-gen -Landroidbp.

hidl_interface {
    name: "android.hardware.biometrics.face@1.1",
    root: "android.hardware",
    vndk: {
        enabled: true,
    },
    srcs: [
        "IBiometricsFace.hal",
    ],
    interfaces: [
        "android.hardware.biometrics.face@1.0",
        "android.hidl.base@1.0",
    ],
    gen_java: true,
}
+82 −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 android.hardware.biometrics.face@1.1;
import @1.0::IBiometricsFace;
import @1.0::Status;
import @1.0::Feature;

/**
 * The HAL interface for biometric face authentication.
 */
interface IBiometricsFace extends @1.0::IBiometricsFace {
    /**
     * Enrolls a user's face for a remote client, for example Android Auto.
     *
     * The HAL implementation is responsible for creating a secure communication
     * channel and receiving the enrollment images from a mobile device with
     * face authentication hardware.
     *
     * Note that the Hardware Authentication Token must be valid for the
     * duration of enrollment and thus should be explicitly invalidated by a
     * call to revokeChallenge() when enrollment is complete, to reduce the
     * window of opportunity to re-use the challenge and HAT. For example,
     * Settings calls generateChallenge() once to allow the user to enroll one
     * or more faces or toggle secure settings without having to re-enter the
     * PIN/pattern/password. Once the user completes the operation, Settings
     * invokes revokeChallenge() to close the transaction. If the HAT is expired,
     * the implementation must invoke onError with UNABLE_TO_PROCESS.
     *
     * Requirements for using this API:
     * - Mobile devices MUST NOT delegate enrollment to another device by calling
     * this API. This feature is intended only to allow enrollment on devices
     * where it is impossible to enroll locally on the device.
     * - The path MUST be protected by a secret key with rollback protection.
     * - Synchronizing between devices MUST be accomplished by having both
     * devices agree on a secret PIN entered by the user (similar to BT
     * pairing procedure) and use a salted version of that PIN plus other secret
     * to encrypt traffic.
     * - All communication to/from the remote device MUST be encrypted and signed
     * to prevent image injection and other man-in-the-middle type attacks.
     * - generateChallenge() and revokeChallenge() MUST be implemented on both
     * remote and local host (e.g. hash the result of the remote host with a
     * local secret before responding to the API call) and any transmission of
     * the challenge between hosts MUST be signed to prevent man-in-the-middle
     * attacks.
     * - In the event of a lost connection, the result of the last
     * generateChallenge() MUST be invalidated and the process started over.
     * - Both the remote and local host MUST honor the timeout and invalidate the
     * challenge.
     *
     * This method triggers the IBiometricsFaceClientCallback#onEnrollResult()
     * method.
     *
     * @param hat A valid Hardware Authentication Token, generated as a result
     *     of a generateChallenge() challenge being wrapped by the gatekeeper
     *     after a successful strong authentication request.
     * @param timeoutSec A timeout in seconds, after which this enroll
     *     attempt is cancelled. Note that the framework can continue
     *     enrollment by calling this again with a valid HAT. This timeout is
     *     expected to be used to limit power usage if the device becomes idle
     *     during enrollment. The implementation is expected to send
     *     ERROR_TIMEOUT if this happens.
     * @param disabledFeatures A list of features to be disabled during
     *     enrollment. Note that all features are enabled by default.
     * @return status The status of this method call.
     */
    enrollRemotely(vec<uint8_t> hat, uint32_t timeoutSec,
        vec<Feature> disabledFeatures) generates (Status status);
};
+29 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 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.
 */

cc_test {
    name: "VtsHalBiometricsFaceV1_1TargetTest",
    defaults: ["VtsHalTargetTestDefaults"],
    srcs: ["VtsHalBiometricsFaceV1_1TargetTest.cpp"],
    static_libs: [
        "android.hardware.biometrics.face@1.0",
        "android.hardware.biometrics.face@1.1",
    ],
    test_suites: [
        "general-tests",
        "vts-core",
    ],
}
+163 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 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.
 */

#define LOG_TAG "biometrics_face_hidl_hal_test"

#include <android/hardware/biometrics/face/1.0/IBiometricsFaceClientCallback.h>
#include <android/hardware/biometrics/face/1.1/IBiometricsFace.h>

#include <VtsHalHidlTargetCallbackBase.h>
#include <android-base/logging.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>

#include <chrono>
#include <cstdint>
#include <random>

using android::sp;
using android::hardware::hidl_vec;
using android::hardware::Return;
using android::hardware::Void;
using android::hardware::biometrics::face::V1_0::FaceAcquiredInfo;
using android::hardware::biometrics::face::V1_0::FaceError;
using android::hardware::biometrics::face::V1_0::IBiometricsFaceClientCallback;
using android::hardware::biometrics::face::V1_0::OptionalUint64;
using android::hardware::biometrics::face::V1_0::Status;
using android::hardware::biometrics::face::V1_1::IBiometricsFace;

namespace {

// Arbitrary, nonexistent userId
constexpr uint32_t kUserId = 9;
constexpr uint32_t kTimeoutSec = 3;
constexpr auto kTimeout = std::chrono::seconds(kTimeoutSec);
constexpr char kFacedataDir[] = "/data/vendor_de/0/facedata";
constexpr char kCallbackNameOnError[] = "onError";

// Callback arguments that need to be captured for the tests.
struct FaceCallbackArgs {
    // The error passed to the last onError() callback.
    FaceError error;

    // The userId passed to the last callback.
    int32_t userId;
};

// Test callback class for the BiometricsFace HAL.
// The HAL will call these callback methods to notify about completed operations
// or encountered errors.
class FaceCallback : public ::testing::VtsHalHidlTargetCallbackBase<FaceCallbackArgs>,
                     public IBiometricsFaceClientCallback {
  public:
    Return<void> onEnrollResult(uint64_t, uint32_t, int32_t, uint32_t) override { return Void(); }

    Return<void> onAuthenticated(uint64_t, uint32_t, int32_t, const hidl_vec<uint8_t>&) override {
        return Void();
    }

    Return<void> onAcquired(uint64_t, int32_t, FaceAcquiredInfo, int32_t) override {
        return Void();
    }

    Return<void> onError(uint64_t, int32_t userId, FaceError error, int32_t) override {
        FaceCallbackArgs args = {};
        args.error = error;
        args.userId = userId;
        NotifyFromCallback(kCallbackNameOnError, args);
        return Void();
    }

    Return<void> onRemoved(uint64_t, const hidl_vec<uint32_t>&, int32_t) override { return Void(); }

    Return<void> onEnumerate(uint64_t, const hidl_vec<uint32_t>&, int32_t) override {
        return Void();
    }

    Return<void> onLockoutChanged(uint64_t) override { return Void(); }
};

// Test class for the BiometricsFace HAL.
class FaceHidlTest : public ::testing::TestWithParam<std::string> {
  public:
    void SetUp() override {
        mService = IBiometricsFace::getService(GetParam());
        ASSERT_NE(mService, nullptr);
        mCallback = new FaceCallback();
        mCallback->SetWaitTimeoutDefault(kTimeout);
        Return<void> ret1 = mService->setCallback(mCallback, [](const OptionalUint64& res) {
            ASSERT_EQ(Status::OK, res.status);
            // Makes sure the "deviceId" represented by "res.value" is not 0.
            // 0 would mean the HIDL is not available.
            ASSERT_NE(0UL, res.value);
        });
        ASSERT_TRUE(ret1.isOk());
        Return<Status> ret2 = mService->setActiveUser(kUserId, kFacedataDir);
        ASSERT_EQ(Status::OK, static_cast<Status>(ret2));
    }

    void TearDown() override {}

    sp<IBiometricsFace> mService;
    sp<FaceCallback> mCallback;
};

// enroll with an invalid (all zeroes) HAT should fail.
TEST_P(FaceHidlTest, EnrollRemotelyZeroHatTest) {
    // Filling HAT with zeros
    hidl_vec<uint8_t> token(69);
    for (size_t i = 0; i < 69; i++) {
        token[i] = 0;
    }

    Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {});
    ASSERT_EQ(Status::OK, static_cast<Status>(ret));

    // onError should be called with a meaningful (nonzero) error.
    auto res = mCallback->WaitForCallback(kCallbackNameOnError);
    EXPECT_TRUE(res.no_timeout);
    EXPECT_EQ(kUserId, res.args->userId);
    EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
}

// enroll with an invalid HAT should fail.
TEST_P(FaceHidlTest, EnrollRemotelyGarbageHatTest) {
    // Filling HAT with pseudorandom invalid data.
    // Using default seed to make the test reproducible.
    std::mt19937 gen(std::mt19937::default_seed);
    std::uniform_int_distribution<uint8_t> dist;
    hidl_vec<uint8_t> token(69);
    for (size_t i = 0; i < 69; ++i) {
        token[i] = dist(gen);
    }

    Return<Status> ret = mService->enrollRemotely(token, kTimeoutSec, {});
    ASSERT_EQ(Status::OK, static_cast<Status>(ret));

    // onError should be called with a meaningful (nonzero) error.
    auto res = mCallback->WaitForCallback(kCallbackNameOnError);
    EXPECT_TRUE(res.no_timeout);
    EXPECT_EQ(kUserId, res.args->userId);
    EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error);
}

}  // anonymous namespace

INSTANTIATE_TEST_SUITE_P(
        PerInstance, FaceHidlTest,
        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBiometricsFace::descriptor)),
        android::hardware::PrintInstanceNameToString);