# Fingerprint Authentication ## Overview ### Function Fingerprint authentication is indispensable in identity authentication scenarios, such as device unlocking, payment, and app logins. The fingerprint authentication (Fingerprint_auth) module provides fingerprint authentication for a device after a user enrolls a fingerprint. The figure below shows the fingerprint authentication architecture. The Fingerprint_auth driver is developed based on the Hardware Driver Foundation (HDF). It shields hardware differences and provides stable fingerprint authentication capabilities for the upper-layer user authentication (User_auth) framework and Fingerprint_auth service. It provides APIs for obtaining the fingerprint authentication executor list, executor information, and template information by template ID, comparing fingerprint template information of the executor and that of User_auth, enrolling or deleting fingerprints, and performing fingerprint authentication. **Figure 1** Fingerprint authentication architecture ![image](figures/fingerprint_auth_architecture.png "Fingerprint authentication architecture") ### Basic Concepts The identity authentication consists of the User_auth framework and basic authentication services. It supports basic functions such as setting and deleting user credentials and performing authentication. The system supports user identity authentication and data collection, processing, storage, and comparison. - Executor The executor collects, processes, stores, and compares data for authentication. Each authentication service provides the executor capabilities, which are scheduled by User_auth to implement basic capabilities. - Executor security level Security level of the runtime environment when an executor provides capabilities. - 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. - User_auth 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 User_auth and basic authentication services. Public keys must be exchanged when the executor provided by a basic authentication service interworks with User_auth. The executor uses the User_auth public key to verify scheduling instructions. User_auth uses the executor public key to verify the authentication result accuracy and the integrity of the information exchanged with the executor. - Authentication credential 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 User_auth manages the mappings between user identities and credential IDs in a unified manner. When connecting to User_auth, the executor obtains the template ID list from User_auth and updates its template ID list based on the template ID list obtained. - HAPs In a broad sense, Harmony Ability Packages (HAPs) are application packages that can be installed on OpenHarmony. In this document, the HAPs only refer to the upper-layer applications of the Face_auth driver. - IDL interface An Interface Definition Language (IDL) is a language that lets a program or object written in one language communicate with another program written in an unknown language. An IDL compiler generates client stub files and server framework files. This document describes how to use the client and server generated by the IDL interface to implement communication between the Fingerprint_auth service and driver. For details, see [IDL](https://gitee.com/openharmony/ability_idl_tool/blob/master/README.md). - IPC Inter-Process Communication (IPC) implements data exchange between two processes. For details, see [IPC](https://gitee.com/openharmony/communication_ipc/blob/master/README.md). - HDI The hardware device interface (HDI) is located between the basic system service layer and the device driver layer. It provides APIs for abstracting hardware device functions, which shields underlying hardware device differences for system services. For details, see [HDI Specifications](../../design/hdi-design-specifications.md). ### Working Principles The fingerprint_auth driver provides stable basic fingerprint authentication capabilities for the upper-layer User_auth framework and Fingerprint_auth service to ensure successful fingerprint authentication on devices. The figure below shows the interaction between the Fingerprint_auth service and the Fingerprint_auth driver. The Fingerprint_auth service obtains executor information by using **GetExecutorInfo()** and registers the executor with the User_auth framework. The Fingerprint_auth service exchanges information with the Fingerprint_auth driver for authentication, identification, and query through the executor APIs. You can develop drivers to call Hardware Device Interface (HDI) APIs based on the HDF and the chip you use. **Figure 2** Interaction between the Fingerprint_auth service and Fingerprint_auth driver ![image](figures/fingerprint_auth_service_and_driver_interaction.png "Fingerprint_auth service and driver interaction") ### Constraints A Trusted Execution Environment (TEE) must be available on the device to store encrypted fingerprint feature information. ## Development Guidelines ### When to Use The fingerprint_auth driver provides stable basic fingerprint authentication capabilities for the upper-layer User_auth framework and Fingerprint_auth service to ensure successful fingerprint authentication on devices. To implement the preceding functions, you need to develop the Fingerprint_auth driver based on the HDF, and then implement the APIs for obtaining the executor list and functional APIs such as authentication and query. ### Available APIs The following table describes the C++ APIs generated from the Interface Definition Language (IDL) interface description. For details about the interface declaration, see the .idl file in **/drivers/interface/fingerprint_auth/**. **Table 1** describes the HDI APIs for fingerprint credential enrollment, authentication, recognition, and deletion. **Table 2** describes the callbacks used to return the executor operation result to the framework or return the authentication tip information to upper-layer applications. **Table 1** Available APIs | API | Description | | -------------------------------- | ----------------------------------- | | GetExecutorList(std::vector\>& allInOneExecutors) | Obtains the executor list of V2_0. | | GetExecutorInfo(ExecutorInfo &executorInfo) | Obtains the executor information, including the executor type, executor role, authentication type, security level, and executor public key. | | OnRegisterFinish(const std::vector\& templateIdList,
const std::vector\& frameworkPublicKey, const std::vector\& extraInfo) | Obtains the public key and template ID list from User_auth after the executor is registered successfully. | | Enroll(uint64_t scheduleId, const std::vector\& extraInfo,
const sptr\& callbackObj) | Enrolls a fingerprint. | | Authenticate(uint64_t scheduleId, const std::vector\& templateIdList, bool endAfterFirstFail,
const std::vector\& extraInfo, const sptr\& callbackObj) | Authenticates a fingerprint template (V2_0). | | Identify(uint64_t scheduleId, const std::vector\& extraInfo,
const sptr\& callbackObj) | Identifies a fingerprint. | | Delete(const std::vector\& templateIdList) | Deletes a fingerprint. | | Cancel(uint64_t scheduleId) | Cancels a fingerprint enrollment, authentication, or identification operation based on the **scheduleId**. | | SendCommand(int32_t commandId, const std::vector\& extraInfo,
const sptr\& callbackObj) | Sends commands to the Fingerprint_auth driver. | | GetProperty(const std::vector\& templateIdList,
const std::vector\& propertyTypes, Property& property) | Obtains executor property information. | | SetCachedTemplates(const std::vector\ &templateIdList) | Sets a list of templates to be cached. | | RegisterSaCommandCallback(const sptr\ &callbackObj) | Registers a callback to be invoked when an SA command is executed. | **Table 2** Callbacks | API | Description | | ------------------------------------------------------------ | ------------------------ | | IExecutorCallback::OnResult(int32_t result, const std::vector\& extraInfo) | Called to return the operation result. | | IExecutorCallback::OnTip(int32_t tip, const std::vector\& extraInfo) | Called to return the interaction information about the operation process. | | ISaCommandCallback::OnSaCommands(const std::vector\& commands) | Called to send the command list. | ### How to Develop The following uses the Hi3516D V300 development board as an example to demonstrate how to develop the Fingerprint_auth driver.
The directory structure is as follows: ```undefined // drivers/peripheral/fingerprint_auth ├── BUILD.gn # Build script ├── bundle.json # Component description file └── hdi_service # Fingerprint_auth driver implementation ├── BUILD.gn # Build script ├── include # Header files └── src # Source files ├── executor_impl.cpp # Implementation of authentication and enrollment APIs ├── fingerprint_auth_interface_driver.cpp # Fingerprint_auth driver entry └── fingerprint_auth_interface_service.cpp # Implementation of the API for obtaining the executor list ``` The development procedure is as follows: 1. Develop the Fingerprint_auth driver based on the HDF using the **Bind()**, **Init()**, **Release()**, and **Dispatch()** functions. For details about the code, see [fingerprint_auth_interface_driver.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/fingerprint_auth/hdi_service/src/fingerprint_auth_interface_driver.cpp).
The sample code is as follows: ```c++ // Create an IRemoteObject object by using the custom HdfFingerprintAuthInterfaceHost object, which consists of the IoService object and HDI service. struct HdfFingerprintAuthInterfaceHost { struct IDeviceIoService ioService; OHOS::sptr stub; }; // Enable the IPC service to call the response API. static int32_t FingerprintAuthInterfaceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, struct HdfSBuf *reply) { auto *hdfFingerprintAuthInterfaceHost = CONTAINER_OF(client->device->service, struct HdfFingerprintAuthInterfaceHost, ioService); OHOS::MessageParcel *dataParcel = nullptr; OHOS::MessageParcel *replyParcel = nullptr; OHOS::MessageOption option; if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) { HDF_LOGE("%{public}s: invalid data sbuf object to dispatch", __func__); return HDF_ERR_INVALID_PARAM; } if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) { HDF_LOGE("%{public}s: invalid reply sbuf object to dispatch", __func__); return HDF_ERR_INVALID_PARAM; } return hdfFingerprintAuthInterfaceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option); } // Initialize the HdfFingerprintAuthInterfaceDriver object. static int HdfFingerprintAuthInterfaceDriverInit(struct HdfDeviceObject *deviceObject) { HDF_LOGI("%{public}s: driver init start", __func__); return HDF_SUCCESS; } // Bind the service provided by the Fingerprint_auth driver to the HDF. static int HdfFingerprintAuthInterfaceDriverBind(struct HdfDeviceObject *deviceObject) { HDF_LOGI("%{public}s: driver bind start", __func__); auto *hdfFingerprintAuthInterfaceHost = new (std::nothrow) HdfFingerprintAuthInterfaceHost; if (hdfFingerprintAuthInterfaceHost == nullptr) { HDF_LOGE("%{public}s: failed to create create HdfFingerprintAuthInterfaceHost object", __func__); return HDF_FAILURE; } hdfFingerprintAuthInterfaceHost->ioService.Dispatch = FingerprintAuthInterfaceDriverDispatch; hdfFingerprintAuthInterfaceHost->ioService.Open = NULL; hdfFingerprintAuthInterfaceHost->ioService.Release = NULL; auto serviceImpl = OHOS::HDI::FingerprintAuth::V2_0::IFingerprintAuthInterface::Get(true); if (serviceImpl == nullptr) { HDF_LOGE("%{public}s: failed to get of implement service", __func__); delete hdfFingerprintAuthInterfaceHost; return HDF_FAILURE; } hdfFingerprintAuthInterfaceHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl, OHOS::HDI::FingerprintAuth::V2_0::IFingerprintAuthInterface::GetDescriptor()); if (hdfFingerprintAuthInterfaceHost->stub == nullptr) { HDF_LOGE("%{public}s: failed to get stub object", __func__); delete hdfFingerprintAuthInterfaceHost; return HDF_FAILURE; } deviceObject->service = &hdfFingerprintAuthInterfaceHost->ioService; return HDF_SUCCESS; } // Release the resources used by the Fingerprint_auth driver. static void HdfFingerprintAuthInterfaceDriverRelease(struct HdfDeviceObject *deviceObject) { HDF_LOGI("%{public}s: driver release start", __func__); if (deviceObject->service == nullptr) { return; } auto *hdfFingerprintAuthInterfaceHost = CONTAINER_OF(deviceObject->service, struct HdfFingerprintAuthInterfaceHost, ioService); if (hdfFingerprintAuthInterfaceHost != nullptr) { delete hdfFingerprintAuthInterfaceHost; } } // Register the Fingerprint_auth driver entry data structure object. struct HdfDriverEntry g_fingerprintAuthInterfaceDriverEntry = { .moduleVersion = 1, .moduleName = "drivers_peripheral_fingerprint_auth", .Bind = HdfFingerprintAuthInterfaceDriverBind, .Init = HdfFingerprintAuthInterfaceDriverInit, .Release = HdfFingerprintAuthInterfaceDriverRelease, }; // 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_fingerprintAuthInterfaceDriverEntry); ``` 2. Implement the API for obtaining the executor list. For details about the code, see [fingerprint_auth_interface_service.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/fingerprint_auth/hdi_service/src/fingerprint_auth_interface_service.cpp).
The sample code is as follows: ```c++ // Executor implementation class class AllInOneExecutorImpl : public IAllInOneExecutor { public: AllInOneExecutorImpl(struct ExecutorInfo executorInfo); virtual ~AllInOneExecutorImpl() {} 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" IFingerprintAuthInterface *FingerprintAuthInterfaceImplGetInstance(void) { auto fingerprintAuthInterfaceService = new (std::nothrow) FingerprintAuthInterfaceService(); if (fingerprintAuthInterfaceService == nullptr) { IAM_LOGE("fingerprintAuthInterfaceService is nullptr"); return nullptr; } return fingerprintAuthInterfaceService; } // Obtain the executor list of V2_0. int32_t FingerprintAuthInterfaceService::GetExecutorList(std::vector> &executorList) { IAM_LOGI("interface mock start"); for (auto executor : executorList_) { executorList.push_back(executor); } IAM_LOGI("interface mock success"); return HDF_SUCCESS; } ``` 3. Implement each function of the executor. For details about the code, see [all_in_one_executor_impl.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/fingerprint_auth/hdi_service/src/all_in_one_executor_impl.cpp).
The sample code is as follows: ```c++ // Obtain the executor information. int32_t AllInOneExecutorImpl::GetExecutorInfo(ExecutorInfo &executorInfo) { IAM_LOGI("interface mock start"); executorInfo = executorInfo_; IAM_LOGI("get executor information success"); return HDF_SUCCESS; } // After the executor is successfully registered, obtain the public key and template ID list from User_auth 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 AllInOneExecutorImpl::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 fingerprints. int32_t AllInOneExecutorImpl::Enroll( uint64_t scheduleId, const std::vector &extraInfo, const sptr &callbackObj) { IAM_LOGI("interface mock start"); static_cast(scheduleId); static_cast(extraInfo); if (callbackObj == nullptr) { IAM_LOGE("callbackObj is nullptr"); return HDF_ERR_INVALID_PARAM; } IAM_LOGI("enroll, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT); int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {}); if (ret != HDF_SUCCESS) { IAM_LOGE("callback result is %{public}d", ret); return HDF_FAILURE; } return HDF_SUCCESS; } // Perform fingerprint authentication. int32_t AllInOneExecutorService::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); if (callbackObj == nullptr) { IAM_LOGE("callbackObj is nullptr"); return HDF_ERR_INVALID_PARAM; } IAM_LOGI("authenticate, result is %{public}d", ResultCode::NOT_ENROLLED); int32_t ret = callbackObj->OnResult(ResultCode::NOT_ENROLLED, {}); if (ret != HDF_SUCCESS) { IAM_LOGE("callback result is %{public}d", ret); return HDF_FAILURE; } return HDF_SUCCESS; } // Identify fingerprints. int32_t AllInOneExecutorService::Identify( uint64_t scheduleId, const std::vector &extraInfo, const sptr &callbackObj) { IAM_LOGI("interface mock start"); static_cast(scheduleId); static_cast(extraInfo); if (callbackObj == nullptr) { IAM_LOGE("callbackObj is nullptr"); return HDF_ERR_INVALID_PARAM; } IAM_LOGI("identify, result is %{public}d", ResultCode::OPERATION_NOT_SUPPORT); int32_t ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {}); if (ret != HDF_SUCCESS) { IAM_LOGE("callback result is %{public}d", ret); return HDF_FAILURE; } return HDF_SUCCESS; } // Delete fingerprints. int32_t AllInOneExecutorService::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 AllInOneExecutorService::Cancel(uint64_t scheduleId) { IAM_LOGI("interface mock start"); static_cast(scheduleId); IAM_LOGI("cancel success"); return HDF_SUCCESS; } // Send template locking or unlocking command from the Fingerprint_auth service to the Fingerprint_auth driver. int32_t AllInOneExecutorService::SendCommand( int32_t commandId, const std::vector &extraInfo, const sptr &callbackObj) { IAM_LOGI("interface mock start"); static_cast(extraInfo); if (callbackObj == nullptr) { IAM_LOGE("callbackObj is nullptr"); return HDF_ERR_INVALID_PARAM; } int32_t ret; switch (commandId) { case DriverCommandId::LOCK_TEMPLATE: IAM_LOGI("lock template, result is %{public}d", ResultCode::SUCCESS); ret = callbackObj->OnResult(ResultCode::SUCCESS, {}); if (ret != HDF_SUCCESS) { IAM_LOGE("callback result is %{public}d", ret); return HDF_FAILURE; } break; case DriverCommandId::UNLOCK_TEMPLATE: IAM_LOGI("unlock template, result is %{public}d", ResultCode::SUCCESS); ret = callbackObj->OnResult(ResultCode::SUCCESS, {}); if (ret != HDF_SUCCESS) { IAM_LOGE("callback result is %{public}d", ret); return HDF_FAILURE; } break; case DriverCommandId::INIT_ALGORITHM: IAM_LOGI("init algorithm, result is %{public}d", ResultCode::SUCCESS); ret = callbackObj->OnResult(ResultCode::SUCCESS, {}); if (ret != HDF_SUCCESS) { IAM_LOGE("callback result is %{public}d", ret); return HDF_FAILURE; } break; default: IAM_LOGD("not support DriverCommandId : %{public}d", commandId); ret = callbackObj->OnResult(ResultCode::OPERATION_NOT_SUPPORT, {}); if (ret != HDF_SUCCESS) { IAM_LOGE("callback result is %{public}d", ret); return HDF_FAILURE; } } return HDF_SUCCESS; } // Obtain executor properties. int32_t AllInOneExecutorService::GetProperty( const std::vector &templateIdList, const std::vector &propertyTypes, Property &property) { IAM_LOGI("interface mock start"); property = {}; IAM_LOGI("get property success"); return HDF_SUCCESS; } // Set a list of templates to be cached. int32_t AllInOneExecutorService::SetCachedTemplates(const std::vector &templateIdList) { IAM_LOGI("interface mock start"); IAM_LOGI("set cached templates success"); return HDF_SUCCESS; } // Register the callback to be invoked when the SA command is executed. int32_t AllInOneExecutorService::RegisterSaCommandCallback(const sptr &callbackObj) { IAM_LOGI("interface mock start"); IAM_LOGI("register sa command callback success"); return HDF_SUCCESS; } ``` 4. Modify **serviceName2Config** in the **fingerprint_auth_service.cpp** file if you need to add a driver or modify driver information. ```c++ // base/user_iam/fingerprint_auth/services/src/fingerprint_auth_service.cpp void FingerprintAuthService::StartDriverManager() { IAM_LOGI("start"); int32_t ret = UserAuth::IDriverManager::Start(HDI_NAME_2_CONFIG); if (ret != UserAuth::ResultCode::SUCCESS) { IAM_LOGE("start driver manager failed"); } } ``` ### Verification Use the [User Authentication APIs](../../application-dev/reference/apis-user-authentication-kit/js-apis-useriam-userauth.md) to develop a HAP and verify the application on the RK3568 platform. The sample code for starting and canceling an authentication is as follows: 1. Initiate a request for user authentication and obtain the authentication result. ```ts // API version 10 import type {BusinessError} from '@ohos.base'; import userIAM_userAuth from '@ohos.userIAM.userAuth'; // Set authentication parameters. const authParam: userIAM_userAuth.AuthParam = { challenge: new Uint8Array([49, 49, 49, 49, 49, 49]), authType: [userIAM_userAuth.UserAuthType.PIN, userIAM_userAuth.UserAuthType.FINGERPRINT], authTrustLevel: userIAM_userAuth.AuthTrustLevel.ATL3, }; // Set the authentication page. const widgetParam: userIAM_userAuth.WidgetParam = { title: 'Verify identity', }; try { // Obtain an authentication object. let userAuthInstance = userIAM_userAuth.getUserAuthInstance(authParam, widgetParam); console.info('get userAuth instance success'); // Subscribe to the authentication result. userAuthInstance.on('result', { onResult(result) { console.info(`userAuthInstance callback result: ${JSON.stringify(result)}`); // Unsubscribe from the authentication result if required. userAuthInstance.off('result'); } }); console.info('auth on success'); userAuthInstance.start(); console.info('auth start success'); } catch (error) { const err: BusinessError = error as BusinessError; console.error(`auth catch error. Code is ${err?.code}, message is ${err?.message}`); } ``` 2. Cancel an authentication. ```ts // API version 10 import type {BusinessError} from '@ohos.base'; import userIAM_userAuth from '@ohos.userIAM.userAuth'; const authParam: userIAM_userAuth.AuthParam = { challenge: new Uint8Array([49, 49, 49, 49, 49, 49]), authType: [userIAM_userAuth.UserAuthType.PIN, userIAM_userAuth.UserAuthType.FINGERPRINT], authTrustLevel: userIAM_userAuth.AuthTrustLevel.ATL3, }; const widgetParam: userIAM_userAuth.WidgetParam = { title: 'Verify identity', }; try { // Obtain an authentication object. let userAuthInstance = userIAM_userAuth.getUserAuthInstance(authParam, widgetParam); console.info('get userAuth instance success'); // Start user authentication. userAuthInstance.start(); console.info('auth start success'); // Cancel the authentication. userAuthInstance.cancel(); console.info('auth cancel success'); } catch (error) { const err: BusinessError = error as BusinessError; console.error(`auth catch error. Code is ${err?.code}, message is ${err?.message}`); } ```