# PIN Authentication ## Overview ### Function Personal Identification Number (PIN) authentication provides user authentication capabilities in identity authentication scenarios, such as device unlocking, payment, and app logins. After a user registers a PIN, the PIN authentication (Pin_auth) module unlocks the device only when the correct PIN is entered. The figure below shows the architecture of PIN authentication. The Pin_auth driver is developed based on the Hardware Driver Foundation (HDF). The Pin_auth driver model shields hardware differences and provides stable PIN authentication capabilities for the user User_auth framework (User_auth) and PIN authentication system ability (SA). The PIN authentication capabilities include obtaining the PIN authentication executor list, executor information, and anti-brute force information of the specified template, comparing the template ID list of the executor and that of User_auth, enrolling or deleting PINs, and performing PIN authentication. **Figure 1** PIN authentication architecture ![image](figures/pin_auth_architecture.png "PIN authentication architecture") ### Basic Concepts The identity authentication consists of User_auth and basic authentication services (including PIN authentication and facial recognition). It supports basic functions such as setting and deleting user credentials and performing authentication. - 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. - PIN 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 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. - 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 Pin_auth service and driver. For details, see [IDL](https://gitee.com/openharmony/ability_idl_tool/blob/master/README.md). - IPC Inter-Process Communication (IPC) is a mechanism that allows processes to communicate with each other. 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 Pin_auth driver provides basic PIN authentication capabilities for the upper-layer User_auth and Pin_auth service to ensure successful PIN authentication. You can develop drivers to call Hardware Device Interface (HDI) APIs based on the HDF and the chip you use. **Figure 2** Pin_auth service and Pin_auth driver APIs ![image](figures/pin_auth_service_and_driver_interaction.png "interaction between the pin_auth service and driver") ### Constraints PIN authentication must be implemented in a TEE, and the confidential information, such as PINs and credentials, must be stored in a TEE. ## Development Guidelines ### When to Use The Pin_auth driver provides basic PIN authentication capabilities for the User_auth and Pin_auth service to ensure successful PIN authentication. ### 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/pin_auth**. **Table 1** describes the HDI APIs for PIN credential enrollment, authentication, and deletion. **Table 2** describes the callbacks used to return the executor operation result to the framework or return the PIN entered by the user. **Table 1** Available APIs | API | Description | | ------------------------------- | ------------------------------------------- | | GetExecutorInfo(ExecutorInfo& executorInfo) | Obtains information about an executor.| | GetExecutorList(std::vector>& allInOneExecutors,
std::vector>& verifiers,
std::vector>& collectors) | Obtains the V2_0 executor list.| | 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.| | Cancel(uint64_t scheduleId) | Cancels an operation. | | SetData(uint64_t scheduleId, uint64_t authSubType,
const std::vector &data, int32_t resultCode) | Called to return the subtype of the PIN enrolled by the user and the anonymization PIN data. | | Enroll(uint64_t scheduleId, const std::vector& extraInfo,
const sptr& callbackObj) | Enrolls a PIN. | | Authenticate(uint64_t scheduleId, const std::vector& templateIdList, const std::vector& extraInfo, const sptr& callbackObj) | Starts PIN authentication. | | Delete(uint64_t templateId) | Deletes a PIN template. | | GetProperty(const std::vector& templateIdList,
const std::vector& propertyTypes, Property& property) | Obtains executor property information.| **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 prompt information about the operation process.| | IExecutorCallback::OnGetData(const std::vector& algoParameter, uint64_t authSubType, uint32_t algoVersion, const std::vector& challenge)| Called to return the PIN information obtained. | | IExecutorCallback::OnMessage(int32_t destRole, const std::vector& msg)| Called to return the interaction information about the operation process. | ### How to Develop The following uses the RK3568 platform as an example to demonstrate how to develop the Pin_auth driver.
The directory structure is as follows: ```text // drivers/peripheral/pin_auth ├── BUILD.gn # Build script ├── bundle.json # Component description file ├── test # Test cases └── hdi_service # Pin_auth driver implementation ├── BUILD.gn # Build script ├── adaptor # Implementation of related algorithms ├── common # Implementation of common interfaces ├── database # Database implementation ├── main # Entry for implementing PIN-related functions └── service # Entry for implementing the Pin_auth driver ├── inc # Header files └── src # Source files ├── all_in_one_impl.cpp # Implementation of authentication and enrollment APIs for the all-in-one executor ├── verifier_impl.cpp # Implementation of authentication and enrollment APIs for the verifier ├── collector_impl.cpp # Implementation of authentication and enrollment APIs for the collector ├── executor_impl_common.cpp # Utilities ├── pin_auth_interface_driver.cpp # Pin_auth driver entry └── pin_auth_interface_service.cpp # Implementation of the APIs for obtaining the executor list ``` The development procedure is as follows: 1. Develop the Pin_auth driver based on the HDF. The **Bind()**, **Init()**, **Release()**, and **Dispatch()** functions are used. For details about the code, see [pin_auth_interface_driver.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/pin_auth/hdi_service/service/src/pin_auth_interface_driver.cpp). ```c++ // Create the PinAuthInterfaceService object by using the custom HdfPinAuthInterfaceHost object, which consists of the IoService object and HDI service. struct HdfPinAuthInterfaceHost { struct IDeviceIoService ioService; OHOS::sptr stub; }; // Enable the IPC service to call the response API. static int32_t PinAuthInterfaceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, struct HdfSBuf *reply) { IAM_LOGI("start"); auto *hdfPinAuthInterfaceHost = CONTAINER_OF(client->device->service, struct HdfPinAuthInterfaceHost, 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 hdfPinAuthInterfaceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option); } // Initialize the HdfPinAuthInterfaceDriver object. static int HdfPinAuthInterfaceDriverInit(struct HdfDeviceObject *deviceObject) { IAM_LOGI("start"); std::shared_ptr pinHdi = OHOS::UserIAM::Common::MakeShared(); constexpr uint32_t SUCCESS = 0; if (pinHdi == nullptr || pinHdi->Init() != SUCCESS) { IAM_LOGE("Pin hal init failed"); return HDF_FAILURE; } return HDF_SUCCESS; } // Bind the service provided by the Pin_auth driver to the HDF. static int HdfPinAuthInterfaceDriverBind(struct HdfDeviceObject *deviceObject) { IAM_LOGI("start"); auto *hdfPinAuthInterfaceHost = new (std::nothrow) HdfPinAuthInterfaceHost; if (hdfPinAuthInterfaceHost == nullptr) { IAM_LOGE("%{public}s: failed to create HdfPinAuthInterfaceHost object", __func__); return HDF_FAILURE; } hdfPinAuthInterfaceHost->ioService.Dispatch = PinAuthInterfaceDriverDispatch; hdfPinAuthInterfaceHost->ioService.Open = NULL; hdfPinAuthInterfaceHost->ioService.Release = NULL; auto serviceImpl = IPinAuthInterface::Get(true); if (serviceImpl == nullptr) { IAM_LOGE("%{public}s: failed to get of implement service", __func__); return HDF_FAILURE; } hdfPinAuthInterfaceHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl, IPinAuthInterface::GetDescriptor()); if (hdfPinAuthInterfaceHost->stub == nullptr) { IAM_LOGE("%{public}s: failed to get stub object", __func__); return HDF_FAILURE; } deviceObject->service = &hdfPinAuthInterfaceHost->ioService; IAM_LOGI("success"); return HDF_SUCCESS; } // Release resources of the Pin_auth driver. static void HdfPinAuthInterfaceDriverRelease(struct HdfDeviceObject *deviceObject) { IAM_LOGI("start"); auto *hdfPinAuthInterfaceHost = CONTAINER_OF(deviceObject->service, struct HdfPinAuthInterfaceHost, ioService); delete hdfPinAuthInterfaceHost; IAM_LOGI("success"); } static struct HdfDriverEntry g_pinAuthInterfaceDriverEntry = { .moduleVersion = 1, .moduleName = "pinauth_interface_service", .Bind = HdfPinAuthInterfaceDriverBind, .Init = HdfPinAuthInterfaceDriverInit, .Release = HdfPinAuthInterfaceDriverRelease, }; // 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_pinauthinterfaceDriverEntry); ``` 2. Obtain the executor list. For details about the code, see [pin_auth_interface_service.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/pin_auth/hdi_service/service/src/pin_auth_interface_service.cpp). ```c++ // Executor implementation class class ExecutorImpl : public HdiIExecutor, public NoCopyable { public: explicit ExecutorImpl(std::shared_ptr pinHdi); ~ExecutorImpl() override; int32_t GetExecutorInfo(HdiExecutorInfo &info) override; int32_t OnRegisterFinish(const std::vector &templateIdList, const std::vector &frameworkPublicKey, const std::vector &extraInfo) override; int32_t Cancel(uint64_t scheduleId) override; int32_t SendMessage(uint64_t scheduleId, int32_t srcRole, const std::vector& msg) override; int32_t SetData(uint64_t scheduleId, uint64_t authSubType, const std::vector &data, int32_t resultCode) override; int32_t Enroll(uint64_t scheduleId, const std::vector &extraInfo, const sptr &callbackObj) override; int32_t Authenticate(uint64_t scheduleId, const std::vector& templateIdList, const std::vector &extraInfo, const sptr &callbackObj) override; int32_t Delete(uint64_t templateId) override; int32_t GetProperty(const std::vector &templateIdList, const std::vector &propertyTypes, HdiProperty &property) override; private: class ScheduleMap { public: uint32_t AddScheduleInfo(const uint64_t scheduleId, const uint32_t commandId, const sptr callback, const uint64_t templateId, const std::vector algoParameter); uint32_t GetScheduleInfo(const uint64_t scheduleId, uint32_t &commandId, sptr &callback, uint64_t &templateId, std::vector &algoParameter); uint32_t DeleteScheduleId(const uint64_t scheduleId); private: struct ScheduleInfo { uint32_t commandId; sptr callback; uint64_t templateId; std::vector algoParameter; }; std::mutex mutex_; std::map scheduleInfo_; }; private: void CallError(const sptr &callbackObj, uint32_t errorCode); int32_t AuthPin(uint64_t scheduleId, uint64_t templateId, const std::vector &data, std::vector &resultTlv); int32_t AuthenticateInner(uint64_t scheduleId, uint64_t templateId, std::vector &algoParameter, const sptr &callbackObj); int32_t EnrollInner(uint64_t scheduleId, const std::vector &extraInfo, const sptr &callbackObj, std::vector &algoParameter, uint32_t &algoVersion); void ReportAuthenticate(uint64_t scheduleId, uint64_t templateId, PinAuthResultBigData pinAuthResultBigData); std::shared_ptr pinHdi_; ScheduleMap scheduleMap_; OHOS::ThreadPool threadPool_; }; // Obtain the executor list and create an executor (example only). int32_t PinAuthInterfaceService::GetExecutorList(std::vector>& allInOneExecutors, std::vector>& verifiers, std::vector>& collectors) { IAM_LOGI("start"); static_cast(verifiers); static_cast(collectors); std::shared_ptr pinHdi = OHOS::UserIam::Common::MakeShared(); if (pinHdi == nullptr) { IAM_LOGE("Generate pinHdi failed"); return HDF_FAILURE; } sptr executor = new (std::nothrow) ExecutorImpl(pinHdi); if (executor == nullptr) { IAM_LOGE("Generate executor failed"); return HDF_FAILURE; } allInOneExecutors.push_back(executor); IAM_LOGI("end"); return HDF_SUCCESS; } ``` 3. Implement each function of the executor. For details about the code, see [all_in_one_impl.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/pin_auth/hdi_service/service/src/all_in_one_impl.cpp). ```c++ // Obtain executor information (example only). int32_t ExecutorImpl::GetExecutorInfo(HdiExecutorInfo &info) { IAM_LOGI("start"); if (pinHdi_ == nullptr) { IAM_LOGE("pinHdi_ is nullptr"); return HDF_FAILURE; } constexpr unsigned short SENSOR_ID = 1; info.sensorId = SENSOR_ID; info.executorMatcher = EXECUTOR_TYPE; info.executorRole = HdiExecutorRole::ALL_IN_ONE; info.authType = HdiAuthType::PIN; uint32_t eslRet = 0; int32_t result = pinHdi_->GetExecutorInfo(info.publicKey, eslRet); if (result != SUCCESS) { IAM_LOGE("Get ExecutorInfo failed, fail code : %{public}d", result); return result; } info.esl = static_cast(eslRet); 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 obtained. The executor compares its template ID list with the template ID list obtained and updates its template ID list. int32_t ExecutorImpl::OnRegisterFinish(const std::vector &templateIdList, const std::vector &frameworkPublicKey, const std::vector &extraInfo) { IAM_LOGI("start"); static_cast(frameworkPublicKey); static_cast(extraInfo); if (pinHdi_ == nullptr) { IAM_LOGE("pinHdi_ is nullptr"); return HDF_FAILURE; } int32_t result = pinHdi_->VerifyTemplateData(templateIdList); if (result != SUCCESS) { IAM_LOGE("Verify templateData failed"); return result; } return HDF_SUCCESS; } // Enroll the PIN. int32_t ExecutorImpl::Enroll(uint64_t scheduleId, const std::vector &extraInfo, const sptr &callbackObj) { IAM_LOGI("start"); if (callbackObj == nullptr) { IAM_LOGE("callbackObj is nullptr"); return HDF_ERR_INVALID_PARAM; } if (pinHdi_ == nullptr) { IAM_LOGE("pinHdi_ is nullptr"); CallError(callbackObj, INVALID_PARAMETERS); return HDF_SUCCESS; } std::vector algoParameter; uint32_t algoVersion = 0; int32_t result = EnrollInner(scheduleId, extraInfo, callbackObj, algoParameter, algoVersion); if (result != SUCCESS) { IAM_LOGE("EnrollInner failed, fail code : %{public}d", result); return HDF_SUCCESS; } std::vector challenge; result = callbackObj->OnGetData(algoParameter, 0, algoVersion, challenge); if (result != SUCCESS) { IAM_LOGE("Enroll Pin failed, fail code : %{public}d", result); CallError(callbackObj, GENERAL_ERROR); // If the enrollment fails, delete scheduleId of scheduleMap. if (scheduleMap_.DeleteScheduleId(scheduleId) != HDF_SUCCESS) { IAM_LOGI("delete scheduleId failed"); } } return HDF_SUCCESS; } // Implement the callback for returning data. int32_t ExecutorImpl::SetData(uint64_t scheduleId, uint64_t authSubType, const std::vector &data, int32_t resultCode) { IAM_LOGI("start"); if (pinHdi_ == nullptr) { IAM_LOGE("pinHdi_ is nullptr"); return HDF_FAILURE; } std::vector resultTlv; int32_t result = GENERAL_ERROR; constexpr uint32_t INVALID_ID = 2; uint32_t commandId = INVALID_ID; sptr callback = nullptr; uint64_t templateId = 0; std::vector algoParameter(0, 0); if (scheduleMap_.GetScheduleInfo(scheduleId, commandId, callback, templateId, algoParameter) != HDF_SUCCESS) { IAM_LOGE("Get ScheduleInfo failed, fail code : %{public}d", result); return HDF_FAILURE; } if (resultCode != SUCCESS && callback != nullptr) { IAM_LOGE("SetData failed, resultCode is %{public}d", resultCode); CallError(callback, resultCode); return resultCode; } switch (commandId) { case ENROLL_PIN: result = pinHdi_->EnrollPin(scheduleId, authSubType, algoParameter, data, resultTlv); if (result != SUCCESS) { IAM_LOGE("Enroll Pin failed, fail code : %{public}d", result); } break; case AUTH_PIN: result = AuthPin(scheduleId, templateId, data, resultTlv); if (result != SUCCESS) { IAM_LOGE("Auth Pin failed, fail code : %{public}d", result); } break; default: IAM_LOGE("Error commandId"); } if (callback == nullptr || callback->OnResult(result, resultTlv) != SUCCESS) { IAM_LOGE("callbackObj Pin failed"); } // Delete scheduleId from scheduleMap when the enrollment and authentication are successful. if (scheduleMap_.DeleteScheduleId(scheduleId) != HDF_SUCCESS) { IAM_LOGI("delete scheduleId failed"); } return HDF_SUCCESS; } // Perform PIN authentication. int32_t ExecutorImpl::Authenticate(uint64_t scheduleId, const std::vector& templateIdList, const std::vector &extraInfo, const sptr &callbackObj) { IAM_LOGI("start"); if (callbackObj == nullptr) { IAM_LOGE("callbackObj is nullptr"); return HDF_ERR_INVALID_PARAM; } if (pinHdi_ == nullptr || templateIdList.size() != 1) { IAM_LOGE("pinHdi_ is nullptr or templateIdList size not 1"); CallError(callbackObj, INVALID_PARAMETERS); return HDF_SUCCESS; } static_cast(extraInfo); std::vector algoParameter; uint32_t algoVersion = 0; uint64_t templateId = templateIdList[0]; int32_t result = pinHdi_->GetAlgoParameter(templateId, algoParameter, algoVersion); if (result != SUCCESS) { IAM_LOGE("Get algorithm parameter failed, fail code : %{public}d", result); CallError(callbackObj, result); return GENERAL_ERROR; } result = AuthenticateInner(scheduleId, templateId, algoParameter, callbackObj); if (result != SUCCESS) { IAM_LOGE("AuthenticateInner failed, fail code : %{public}d", result); return HDF_SUCCESS; } std::vector challenge; result = callbackObj->OnGetData(algoParameter, 0, algoVersion, challenge); if (result != SUCCESS) { IAM_LOGE("Authenticate Pin failed, fail code : %{public}d", result); CallError(callbackObj, GENERAL_ERROR); // If the authentication fails, delete scheduleId of scheduleMap. if (scheduleMap_.DeleteScheduleId(scheduleId) != HDF_SUCCESS) { IAM_LOGI("delete scheduleId failed"); } } return HDF_SUCCESS; } // Delete the PIN template. int32_t ExecutorImpl::Delete(uint64_t templateId) { IAM_LOGI("start"); if (pinHdi_ == nullptr) { IAM_LOGE("pinHdi_ is nullptr"); return HDF_FAILURE; } int32_t result = pinHdi_->DeleteTemplate(templateId); if (result != SUCCESS) { IAM_LOGE("Verify templateData failed, fail code : %{public}d", result); return result; } return HDF_SUCCESS; } // Cancel the operation based on the specified scheduleId. int32_t ExecutorImpl::Cancel(uint64_t scheduleId) { IAM_LOGI("start"); if (scheduleMap_.DeleteScheduleId(scheduleId) != HDF_SUCCESS) { IAM_LOGE("scheduleId is not found"); return HDF_FAILURE; } return HDF_SUCCESS; } // Obtain the executor property information. int32_t ExecutorImpl::GetProperty( const std::vector &templateIdList, const std::vector &propertyTypes, Property &property) { IAM_LOGI("start"); if (pinHdi_ == nullptr) { IAM_LOGE("pinHdi_ is nullptr"); return HDF_FAILURE; } if (templateIdList.size() != 1) { IAM_LOGE("templateIdList size is not 1"); return HDF_FAILURE; } uint64_t templateId = templateIdList[0]; OHOS::UserIam::PinAuth::PinCredentialInfo infoRet = {}; int32_t result = pinHdi_->QueryPinInfo(templateId, infoRet); if (result != SUCCESS) { IAM_LOGE("Get TemplateInfo failed, fail code : %{public}d", result); return HDF_FAILURE; } property.authSubType = infoRet.subType; property.remainAttempts = infoRet.remainTimes; property.lockoutDuration = infoRet.freezingTime; return HDF_SUCCESS; } ``` ### Verification Verify whether PIN authentication can be successfully performed on the RK3568 platform as follows: 1. Touch **Settings** > **Biometrics & passwords** > **Password**, and enter your password. 2. Press the power button to lock the screen. Press the power button again and enter the password. The screen is unlocked if a correct password is entered. 3. Touch **Settings** > **Biometrics & passwords** > **Password**, and verify **Disable lock screen password** and **Change lock screen password**. 4. After step 1 is complete, enter incorrect passwords for a specified number of times to check whether the anti-brute force cracking capability is normal. For example, if you enter incorrect passwords for five consecutive times, the device will be frozen for 60s.