From 8e2e6f2353283d37d971bad9af692661d889ee57 Mon Sep 17 00:00:00 2001 From: Annie_wang Date: Sat, 28 May 2022 13:59:15 +0800 Subject: [PATCH] update docs Signed-off-by: Annie_wang --- .../driver-peripherals-face_auth-des.md | 483 ++++++++++++++++++ .../driver/figures/face_auth_architecture.png | Bin 0 -> 12372 bytes ...ce_auth_service_and_driver_interaction.png | Bin 0 -> 15151 bytes 3 files changed, 483 insertions(+) create mode 100644 en/device-dev/driver/driver-peripherals-face_auth-des.md create mode 100644 en/device-dev/driver/figures/face_auth_architecture.png create mode 100644 en/device-dev/driver/figures/face_auth_service_and_driver_interaction.png diff --git a/en/device-dev/driver/driver-peripherals-face_auth-des.md b/en/device-dev/driver/driver-peripherals-face_auth-des.md new file mode 100644 index 0000000000..4b5779e6fd --- /dev/null +++ b/en/device-dev/driver/driver-peripherals-face_auth-des.md @@ -0,0 +1,483 @@ +# Face_auth + +## Overview + +### Function + +Facial authentication provides user authentication capabilities in identity authentication scenarios, such as device unlocking, payment, and app logins. It uses biometric recognition technologies to identify individuals based on facial characteristics. A camera is used to collect images or video streams that contain human faces, and automatically detect, track, and recognize human faces. Facial authentication is also called facial recognition or face recognition. The figure below shows the architecture of facial authentication. + +The face authentication driver (Face_auth) driver is developed based on the Hardware Driver Foundation (HDF) driver framework. It shields hardware differences and provides stable facial authentication capabilities for the user IAM framework (UserIAM) and Face_auth service. The facial authentication capabilities include obtaining facial recognition executor list, executor information, and template information by template ID, comparing face image template information of the executor and that of UserIAM, enrolling or deleting face templates, and performing facial authentication. + +**Figure 1** Facial authentication architecture + +![image](figures/face_auth_architecture.png "Facial authentication architecture") + +### Basic Concepts + +The identity authentication consists of UserIAM and basic authentication services (including PIN authentication and facial authentication). It supports basic functions such as setting and deleting user credentials and performing authentication. The system supports user identity authentication and provides data collection, processing, storage, and comparison capabilities. +- Executor + + The executor collects, processes, stores, and compares data for authentication. Each authentication service provides the executor capabilities, which are scheduled by UserIAM to implement basic capabilities. + +- Executor security level + + Certain security level is required for the execution environment of an executor. For example, the executor security level is low for an operation performed without access control and high for an operation performed in a Trusted Execution Environment (TEE). + +- Executor role + + - Executor: independently completes the entire process of credential registration and identity authentication. The executor can collect, process, store, and compare data to complete the authentication. + + - Collector: only collects data during user authentication. It needs to work with the authenticator to complete user authentication. + + - Authenticator: only processes data, obtains the stored credential template, and compares it with the authentication information generated. + +- Executor type + + The authentication algorithm varies depending on the authentication mode and device used. Different executor types are defined based on the supported algorithm type or the device in use. + +- UserIAM public key & executor public key + + To ensure user data security and authentication result accuracy, measures must be taken to protect the integrity of the key information exchanged between UserIAM and basic authentication services. Public keys must be exchanged when the executor provided by a basic authentication service interworks with UserIAM. + + The executor uses the UserIAM public key to verify scheduling instructions. For example, if a face image template is locked, the related facial authentication capability cannot be used. The instruction for unlocking the face image template must be verified before being executed. + + UserIAM uses the executor public key to verify the authentication result accuracy and the integrity of the information exchanged with the executor. + +- Facial authentication credential template + + Authentication credentials are generated and stored by the authentication service when users set authentication credentials. Each template has an ID to index a set of template information files. The template information needs to be compared with the authentication data generated during authentication to complete identity authentication. + +- Data verification by the executor + + UserIAM manages the mappings between user identities and credential IDs in a unified manner. When connecting to UserIAM, the executor obtains the template ID list from UserIAM and updates its template ID list based on the template ID list obtained. + +### Working Principles + +The Face_auth driver provides basic facial authentication capabilities for the UserIAM and Face_auth service to ensure successful facial authentication. +You can develop drivers to call Hardware Device Interface (HDI) APIs based on the HDF and the chip you use. + +**Figure 2** Face_auth service and Face_auth driver interaction + +![image](figures/face_auth_service_and_driver_interaction.png "Face_auth service and Face_auth driver interaction") + +### Constraints + +- To implement facial authentication, the device must have a camera with a face image pixel greater than 100x100. +- TEE must be available, and facial feature information must be encrypted and stored in a TEE. +- The face matching accuracy varies with people with similar looks and children whose facial features keep changing. If you are concerned about this, consider using other authentication modes. + +## Development Guidelines + +### When to Use + +The Face_auth driver provides basic facial authentication capabilities for the UserIAM and Face_auth service to ensure successful facial authentication. + +### Available APIs + +**Table 1** Available APIs + +| API | Description | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| GetExecutorList(std::vector>& executorList) | Obtains the executor list. | +| GetExecutorInfo(ExecutorInfo& info) | Obtain the executor information, including the executor type, executor role, authentication type, security level, and executor public key.| +| GetTemplateInfo(uint64_t templateId, TemplateInfo& info) | Obtains information about a face image template based on the specified template ID. | +| OnRegisterFinish(const std::vector& templateIdList,
const std::vector& frameworkPublicKey, const std::vector& extraInfo) | Obtains the public key and template ID list from UserIAM after the executor is registered successfully.| +| Enroll(uint64_t scheduleId, const std::vector& extraInfo,
const sptr& callbackObj) | Enrolls a face image template. | +| Authenticate(uint64_t scheduleId, const std::vector& templateIdList,
const std::vector& extraInfo, const sptr& callbackObj) | Performs facial authentication. | +| Identify(uint64_t scheduleId, const std::vector& extraInfo,
const sptr& callbackObj) | Performs face identification. | +| Delete(const std::vector& templateIdList) | Deletes a face image template. | +| Cancel(uint64_t scheduleId) | Cancels a face enrolling, authentication, or identification operation based on the **scheduleId**. | +| SendCommand(int32_t commandId, const std::vector& extraInfo,
const sptr& callbackObj) | Sends commands to the Face_auth service. | + +**Table 2** Callbacks + +| API | Description | +| ------------------------------------------------------------ | ------------------------ | +| IExecutorCallback::OnResult(int32_t code, const std::vector& extraInfo) | Called to return the operation result. | +| IExecutorCallback::OnAcquireInfo(int32_t code, const std::vector& extraInfo) | Called to return the interaction information about the operation process.| + +### How to Develop + +The following uses the Hi3516DV300 platform as an example to demonstrate how to develop the Face_auth driver.
The directory structure is as follows: + +```undefined +// drivers/peripheral/face_auth +├── BUILD.gn # Build script +├── bundle.json # Module description file +└── hdi_service # Face_auth driver implementation + ├── BUILD.gn # Build script + ├── include # Header files + └── src + ├── executor_impl.cpp # Implementation of authentication and enrollment APIs + ├── face_auth_interface_driver.cpp # Face_auth driver entry + └── face_auth_interface_service.cpp # Implementation of the APIs for obtaining the executor list +``` + +The development procedure is as follows: + +1. Develop the Face_auth driver based on the HDF. The **Bind()**, **Init()**, **Release()**, and **Dispatch()** functions are used. For details about the code, see [face_auth_interface_driver.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/face_auth/hdi_service/src/face_auth_interface_driver.cpp). + + ```c++ + // Create the IRemoteObject object by using the custom HdfFaceAuthInterfaceHost object, which consists of the IoService object and HDI service. + struct HdfFaceAuthInterfaceHost { + struct IDeviceIoService ioService; + OHOS::sptr stub; + }; + + // Enable the IPC service to call the response API. + static int32_t FaceAuthInterfaceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, + struct HdfSBuf *reply) + { + IAM_LOGI("start"); + auto *hdfFaceAuthInterfaceHost = CONTAINER_OF(client->device->service, + struct HdfFaceAuthInterfaceHost, ioService); + + OHOS::MessageParcel *dataParcel = nullptr; + OHOS::MessageParcel *replyParcel = nullptr; + OHOS::MessageOption option; + + if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) { + IAM_LOGE("%{public}s:invalid data sbuf object to dispatch", __func__); + return HDF_ERR_INVALID_PARAM; + } + if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) { + IAM_LOGE("%{public}s:invalid reply sbuf object to dispatch", __func__); + return HDF_ERR_INVALID_PARAM; + } + + return hdfFaceAuthInterfaceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option); + } + + // Initialize the HdfFaceAuthInterfaceDriver object. + int HdfFaceAuthInterfaceDriverInit(struct HdfDeviceObject *deviceObject) + { + IAM_LOGI("start"); + return HDF_SUCCESS; + } + + // Bind the service provided by the Face_auth driver to the HDF. + int HdfFaceAuthInterfaceDriverBind(struct HdfDeviceObject *deviceObject) + { + IAM_LOGI("start"); + auto *hdfFaceAuthInterfaceHost = new (std::nothrow) HdfFaceAuthInterfaceHost; + if (hdfFaceAuthInterfaceHost == nullptr) { + IAM_LOGE("%{public}s: failed to create create HdfFaceAuthInterfaceHost object", __func__); + return HDF_FAILURE; + } + + hdfFaceAuthInterfaceHost->ioService.Dispatch = FaceAuthInterfaceDriverDispatch; + hdfFaceAuthInterfaceHost->ioService.Open = NULL; + hdfFaceAuthInterfaceHost->ioService.Release = NULL; + + auto serviceImpl = IFaceAuthInterface::Get(true); + if (serviceImpl == nullptr) { + IAM_LOGE("%{public}s: Failed to implement the service", __func__); + return HDF_FAILURE; + } + + hdfFaceAuthInterfaceHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl, + IFaceAuthInterface::GetDescriptor()); + if (hdfFaceAuthInterfaceHost->stub == nullptr) { + IAM_LOGE("%{public}s: Failed to get stub object", __func__); + return HDF_FAILURE; + } + + deviceObject->service = &hdfFaceAuthInterfaceHost->ioService; + IAM_LOGI("success"); + return HDF_SUCCESS; + } + + // Release resources of the Face_auth driver. + void HdfFaceAuthInterfaceDriverRelease(struct HdfDeviceObject *deviceObject) + { + IAM_LOGI("start"); + auto *hdfFaceAuthInterfaceHost = CONTAINER_OF(deviceObject->service, + struct HdfFaceAuthInterfaceHost, ioService); + delete hdfFaceAuthInterfaceHost; + IAM_LOGI("success"); + } + + // Register the entry data structure object of the Face_auth driver. + struct HdfDriverEntry g_faceAuthInterfaceDriverEntry = { + .moduleVersion = 1, + .moduleName = "faceauth_interface_service", + .Bind = HdfFaceAuthInterfaceDriverBind, + .Init = HdfFaceAuthInterfaceDriverInit, + .Release = HdfFaceAuthInterfaceDriverRelease, + }; + + // Call HDF_INIT to register the driver entry with the HDF. When loading the driver, the HDF calls the Bind() function and then the Init() function. If the Init() function fails to be called, the HDF will call Release() to release driver resources and exit the driver model. + HDF_INIT(g_faceAuthInterfaceDriverEntry); + ``` + +2. Implement the API for obtaining the executor list. For details about the code, see [face_auth_interface_service.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/face_auth/hdi_service/src/face_auth_interface_service.cpp). + + ```c++ + // Executor implementation class + class ExecutorImpl : public IExecutor { + public: + ExecutorImpl(struct ExecutorInfo executorInfo); + virtual ~ExecutorImpl() {} + + private: + struct ExecutorInfo executorInfo_; // Executor information + }; + + static constexpr uint16_t SENSOR_ID = 123; // Executor sensor ID + static constexpr uint32_t EXECUTOR_TYPE = 123; // Executor type + static constexpr size_t PUBLIC_KEY_LEN = 32; //32-byte public key of the executor + + // Create an HDI service object. + extern "C" IFaceAuthInterface *FaceAuthInterfaceImplGetInstance(void) + { + auto faceAuthInterfaceService = new (std::nothrow) FaceAuthInterfaceService(); + if (faceAuthInterfaceService == nullptr) { + IAM_LOGE("faceAuthInterfaceService is nullptr"); + return nullptr; + } + return faceAuthInterfaceService; + } + + // Obtain the executor list and create an executor. + int32_t GetExecutorList(std::vector>& executorList) + { + IAM_LOGI("interface mock start"); + executorList.clear(); + struct ExecutorInfo executorInfoExample = { + .sensorId = SENSOR_ID, + .executorType = EXECUTOR_TYPE, + .executorRole = ExecutorRole::ALL_IN_ONE, + .authType = AuthType::FACE, + .esl = ExecutorSecureLevel::ESL0, // Executor security level, which ranges from ESL0 to ESL3 (highest). + .publicKey = std::vector(PUBLIC_KEY_LEN, 0), // 32-byte public key, using the Ed25519 algorithm. + .extraInfo = {}, + }; + auto executor = new (std::nothrow) ExecutorImpl(executorInfoExample); + if (executor == nullptr) { + IAM_LOGE("executor is nullptr"); + return HDF_FAILURE; + } + executorList.push_back(sptr(executor)); + IAM_LOGI("interface mock success"); + return HDF_SUCCESS; + } + ``` + +3. Implement each function of the executor. For details about the code, see [executor_impl.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/face_auth/hdi_service/src/executor_impl.cpp). + + ```c++ + // Obtain the executor information. + int32_t GetExecutorInfo(ExecutorInfo& info) + { + IAM_LOGI("interface mock start"); + info = executorInfo_; + IAM_LOGI("get executor information success"); + return HDF_SUCCESS; + } + + // Obtain template information based on templateId. + int32_t GetTemplateInfo(uint64_t templateId, TemplateInfo& info) + { + IAM_LOGI("interface mock start"); + static_cast(templateId); + info = {0}; + IAM_LOGI("get template information success"); + return HDF_SUCCESS; + } + + // After the executor is successfully registered, obtain the public key and template ID list from UserIAM and save the public key. The executor compares its template ID list with the template ID list obtained and updates its template ID list. + int32_t OnRegisterFinish(const std::vector& templateIdList, + const std::vector& frameworkPublicKey, const std::vector& extraInfo) + { + IAM_LOGI("interface mock start"); + static_cast(templateIdList); + static_cast(extraInfo); + static_cast(frameworkPublicKey); + IAM_LOGI("register finish"); + return HDF_SUCCESS; + } + + // Enroll face image. + int32_t Enroll(uint64_t scheduleId, const std::vector& extraInfo, + const sptr& callbackObj) + { + IAM_LOGI("interface mock start"); + static_cast(scheduleId); + static_cast(extraInfo); + IAM_LOGI("enroll, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT); + int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {}); + if (ret != ResultCode::SUCCESS) { + IAM_LOGE("callback result is %{public}d", ret); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } + + // Start facial authentication. + int32_t Authenticate(uint64_t scheduleId, const std::vector& templateIdList, + const std::vector& extraInfo, const sptr& callbackObj) + { + IAM_LOGI("interface mock start"); + static_cast(scheduleId); + static_cast(templateIdList); + static_cast(extraInfo); + IAM_LOGI("authenticate, result is %{public}d", ResultCode::NOT_ENROLLED); + int32_t ret = callbackObj->OnResult(ResultCode::NOT_ENROLLED, {}); + if (ret != ResultCode::SUCCESS) { + IAM_LOGE("callback result is %{public}d", ret); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } + + // Perform face recognition. + int32_t Identify(uint64_t scheduleId, const std::vector& extraInfo, + const sptr& callbackObj) + { + IAM_LOGI("interface mock start"); + static_cast(scheduleId); + static_cast(extraInfo); + IAM_LOGI("identify, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT); + int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {}); + if (ret != ResultCode::SUCCESS) { + IAM_LOGE("callback result is %{public}d", ret); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } + + // Delete the face image template. + int32_t Delete(const std::vector& templateIdList) + { + IAM_LOGI("interface mock start"); + static_cast(templateIdList); + IAM_LOGI("delete success"); + return HDF_SUCCESS; + } + + // Cancel the operation based on the specified scheduleId. + int32_t Cancel(uint64_t scheduleId) + { + IAM_LOGI("interface mock start"); + static_cast(scheduleId); + IAM_LOGI("cancel success"); + return HDF_SUCCESS; + } + + // Send template freezing or unlocking command from the Face_auth service to the Face_auth driver. + int32_t SendCommand(int32_t commandId, const std::vector& extraInfo, + const sptr& callbackObj) + { + IAM_LOGI("interface mock start"); + static_cast(extraInfo); + int32_t ret; + switch (commandId) { + case LOCK_TEMPLATE: + IAM_LOGI("unlock template, result is %{public}d", ResultCode::SUCCESS); + ret = callbackObj->OnResult(ResultCode::SUCCESS, {}); + if (ret != ResultCode::SUCCESS) { + IAM_LOGE("callback result is %{public}d", ret); + return HDF_FAILURE; + } + break; + case UNLOCK_TEMPLATE: + IAM_LOGI("unlock template, result is %{public}d", ResultCode::SUCCESS); + ret = callbackObj->OnResult(ResultCode::SUCCESS, {}); + if (ret != ResultCode::SUCCESS) { + IAM_LOGE("callback result is %{public}d", ret); + return HDF_FAILURE; + } + break; + default: + IAM_LOGD("not support CommandId : %{public}d", commandId); + ret = callbackObj->OnResult(ResultCode::GENERAL_ERROR, {}); + if (ret != ResultCode::SUCCESS) { + IAM_LOGE("callback result is %{public}d", ret); + return HDF_FAILURE; + } + } + return HDF_SUCCESS; + } + ``` + +4. Modify **serviceName2Config** in the **face_auth_service.cpp** file if you need to add a driver or modify driver information. + + ```c++ + // base/user_iam/face_auth/services/src/face_auth_service.cpp + void FaceAuthService::StartDriverManager() + { + IAM_LOGI("start"); + // Service name and ID of the driver to add or modify. The driver service name and ID must be globally unique. + const std::map serviceName2Config = { + {"face_auth_interface_service", {1, std::make_shared()}}, + }; + UserIAM::UserAuth::IDriverManager::GetInstance().Start(serviceName2Config); + } + ``` + +### Verification + +Use the [User Authentication APIs](../../application-dev/reference/apis/js-apis-useriam-userauth.md) to develop a JavaScript application and verify the application on the Hi3516DV300 platform. The sample code for verifying the authentication and authentication cancellation is as follows: + +```js +// API version 8 +import userIAM_userAuth from '@ohos.userIAM.userAuth'; +let auth = new userIAM_userAuth.UserAuth(); + +export default { + getVersion() { + console.info("start get version"); + let version = this.auth.getVersion(); + console.info("auth version = " + version); + }, + + startAuth() { + console.info("start auth"); + this.auth.auth(null, userIAM_userAuth.UserAuthType.FACE, userIAM_userAuth.AuthTrustLevel.ATL1, { + onResult: (result, extraInfo) => { + try { + console.info("auth onResult result = " + result); + console.info("auth onResult extraInfo = " + JSON.stringify(extraInfo)); + if (result == 'SUCCESS') { + // Add the logic to be executed when the authentication is successful. + } else { + // Add the logic to be executed when the authentication fails. + } + } catch (e) { + console.info("auth onResult error = " + e); + } + }, + + onAcquireInfo: (module, acquire, extraInfo) => { + try { + console.info("auth onAcquireInfo module = " + module); + console.info("auth onAcquireInfo acquire = " + acquire); + console.info("auth onAcquireInfo extraInfo = " + JSON.stringify(extraInfo)); + } catch (e) { + console.info("auth onAcquireInfo error = " + e); + } + } + }); + }, + + cancelAuth() { + console.info("start cancel auth"); + // Obtain contextId using auth(). + let contextId = auth.auth(null, userIAM_userAuth.UserAuthType.FACE, userIAM_userAuth.AuthTrustLevel.ATL1, { + onResult: (result, extraInfo) => { + console.info("auth onResult result = " + result); + }, + + onAcquireInfo: (module, acquire, extraInfo) => { + console.info("auth onAcquireInfo module = " + module); + } + }); + let cancelCode = this.auth.cancel(contextId); + if (cancelCode == userIAM_userAuth.Result.SUCCESS) { + console.info("cancel auth success"); + } else { + console.error("cancel auth fail"); + } + } +} +``` diff --git a/en/device-dev/driver/figures/face_auth_architecture.png b/en/device-dev/driver/figures/face_auth_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..a21c7c9ec4f17da65c81d8672c4ea9a9c5a1dd16 GIT binary patch literal 12372 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 literal 0 HcmV?d00001 -- GitLab