OpenHarmony外设驱动使用 (九),Pin_auth
OpenHarmony外设驱动使用 (九)
Pin_auth
概述
功能简介
口令认证是端侧设备不可或缺的一部分,为设备提供一种用户认证能力,可应用于设备解锁、支付、应用登录等身份认证场景。用户注册口令后,口令认证模块就可为设备提供密码解锁的功能,保证设备的安全使用。口令识别的整体架构如图1。
基于HDF(Hardware Driver Foundation)驱动框架开发的Pin_auth驱动,Pin_auth驱动模型屏蔽硬件差异,为上层用户IAM子系统基础框架和口令认证SA提供稳定的口令认证基础能力,包括口令认证执行器列表查询、执行器信息查询、指定模板防暴信息查询、用户认证和执行器间的模板信息对账,以及口令的录入、认证、删除。
图1 口令认证架构图
基本概念
用户认证框架与各个基础认证服务(包含口令认证、人脸识别等)组成的身份认证系统,支持用户认证凭据设置、删除、认证等基础功能。
-
执行器
执行器是能够提供数据采集、处理、存储及比对能力的模块,各基础认证服务提供执行器能力,被身份认证框架调度完成各项基础能力。
-
执行器安全等级
执行器提供能力时所在运行环境达到的安全级别。
-
执行器角色
-
全功能执行器:执行器可独立处理一次凭据注册和身份认证请求,即可提供用户认证数据采集、处理、储存及比对能力。
-
采集器:执行器提供用户认证时的数据采集能力,需要和认证器配合完成用户认证。
-
认证器:认证器提供用户认证时的数据处理能力,读取存储的凭据模板与当前认证信息完成对比。
-
-
执行器类型
同一种身份认证类型的不同认证方式会产生认证算法差异,设备器件差异也会导致算法差异,执行器根据支持的算法类型差异或对接的器件差异,会定义不同的执行器类型。
-
用户认证框架公钥 & 执行器公钥
用户身份认证处理需要保证用户数据安全以及认证结果的准确性,用户认证框架与基础认证服务间的关键交互信息需要做数据完整性保护,各基础认证服务将提供的执行器能力对接到用户认证框架时,需要交换各自的公钥,其中:
-
执行器通过用户认证框架公钥校验调度指令的准确性。
-
执行器公钥可被用户认证框架用于校验认证结果的准确性,同时用于执行器交互认证时的校验交互信息的完整性。
-
-
口令认证凭据模板
认证凭据是在用户设置认证凭据时由认证服务产生并存储,每个模板有一个ID,用于索引模板信息文件,在认证时读取模板信息并用于与当次认证过程中产生的认证数据做对比,完成身份认证。
-
执行器对账
用户认证框架统一管理用户身份和凭据ID的映射关系,执行器对接到用户认证框架时,会读取用户身份认证框架内保存的该执行器的模板ID列表,执行器需要与自己维护的模板ID列表进行比对,并删除冗余信息。
-
IDL接口
接口定义语言(Interface Definition Language)通过IDL编译器编译后,能够生成与编程语言相关的文件:客户端桩文件,服务器框架文件。本文主要是通过IDL接口生成的客户端和服务端来实现Pin_auth服务和驱动的通信,详细使用方法可参考IDL简介。
-
IPC通信
IPC(Inter Process Communication),进程间通信是指两个进程的数据之间产生交互,详细原理可参考IPC通信简介。
-
HDI
HDI(Hardware Device Interface),硬件设备接口,位于基础系统服务层和设备驱动层之间,是提供给硬件系统服务开发者使用的、统一的硬件设备功能抽象接口,其目的是为系统服务屏蔽底层硬件设备差异,具体可参考HDI规范。
运作机制
Pin_auth驱动的主要工作是为上层用户认证框架和Pin_auth服务提供稳定的口令认证的基础能力,保证口令认证的功能可以正常运行。开发者可基于HDF框架对不同芯片进行各自驱动的开发以及HDI层接口的调用。
图2 Pin_auth服务和pin_auth驱动接口
约束与限制
口令认证的实现需要在TEE安全环境中实现,口令凭据等数据的加密信息需要在安全环境中存储。
开发指导
场景介绍
Pin_auth驱动的主要工作是为上层用户认证框架和Pin_auth服务提供稳定的口令认证基础能力,保证设备上口令认证功能可以正常运行。
接口说明
注:以下接口列举的为IDL接口描述生成的对应C++语言函数接口,接口声明见idl文件(/drivers/interface/pin_auth)。 在本文中,口令凭据的录入、认证和删除相关的HDI接口如表1所示,表2中的回调函数分别用于口令执行器返回操作结果给框架和获取用户输入的口令信息。
表1 接口功能介绍
接口名称 | 功能介绍 |
---|---|
GetExecutorInfo(ExecutorInfo& executorInfo) | 获取执行器信息。 |
GetExecutorList(std::vector<sptr<V2_0::IAllInOneExecutor>>& allInOneExecutors, std::vector<sptr<V2_0::IVerifier>>& verifiers, std::vector<sptr<V2_0::ICollector>>& collectors) | 获取V2_0执行器列表。 |
OnRegisterFinish(const std::vector<uint64_t>& templateIdList, const std::vector<uint8_t>& frameworkPublicKey, const std::vector<uint8_t>& extraInfo) | 执行器注册成功后,获取用户认证框架的公钥信息;获取用户认证框架的template 列表用于对账。 |
Cancel(uint64_t scheduleId) | 通过scheduleId取消指定操作。 |
SetData(uint64_t scheduleId, uint64_t authSubType, const std::vector<uint8_t> &data, int32_t resultCode) | 回调函数,返回用户录入的口令子类型和录入的口令脱敏数据。 |
Enroll(uint64_t scheduleId, const std::vector<uint8_t>& extraInfo, const sptr& callbackObj) | 录入pin码。 |
Authenticate(uint64_t scheduleId, const std::vector<uint64_t>& templateIdList, const std::vector<uint8_t>& extraInfo, const sptr& callbackObj) | pin码认证。 |
Delete(uint64_t templateId) | 删除pin码模板。 |
GetProperty(const std::vector<uint64_t>& templateIdList, const std::vector& propertyTypes, Property& property) | 获取执行器属性信息。 |
表2 回调函数介绍
接口名称 | 功能介绍 |
---|---|
IExecutorCallback::OnResult(int32_t result, const std::vector<uint8_t>& extraInfo) | 返回操作的最终结果。 |
IExecutorCallback::OnTip(int32_t tip, const std::vector<uint8_t>& extraInfo) | 返回操作的过程提示信息。 |
IExecutorCallback::OnGetData(const std::vector<uint8_t>& algoParameter, uint64_t authSubType, uint32_t algoVersion, const std::vector<uint8_t>& challenge) | 返回获取pin码数据信息。 |
IExecutorCallback::OnMessage(int32_t destRole, const std::vector<uint8_t>& msg) | 返回操作的过程交互消息。 |
开发步骤
以RK3568平台为例,我们提供了Pin_auth驱动DEMO实例,以下是目录结构及各部分功能简介。
// drivers/peripheral/pin_auth
├── BUILD.gn # 编译脚本
├── bundle.json # 组件描述文件
├── test # 测试用例
└── hdi_service # Pin_auth驱动实现├── BUILD.gn # 编译脚本├── adaptor # 相关算法实现├── common # 公共接口实现├── database # 数据库实现├── main # 口令相关功能实现入口└── service # Pin_auth驱动实现入口├── inc # 头文件└── src # 源文件├── all_in_one_impl.cpp # 全功能执行器认证、录入等功能接口实现├── verifier_impl.cpp # 认证器认证、录入等功能接口实现├── collector_impl.cpp # 采集器认证、录入等功能接口实现├── executor_impl_common.cpp # 工具类├── pin_auth_interface_driver.cpp # Pin_auth驱动入口└── pin_auth_interface_service.cpp # 获取执行器列表接口实现
下面结合DEMO实例介绍驱动开发的具体步骤。
-
基于HDF驱动框架,按照驱动Driver Entry程序,完成pin_auth驱动开发,主要由Bind、Init、Release、Dispatch函数接口实现,详细代码参见pin_auth_interface_driver.cpp文件。
// 通过自定义的HdfPinAuthInterfaceHost对象包含IoService对象和真正的HDI Service实现PinAuthInterfaceService对象 struct HdfPinAuthInterfaceHost {struct IDeviceIoService ioService;OHOS::sptr<OHOS::IRemoteObject> stub; };// 服务接口调用响应接口 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); }// 初始化接口 static int HdfPinAuthInterfaceDriverInit(struct HdfDeviceObject *deviceObject) {IAM_LOGI("start");std::shared_ptr<OHOS::UserIAM::PinAuth::PinAuth> pinHdi =OHOS::UserIAM::Common::MakeShared<OHOS::UserIAM::PinAuth::PinAuth>();constexpr uint32_t SUCCESS = 0;if (pinHdi == nullptr || pinHdi->Init() != SUCCESS) {IAM_LOGE("Pin hal init failed");return HDF_FAILURE;}return HDF_SUCCESS; }// PinAuth驱动对外提供的服务绑定到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; }// 释放PinAuth驱动中的资源 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, };// 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出 HDF_INIT(g_pinauthinterfaceDriverEntry);
-
完成获取执行器列表接口实现,详细代码参见pin_auth_interface_service.cpp文件。
// 执行器实现类class ExecutorImpl : public HdiIExecutor, public NoCopyable {public:explicit ExecutorImpl(std::shared_ptr<OHOS::UserIam::PinAuth::PinAuth> pinHdi);~ExecutorImpl() override;int32_t GetExecutorInfo(HdiExecutorInfo &info) override;int32_t OnRegisterFinish(const std::vector<uint64_t> &templateIdList,const std::vector<uint8_t> &frameworkPublicKey, const std::vector<uint8_t> &extraInfo) override;int32_t Cancel(uint64_t scheduleId) override;int32_t SendMessage(uint64_t scheduleId, int32_t srcRole, const std::vector<uint8_t>& msg) override;int32_t SetData(uint64_t scheduleId, uint64_t authSubType, const std::vector<uint8_t> &data,int32_t resultCode) override;int32_t Enroll(uint64_t scheduleId, const std::vector<uint8_t> &extraInfo,const sptr<HdiIExecutorCallback> &callbackObj) override;int32_t Authenticate(uint64_t scheduleId, const std::vector<uint64_t>& templateIdList,const std::vector<uint8_t> &extraInfo, const sptr<HdiIExecutorCallback> &callbackObj) override;int32_t Delete(uint64_t templateId) override;int32_t GetProperty(const std::vector<uint64_t> &templateIdList, const std::vector<int32_t> &propertyTypes,HdiProperty &property) override;private:class ScheduleMap {public:uint32_t AddScheduleInfo(const uint64_t scheduleId, const uint32_t commandId,const sptr<HdiIExecutorCallback> callback, const uint64_t templateId,const std::vector<uint8_t> algoParameter);uint32_t GetScheduleInfo(const uint64_t scheduleId, uint32_t &commandId, sptr<HdiIExecutorCallback> &callback,uint64_t &templateId, std::vector<uint8_t> &algoParameter);uint32_t DeleteScheduleId(const uint64_t scheduleId);private:struct ScheduleInfo {uint32_t commandId;sptr<HdiIExecutorCallback> callback;uint64_t templateId;std::vector<uint8_t> algoParameter;};std::mutex mutex_;std::map<uint64_t, struct ScheduleInfo> scheduleInfo_;};private:void CallError(const sptr<HdiIExecutorCallback> &callbackObj, uint32_t errorCode);int32_t AuthPin(uint64_t scheduleId, uint64_t templateId,const std::vector<uint8_t> &data, std::vector<uint8_t> &resultTlv);int32_t AuthenticateInner(uint64_t scheduleId, uint64_t templateId, std::vector<uint8_t> &algoParameter,const sptr<HdiIExecutorCallback> &callbackObj);int32_t EnrollInner(uint64_t scheduleId, const std::vector<uint8_t> &extraInfo,const sptr<HdiIExecutorCallback> &callbackObj, std::vector<uint8_t> &algoParameter, uint32_t &algoVersion);void ReportAuthenticate(uint64_t scheduleId, uint64_t templateId, PinAuthResultBigData pinAuthResultBigData);std::shared_ptr<OHOS::UserIam::PinAuth::PinAuth> pinHdi_;ScheduleMap scheduleMap_;OHOS::ThreadPool threadPool_;};// 获取执行器列表实现,创建执行器(仅作示例) int32_t PinAuthInterfaceService::GetExecutorList(std::vector<sptr<HdiIExecutor>>& allInOneExecutors,std::vector<sptr<HdiIVerifier>>& verifiers, std::vector<sptr<HdiICollector>>& collectors) {IAM_LOGI("start");static_cast<void>(verifiers);static_cast<void>(collectors);std::shared_ptr<OHOS::UserIam::PinAuth::PinAuth> pinHdi =OHOS::UserIam::Common::MakeShared<OHOS::UserIam::PinAuth::PinAuth>();if (pinHdi == nullptr) {IAM_LOGE("Generate pinHdi failed");return HDF_FAILURE;}sptr<HdiIExecutor> 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; }
-
完成执行器每个功能接口实现,详细代码参见all_in_one_impl.cpp文件。
// 实现获取执行器信息接口(仅作示例) 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<HdiExecutorSecureLevel>(eslRet);return HDF_SUCCESS; }// 实现执行器注册成功后,获取用户认证框架的公钥信息、获取用户认证框架的template 列表接口,将公钥信息保存,template列表用于和本地的template做对账 int32_t ExecutorImpl::OnRegisterFinish(const std::vector<uint64_t> &templateIdList,const std::vector<uint8_t> &frameworkPublicKey, const std::vector<uint8_t> &extraInfo) {IAM_LOGI("start");static_cast<void>(frameworkPublicKey);static_cast<void>(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; }// 实现口令录入接口 int32_t ExecutorImpl::Enroll(uint64_t scheduleId, const std::vector<uint8_t> &extraInfo,const sptr<IExecutorCallback> &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<uint8_t> 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<uint8_t> 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 enroll fails, delete scheduleId of scheduleMapif (scheduleMap_.DeleteScheduleId(scheduleId) != HDF_SUCCESS) {IAM_LOGI("delete scheduleId failed");}}return HDF_SUCCESS; }// 实现回调数据获取的接口 int32_t ExecutorImpl::SetData(uint64_t scheduleId, uint64_t authSubType, const std::vector<uint8_t> &data,int32_t resultCode) {IAM_LOGI("start");if (pinHdi_ == nullptr) {IAM_LOGE("pinHdi_ is nullptr");return HDF_FAILURE;}std::vector<uint8_t> resultTlv;int32_t result = GENERAL_ERROR;constexpr uint32_t INVALID_ID = 2;uint32_t commandId = INVALID_ID;sptr<HdiIExecutorCallback> callback = nullptr;uint64_t templateId = 0;std::vector<uint8_t> 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 the scheduleMap_ when the enroll and authentication are successfulif (scheduleMap_.DeleteScheduleId(scheduleId) != HDF_SUCCESS) {IAM_LOGI("delete scheduleId failed");}return HDF_SUCCESS; }// 实现口令认证接口 int32_t ExecutorImpl::Authenticate(uint64_t scheduleId, const std::vector<uint64_t>& templateIdList,const std::vector<uint8_t> &extraInfo, const sptr<HdiIExecutorCallback> &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<void>(extraInfo);std::vector<uint8_t> 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<uint8_t> 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 scheduleMapif (scheduleMap_.DeleteScheduleId(scheduleId) != HDF_SUCCESS) {IAM_LOGI("delete scheduleId failed");}}return HDF_SUCCESS; }// 实现删除口令模板接口 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; }// 实现通过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; }// 获取执行器属性信息接口 int32_t ExecutorImpl::GetProperty(const std::vector<uint64_t> &templateIdList, const std::vector<GetPropertyType> &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; }
调测验证
驱动开发完成后,可基于RK3568平台验证, 通过设备的设置和锁屏功能验证口令认证功能是否正常,测试步骤如下:
- 点击设备的 “ 设置 > 生物识别和密码 > 锁屏密码" 后,录入锁屏密码。
- 按设备电源键进行锁屏,再次按设备的电源键进行解锁,输入锁屏密码进行解锁验证,至此就完成了口令的录入和认证功能。
- 进入设置中的生物识别和密码,点击关闭锁屏密码或者更改锁屏密码,来验证口令的删除和更新功能是否正常。
- 在步骤1完成后,进行步骤2的输入锁屏密码时,输入错误密码达到一定的次数来验证,防暴力破解能力是否正常(例如:连续输入5次错误密码,设备将被冻结60s)。
相关文章:
OpenHarmony外设驱动使用 (九),Pin_auth
OpenHarmony外设驱动使用 (九) Pin_auth 概述 功能简介 口令认证是端侧设备不可或缺的一部分,为设备提供一种用户认证能力,可应用于设备解锁、支付、应用登录等身份认证场景。用户注册口令后,口令认证模块就可为设备…...
MySQL基础(InnoDB)
✅ InnoDB:支持事务、行级锁、外键。 为什么要用事务? 安全:如果中途发现错误(比如改错分数),可以一键撤销所有操作,就像游戏里的“回档”功能! 原子…...
自建srs实时视频服务器支持RTMP推流和拉流
文章目录 一、整体示意图二、服务器端1.srs简介及架构2.docker方式安装3.k8s方式安装4.端口 三、推流端1.OBS Studio2.ffmpeg推流3.streamlabs苹果手机4.twire安卓手机5.网络推流摄像头 四、拉流端1.vlc2.srs 参考awesome系列:https://github.com/juancarlospaco/aw…...
C++性能优化的7大核心策略与实战案例
在大型C项目中,性能优化需从语言特性、系统架构、硬件特性等多维度切入。以下是经过验证的关键技术路径👇 🔧 一、内存管理的极致控制 问题:频繁的动态内存分配会导致性能抖动和内存碎片,尤其在实时系统中可能…...
《国家高等教育智慧平台:重塑学习新时代》
时代之需:平台应运而生 在数字化浪潮席卷全球的当下,高等教育领域也在经历着深刻的变革。数字化技术的迅猛发展,正以前所未有的力量重塑着高等教育的形态。从在线课程的兴起,到虚拟实验室的应用,再到智能化教学工具的普…...
【Django】Django DRF 中如何手动调用分页器返回分页数据(APIView,action场景)
📦 Django DRF 中如何手动调用分页器返回分页数据(APIView,action场景) 在使用 Django REST Framework(DRF)时,很多人习惯了用 GenericAPIView 或 ViewSet 自动帮我们处理分页。但在某些场景中…...
遨游科普:三防平板有哪些品牌?哪个品牌值得推荐?
在工业数字化与户外作业场景日益多元化的今天,三防平板凭借其卓越的防护性能与功能集成能力,成为电力巡检、地质勘探、应急救援等领域不可或缺的智能终端。所谓“三防”,即防尘、防水、防摔,国际标准IP68与军用标准MIL-STD-810H的…...
Flannel后端为UDP模式下,分析数据包的发送方式(一)
Flannel 使用的是 UDP 模式,分析发往 10.244.2.5 的数据包会从哪个网卡发出。 路由表 以下是提供的路由表: Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.1.1 …...
华为鸿蒙电脑发布,折叠屏怎么选?
1⃣屏幕特性: 分辨率:高分辨率能保证图像和文字的清晰细腻 屏幕材质:OLED 屏幕通常具有更好的对比度、色彩表现和更广的色域 刷新率:支持自适应刷新率的屏幕可以根据不同的使用场景自动调整刷新率,在保证流畅度的同时优…...
将VMware上的虚拟机和当前电脑上的Wifi网卡处在同一个局域网下,实现同一个局域网下实现共享
什么是桥接模式:桥接模式(Bridging Mode)是一种网络连接模式,常用于虚拟机环境中,将虚拟机的虚拟网络适配器直接连接到主机的物理网络适配器上,使虚拟机能够像独立的物理设备一样直接与物理网络通信 1.打开…...
论文阅读:Auto-Encoding Variational Bayes
对图像生成论文自编码变分贝叶斯Auto-Encoding Variational Bayes原理理解和记录 Abstract 我们如何在有向概率模型中,在具有难以处理的后验分布的连续潜在变量z和大型数据集的存在下,执行有效的推理和学习? 我们介绍了一种随机变分推理和学…...
API面临哪些风险,如何做好API安全?
API面临的风险 API(应用程序编程接口)在现代软件开发和集成中扮演着至关重要的角色,但同时也面临着多种安全风险,主要包括以下几个方面: 数据泄露风险: API通常涉及敏感数据的传输和交换,如用…...
C# Prism框架详解:构建模块化WPF应用程序
1. Prism框架简介 Prism是一个用于构建松散耦合、可测试和可维护的WPF桌面应用程序的框架。它最初由微软模式与实践团队开发,现在由社区维护,是构建企业级WPF应用程序的首选框架之一。 Prism框架的核心优势: 模块化设计:将应用…...
【工具教程】图片识别内容改名,图片指定区域识别重命名,批量识别单据扫描件批量改名,基于WPF和腾讯OCR的实现方案
基于WPF和腾讯OCR的图片指定区域识别与批量重命名实现方案 一、应用场景 电商商品管理 电商平台每天需处理大量商品图片,原始文件名无规律(如IMG_001.jpg)。通过指定图片中商品名称、颜色、尺码等区域,OCR识别后自动重命名…...
数独求解器3.0 增加latex格式读取
首先说明两种读入格式 latex输入格式说明 \documentclass{article} \begin{document}This is some text before oku.\begin{array}{|l|l|l|l|l|l|l|l|l|} \hline & & & & 5 & & 2 & 9 \\ \hline& & 5 & 1 & & 7…...
WPF核心类继承树结构
WPF(Windows Presentation Foundation)的类继承结构非常庞大而复杂,以下是最核心的继承树结构,按照主要功能区域展示: 基础对象层级 Object └── DispatcherObject└── DependencyObject├── Freezable│ ├── Animatable│ │ …...
Mysql的binlog日志
环境准备 [rootmysql152 ~]# yum install -y mysql-server mysql [rootmysql152 ~]# systemctl enable mysqld --now1.查看正在使用的binlog日志文件 mysql> show master status; ---------------------------------------------------------------------------- | File …...
Java 安全SPEL 表达式SSTI 模版注入XXEJDBCMyBatis 注入
https://github.com/bewhale/JavaSec https://github.com/j3ers3/Hello-Java-Sec https://mp.weixin.qq.com/s/ZO4tpz9ys6kCIryNhA5nYw #Java 安全 -SQL 注入 -JDBC&MyBatis -JDBC 1 、采用 Statement 方法拼接 SQL 语句 2 、 PrepareStatement 会对 SQL 语…...
TypeScript 泛型讲解
如果说 TypeScript 是一门对类型进行编程的语言,那么泛型就是这门语言里的(函数)参数。本章,我将会从多角度讲解 TypeScript 中无处不在的泛型,以及它在类型别名、对象类型、函数与 Class 中的使用方式。 一、泛型的核…...
BERT、GPT-3与超越:NLP模型演进全解析
自然语言处理(NLP)领域近年来经历了前所未有的变革,从早期的统计方法到如今的深度学习大模型,技术的进步推动了机器理解、生成和交互能力的飞跃。其中,BERT和GPT-3作为两个里程碑式的模型,分别代表了不同的…...
RISC-V IDE MRS2 开发笔记一:volatile关键字的使用
RISC-V IDE MRS2 开发笔记一:volatile关键字的使用 一、volatile是什么 二、GCC 中 volatile 的行为 2.1禁止编译器优化 2.2 不等于内存屏障 2.3 GCC扩展行为 三、什么时候需要 volatile 3.1防止编译器优化掉“有效代码” 3.2 访问硬件寄存器 3.3 中断服务…...
25、工业防火墙 - 工控网络保护 (模拟) - /安全与维护组件/industrial-firewall-dcs-protection
76个工业组件库示例汇总 工业防火墙 - 工控网络保护 (模拟) 概述 这是一个交互式的 Web 组件,旨在模拟工业防火墙在保护关键工控网络(特别是 DCS - 分布式控制系统)免受网络攻击(如勒索软件传播)方面的核心功能。组件通过可视化简化的网络拓扑、模拟网络流量、应用防火…...
LAN(局域网)和WAN(广域网)
你的问题非常清晰!我来用一个直观的比喻实际拓扑图帮你彻底理解LAN(局域网)和WAN(广域网)如何协同工作,以及路由器在其中的位置。你可以把整个网络想象成一座城市: 1. 比喻:城市交通…...
ArcGIS Pro 3.4 二次开发 - Arcade
环境:ArcGIS Pro SDK 3.4 .NET 8 文章目录 Arcade1 基本查询1.1 基本查询1.2 使用要素进行基本查询1.3 使用 FeatureSetByName 检索要素1.4 使用过滤器检索要素1.5 使用数学函数计算基本统计量1.6 使用 FeatureSet 函数的 Filter 和 Intersects 2 评估表达式2.1 评…...
PCB智能报价系统——————仙盟创梦IDE
软件署名 代码贡献: 紫金电子科技有限公司 文案正路:cybersnow 正文 对企业的竞争力有着深远影响。传统的 PCB 报价方式往往依赖人工核算,不仅耗时较长,还容易出现误差。随着科技的发展,PCB 自动报价系统应运而生&a…...
灾备认证助力构建数据资产安全防线
信息安全保障人员(CISAW)-灾难备份与恢复认证 1.权威认证体系:技术护城河 在数字化进程加速的背景下,数据资产已成为政府与企业的核心资源,容灾备份能力成为保障业务连续性的关键。特别是近年来,因灾备缺…...
[特殊字符] 遇见Flask
一、初识Flask:像风一样自由 想象一下,你手里有一盒乐高积木——没有说明书,但每一块都精致小巧,任你组合成城堡、飞船,甚至整个宇宙。Flask就是这样一个存在。它不像Django那样“手把手教你搭房子”,而是…...
Axure高级交互设计:中继器嵌套动态面板实现超强体验感台账
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!如有帮助请订阅专栏! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:中继器嵌套动态面板 主要内容:中继器内部嵌套动态面板,实现可移动式台账,增强数据表现…...
告别手动绘图!2分钟用 AI 生成波士顿矩阵
波士顿矩阵作为经典工具,始终是企业定位产品组合、制定竞争策略的核心方法论。然而,传统手动绘制矩阵的方式,往往面临数据处理繁琐、图表调整耗时、团队协作低效等痛点。 随着AI技术的发展,这一现状正在被彻底改变。boardmix博思白…...
iframe加载或者切换时候,短暂的白屏频闪问题解决
问题描述 iframe加载或者是切换iframe链接的时候,会有短暂的白屏,这个时候是在加载,目前没有想到避免的问题,应该是浏览器层面的,所以解决方法之一就是,用页面的主题背景色来遮盖一下,当他加载…...
Python数据可视化高级实战之一——绘制GE矩阵图
目录 一、课程概述 二、GE矩阵? 三、GE 矩阵图的适用范围 五、GE 矩阵的评估方法 (一)市场吸引力的评估要素 二、企业竞争实力的评估要素 三、评估方法与实践应用 1. 定量与定性结合法 2. 数据来源 六、GE矩阵的图形化实现 七、总结:GE 矩阵与 BCG 矩阵的对比分析 (一)GE…...
量子计算与云计算的融合:技术前沿与应用前景
目录 引言 量子计算基础 量子计算的基本原理 量子计算的优势与挑战 量子计算的发展阶段 云计算基础 云计算的基本概念 云计算的应用领域 云计算面临的挑战 量子计算与云计算的结合 量子云计算的概念与架构 量子云计算的服务模式 量子云计算的优势 量子云计算的发展…...
QMK固件RGB矩阵照明功能详解 - 打造你的专属炫彩键盘
QMK固件RGB矩阵照明功能详解 - 打造你的专属炫彩键盘 🌈 大家好!今天我要详细讲解QMK固件中的RGB矩阵照明功能,让你轻松打造一个真正炫彩的机械键盘!本文从基础原理到实战配置,手把手教你如何配置各种绚丽的灯光效果,即使你是小白也能轻松上手!文中所有代码都配有详细的…...
Rust 学习笔记:关于泛型的练习题
Rust 学习笔记:关于泛型的练习题 Rust 学习笔记:关于泛型的练习题问题 1下面代码能否通过编译?若能,输出是?下面代码能否通过编译?若能,输出是? Rust 学习笔记:关于泛型的…...
Panasonic松下焊接机器人节气
Panasonic松下焊接机器人节气装置 一、工作原理 松下焊接机器人节气装置的工作原理主要是通过智能控制技术,实现对焊接过程中气体流量的精确调节。例如,在焊接的不同阶段,根据焊接电流的大小自动调整气体的供给量。当焊接电流较强时&#x…...
2023 睿抗机器人开发者大赛CAIP-编程技能赛-本科组(国赛) 解题报告 | 珂学家
前言 题解 2023 睿抗机器人开发者大赛CAIP-编程技能赛-本科组(国赛)。 vp了下,题目挺好的,难度也适中,但是彻底红温了。 第二题,题意不是那么清晰, M i n ( K 1 , K 2 ) Min(K_1, K_2) Min(K1,K2)容易求&#x…...
LeetCode 3355.零数组变换 I:差分数组
【LetMeFly】3355.零数组变换 I:差分数组 力扣题目链接:https://leetcode.cn/problems/zero-array-transformation-i/ 给定一个长度为 n 的整数数组 nums 和一个二维数组 queries,其中 queries[i] [li, ri]。 对于每个查询 queries[i]&am…...
java21
1.数据结构之二叉树,二叉查找树,平衡二叉树 原理:和节点比较大小然后选左边还是右边 注意:中序遍历最常见,因为遍历的结果是从小到大 注意是任意节点,上面的二叉查找树的节点10的左子树高度是0,…...
ES的倒排索引和正排索引的区别及适用场景?为什么倒排索引适合全文搜索?
正排索引解析 1.存储机制 Elasticsearch 的文档存储机制比简单的固定字节分配更复杂,其核心原理如下: 1. 数值类型存储机制 对于 long/float/double 等数值类型: 文档ID2 → 偏移量 2 * sizeof(type) 例如:long 类型…...
ElasticSearch各种查询语法示例
1. 每种查询语法的区别与优缺点 Query DSL 区别: JSON 格式的结构化查询,功能强大,支持复杂查询逻辑,适用于 Elasticsearch 的核心查询场景。优点: 灵活,功能全面,支持全文搜索、精确匹配、聚合等;可组合…...
Ubuntu20.04部署KVM
文章目录 一. 环境准备关闭防火墙(UFW)禁用 SELinux更换镜像源检查 CPU 虚拟化支持 二. 安装KVM安装 KVM 及相关组件启动 libvirtd 服务验证安装创建虚拟机 一. 环境准备 4C8G,50G硬盘——VMware Workstation需要给虚拟机开启虚拟化引擎 roo…...
Android车载应用开发:Kotlin与Automotive OS深度实践
一、Android Automotive OS简介 Android Automotive OS(AAOS)是Google为车辆定制的操作系统,直接运行于车机硬件,提供完整的车载信息娱乐系统(IVI)。与Android Auto(手机投影方案)不…...
提示词工程(Prompt Engineering)是智能Agent交互中不可或缺的一环
Prompt Engineering 提示词,参考OpenAI提示词建议 2.5.1 概述 提示词工程(Prompt Engineering)是智能Agent交互中不可或缺的一环,它们指导智能体如何理解和回应用户的需求。这一节将探讨如何设计有效的提示词,以及如何通过精确的语言引导智能…...
九天画芯CEO张锦:AR 与 AI 融合重构智能终端生态,消费级市场迎来关键拐点
一、AR设备逆势突围:从技术验证到规模商用的跨越 根据洛图科技显示,2025 年 Q1 中国 XR(ARVR) 设备市场数据显示,AR 设备以 9.1 万台销量、116% 的同比增速远超 VR/MR 设备,这标志着消费级 AR 市场正式从 “…...
Python爬虫(32)Python爬虫高阶:动态页面处理与Scrapy+Selenium+BeautifulSoup分布式架构深度解析实战
目录 引言一、动态页面爬取的技术背景1.1 动态页面的核心特征1.2 传统爬虫的局限性 二、技术选型与架构设计2.1 核心组件分析2.2 架构设计思路1. 分层处理2. 数据流 三、代码实现与关键技术3.1 Selenium与Scrapy的中间件集成3.2 BeautifulSoup与Scrapy Item的整合3.3 分布式爬取…...
[案例七] NX二次开发标识特征的导入与布尔运算
本来做的是案例六NX二次开发减重块。但是当时在处理导入指定面时发现坐标转化存在很大问题,开了我很长时间,太难受了,决定放一放,昨天做了第六个案例采取了一种补救方法(实在不会的一种解决思路),个人认为NX这么优秀的软件应该有更好的方法,坐标转化真让人头大。刚开始…...
Canvas进阶篇:鼠标交互动画
Canvas进阶篇:鼠标交互动画 前言获取鼠标坐标鼠标事件点击事件监听代码示例效果预览 拖动事件监听代码示例效果预览 结语 前言 在上一篇文章Canvas进阶篇:基本动画详解 中,我们讲述了在Canvas中实现动画的基本步骤和动画的绘制方法。本文将进…...
Axure项目实战:智慧运输平台后台管理端-订单管理2(多级交互)
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!如有帮助请订阅专栏! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:订单管理2 主要内容:中继器筛选、表单跟随菜单拖动、审批数据互通等 应用场景:订单管理…...
AG-UI:重构AI代理与前端交互的下一代协议标准
目录 技术演进背景与核心价值协议架构与技术原理深度解析核心功能与标准化事件体系典型应用场景与实战案例开发者生态与集成指南行业影响与未来展望1. 技术演进背景与核心价值 1.1 AI交互的三大痛点 当前AI应用生态面临三大核心挑战: 交互碎片化:LangGraph、CrewAI等框架各…...
鸿蒙版Flutter库torch_light手电筒功能深度适配
鸿蒙版Flutter库torch_light手电筒功能深度适配:跨平台开发者的光明之路 本项目作者:kirk/坚果 适配仓库地址 作者仓库:https://github.com/svprdga/torch_light# 在数字化浪潮的推动下,跨平台开发框架如 Flutter 凭借其高效、…...