From 5fc5e0c5acd885246af18ac4da8cb8cfc96d2575 Mon Sep 17 00:00:00 2001 From: liyuxuan_hw <447978021@qq.com> Date: Fri, 27 Jun 2025 15:33:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0TEE=20kit=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E6=8C=87=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liyuxuan_hw <447978021@qq.com> --- zh-cn/application-dev/tee/Readme-CN.md | 5 + .../tee/ca-ta-development-guideline.md | 316 +++++++ .../tee/feature-development-guideline.md | 814 ++++++++++++++++++ zh-cn/application-dev/tee/figures/ca-ta.png | Bin 0 -> 51479 bytes .../tee/figures/overview-of-opentrustee.png | Bin 0 -> 26750 bytes zh-cn/application-dev/tee/tee-kit-overview.md | 79 ++ 6 files changed, 1214 insertions(+) create mode 100644 zh-cn/application-dev/tee/Readme-CN.md create mode 100644 zh-cn/application-dev/tee/ca-ta-development-guideline.md create mode 100644 zh-cn/application-dev/tee/feature-development-guideline.md create mode 100644 zh-cn/application-dev/tee/figures/ca-ta.png create mode 100644 zh-cn/application-dev/tee/figures/overview-of-opentrustee.png create mode 100644 zh-cn/application-dev/tee/tee-kit-overview.md diff --git a/zh-cn/application-dev/tee/Readme-CN.md b/zh-cn/application-dev/tee/Readme-CN.md new file mode 100644 index 00000000000..66cd2ba1c06 --- /dev/null +++ b/zh-cn/application-dev/tee/Readme-CN.md @@ -0,0 +1,5 @@ +# TEE Kit(可信执行环境开发套件) + +- [TEE Kit简介](tee-kit-overview.md) +- [CA-TA通信开发指导](ca-ta-development-guideline.md) +- [CA/TA特性开发指导](feature-development-guideline.md) \ No newline at end of file diff --git a/zh-cn/application-dev/tee/ca-ta-development-guideline.md b/zh-cn/application-dev/tee/ca-ta-development-guideline.md new file mode 100644 index 00000000000..03aecb18ae8 --- /dev/null +++ b/zh-cn/application-dev/tee/ca-ta-development-guideline.md @@ -0,0 +1,316 @@ +# CA-TA通信开发指导 + +可信应用的设计分为客户端应用和可信端应用,GlobalPlatform API标准定义(GP规范)定义了CA和TA之间的API接口,CA和TA按照规定的接口进行通信。典型客户端应用和可信端应用接口交互过程如下图所示: + +![CA-TA](figures/ca-ta.png) + +## CA-TA通信步骤 + +CA-TA的访问类似C/S架构,CA是client端,TA是server端,具体通信步骤如下: +1. CA首先调用TEEC_InitializeContext初始化TEE环境,建立与TEE的链接; +2. 根据TA的UUID,调用TEEC_OpenSession建立与TA的会话; +3. 调用TEEC_InvokeCommand向TA发送命令,可多次调用; +4. 调用TEEC_CloseSession结束与TA的会话; +5. 调用TEEC_FinalizeContext关闭与TEE的链接。 + +其中TEEC_InitializeContext和TEEC_FinalizeContext,还有TEEC_OpenSession和TEEC_CloseSession必须配对使用,即使在出现异常的情况下也要注意这一点,否则可能导致资源无法释放。 + +CA-TA之间支持同时建立多个session进行并发访问,前提是开发者的CA和TA在设计上都支持并发。如果开发者的CA需要请求多个TA的服务,也是可以的。 + +在调用TEEC_OpenSession或TEEC_InvokeCommand时,CA需要通过operation参数向TA发送数据。Operation参数的类型为结构体TEEC_Operation,定义在“tee_client_type.h”中,其参数格式如下: +```C +typedef struct { + uint32_t started; + uint32_t paramTypes; + TEEC_Parameter params[4]; + TEEC_Session *session; + bool cancel_flag; +} TEEC_Operation; +``` + +* 数据成员uint32_t started:为0表示取消操作,非0表示发送命令。目前仅支持非0,建议赋值为1即可。 +* 数据成员uint32_t paramTypes:该成员与下一个成员TEEC_Parameter params[4]是对应的,我们在一次访问中支持4组数据的传递,这4组数据可以分别是不同的类型,类型取值参考枚举TEEC_ParamType。开发者需要把4组数据的类型组装到paramTypes中,我们提供了宏TEEC_PARAM_TYPES来完成组装。举例如下: + +```C +paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, + TEEC_VALUE_OUTPUT, + TEEC_NONE, + TEEC_NONE); +``` + +该示例表示,第一组数据作为输入,第二组数据作为输出,其数据类型均为TEEC_Value类型,第三组和第四组数据没有使用。 + * 数据成员TEEC_Parameter params[4]:用来传递数据,支持4组数据的传递,这4组数据的类型需要与上个成员paramTypes中定义的类型一致。TEEC_Parameter是一个枚举类型,格式如下: + +```C +typedef union { + TEEC_TempMemoryReference tmpref; + TEEC_RegisteredMemoryReference memref; + TEEC_Value value; + TEEC_IonReference ionref; +} TEEC_Parameter; +``` + +该枚举定义了CA向TA传递数据的几种方式: +1. TEEC_Value:该类型只有两个uint32的空间,用于传递微量数据。 +2. TEEC_TempMemoryReference:CA创建一段临时缓冲区,使用该临时缓冲区传递数据到TEE。 +3. TEEC_RegisteredMemoryReference:CA在发送命令之前申请一段共享内存,通过共享内存将数据传递到TEE,这种方式可以减少内存的拷贝次数,在传递大量数据时可以提高效率。 +4. TEEC_IonReference:使用ION内存,这是一种特殊场景,并不具备普遍适用性。 + +关于TEEC_RegisteredMemoryReference这种方式,我们提供了获取共享内存的接口。获取共享内存的方式有以下两种: +1. 调用接口TEEC_RegisterSharedMemory,注册一块共享内存。这要求CA自己先申请一块内存,然后调用该接口将内存注册到TEE中,调用接口时需传入内存的地址。之后CA在发送命令时便可以使用该内存传递数据。 +2. 调用接口TEEC_AllocateSharedMemory,申请一块共享内存。采用这种方式,CA只需要传入申请内存的大小,而无需自己申请内存,TEE会为CA分配指定大小的内存。之后CA在发送命令时可以使用该内存传递数据。 + +CA在访问结束后,需要释放共享内存,对于上述两种方式,我们提供了同一个内存释放接口TEEC_ReleaseSharedMemory。对于方式1,该接口会解除TEE中的内存注册关系,但是不会释放CA自己申请的内存,CA需要自己释放。对于方式2,该接口会释放共享内存,CA无需自己释放。 + +共享内存相关函数接口定义在“tee_client_api.h”中,在使用前建议先阅读头文件注释。 + +> 注意: +> 1. 在调用TEEC_OpenSession接口时,params[2]和params[3]是预留给系统的,不允许CA使用,CA仅可以使用params[0]和params[1]。在调用TEEC_InvokeCommand接口时,则没有限制。 +> 2. 系统总共可用CA访问TA共享内存4M,无CA并发访问TEE时,限制为:单个TEEC_Parameter中buffer 最大1M,所有buffer总和需小于4M。 +> 3. TEEC_InvokeCommand接口支持传递为NULL的tempref buffer。 +> 4. TA默认加载路径与TA2TA无感知加载TA默认路径为: "/vendor/bin"。 + +## CA开发示例 + +CA开发示例如下: +```C +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tee_client_api.h" +#define VERSION_BUFFER_SIZE 256 +#define OPERATION_START_FLAG 1 +#define OUT_BUFF_INDEX 3 +static const TEEC_UUID g_demoTemplateUuid = { + 0xe3d37f4a, 0xf24c, 0x48d0, { 0x88, 0x84, 0x3b, 0xdd, 0x6c, 0x44, 0xe9, 0x88 } +}; +enum { + CMD_GET_TA_VERSION = 1, +}; +int main(void) +{ + TEEC_Context context = {0}; + TEEC_Session session = {0}; + TEEC_Result result; + TEEC_Operation operation = {0}; + uint32_t origin = 0; + char versionBuf[VERSION_BUFFER_SIZE] = {0}; + unsigned int bufLen = VERSION_BUFFER_SIZE; + /* 1.初始化TEE环境 */ + result = TEEC_InitializeContext(NULL, &context); + if (result != TEEC_SUCCESS) { + printf("teec initial failed"); + goto cleanup_1; + } + operation.started = OPERATION_START_FLAG; + operation.paramTypes = TEEC_PARAM_TYPES( + TEEC_NONE, + TEEC_NONE, + TEEC_NONE, + TEEC_NONE); + /* 2.与TA建立会话 */ + result = TEEC_OpenSession( + &context, &session, &g_demoTemplateUuid, TEEC_LOGIN_IDENTIFY, NULL, &operation, &origin); + if (result != TEEC_SUCCESS) { + printf("teec open session failed"); + goto cleanup_2; + } + operation.started = OPERATION_START_FLAG; + operation.paramTypes = TEEC_PARAM_TYPES( + TEEC_NONE, + TEEC_NONE, + TEEC_NONE, + TEEC_MEMREF_TEMP_OUTPUT); + operation.params[OUT_BUFF_INDEX].tmpref.buffer = versionBuf; + operation.params[OUT_BUFF_INDEX].tmpref.size = bufLen; + /* 3.向TA发送命令 */ + result = TEEC_InvokeCommand(&session, CMD_GET_TA_VERSION, &operation, &origin); + if (result != TEEC_SUCCESS) { + printf("invoke failed, codes=0x%x, origin=0x%x", result, origin); + } else { + printf("Succeed to load TA, TA's version: %s.\n", versionBuf); + } + /* 4.结束与TA会话 */ + TEEC_CloseSession(&session); +cleanup_2: + /* 5.关闭与TEE的链接 */ + TEEC_FinalizeContext(&context); +cleanup_1: + return 0; +} +``` + + +## TA开发示例 + +TA开发示例 +TA入口函数按照GP规范定义如下: +| TA 入口函数 | 函数描述 | +|-----------------------------|--------------------------------------------------------------------------| +| TA_CreateEntryPoint | TA 实例的构造函数,每个 TA 实例的生命周期中只被调用一次。 | +| TA_DestroyEntryPoint | TA 实例的析构函数,TEE 系统在销毁 TA 实例时调用此函数。 | +| TA_OpenSessionEntryPoint | 在 CA 请求创建一个与 TA 的会话时,TEE 系统会调用此函数来创建一个会话。 | +| TA_CloseSessionEntryPoint | 在 CA 发起关闭与 TA 的会话连接时,TEE 系统会调用此函数。 | +| TA_InvokeCommandEntryPoint | 在 CA 发送指令给 TA 时,需要指定之前创建成功的会话,TEE 系统收到请求后会调用此函数。 | + +TA的这些入口函数必须按照如下来定义,否则TEE里加载TA二进制文件会有异常。 +### TA_CreateEntryPoint +TA实例的构造函数,每个TA实例的生命周期中只被调用一次。如果函数执行失败,则表示创建TA实例失败。通常在CA请求创建会话时,如果TA实例不存在,TEE系统会调用此函数创建TA实例。
+ +TEE_Result TA_CreateEntryPoint(void)
+ +参数:无
+ +返回值:
+* TEE_SUCCESS:TA实例创建成功。 +* 其他:TA实例创建失败,具体原因参考错误码说明。 + +### TA_DestroyEntryPoint +TA实例的析构函数,TEE系统在销毁TA实例时调用此函数。通常在所有会话都关闭后TEE系统会调用此函数销毁TA实例。 + +void TA_DestroyEntryPoint(void)
+ +参数:无
+ +返回值:
+ +### TA_OpenSessionEntryPoint +在CA请求创建一个与TA的会话时,TEE系统会调用此函数来创建一个会话。后续CA与TA的通信都需要基于这个创建成功的会话来完成。用于标识一个会话的结构是由TA自己进行申请的,并在函数执行成功后返回给调用者。
+ +TEE_Result TA_OpenSessionEntryPoint(uint32_t paramTypes, TEE_Param params[4], void** sessionContext) + +参数: +* paramTypes:四个参数属性值。 +* params:指向四个参数数组的指针。 +* sessionContext:指向会话的二级指针。 + +返回值: +* TEE_SUCCESS:TA打开session成功。 +* 其他:指令执行失败,具体原因参考错误码说明。 + + +### TA_CloseSessionEntryPoint +在CA发起关闭与TA的会话连接时,TEE系统会调用此函数。TA需要释放用于此会话的资源。 + +void TA_CloseSessionEntryPoint(void* session_context)
+ +参数: +* Session_context:指向会话的指针。
+ +返回值:无 + +### TA_InvokeCommandEntryPoint +在CA发送指令给TA时,需要指定之前创建成功的会话,TEE系统收到请求后会调用此函数。TA根据指令ID和参数信息做相应的处理。 +TEE_Result TA_InvokeCommandEntryPoint(void* session_context, uint32_t cmd_id, uint32_t paramTypes, TEE_Param params[4]) +参数: + * session_context:指向会话的指针。 + * cmd_id:由TA自己定义的指令ID。 + * paramTypes:四个参数属性值。 + * params:指向四个参数数组的指针。 +返回值: + * TEE_SUCCESS:TA执行指令成功。 + * 其他:指令执行失败,具体原因参考错误码说明。 + +TA开发示例如下: + +```c +#include +#include +#include +#define TA_TEMPLATE_VERSION "demo_20200601" +#define PARAM_COUNT 4 +#define OUT_BUFFER_INDEX 3 +enum { + CMD_GET_TA_VERSION = 1, +}; +static TEE_Result get_ta_version(char* buffer, size_t *buf_len) +{ + const char *version = TA_TEMPLATE_VERSION; + if (*buf_len < strlen(version) + 1) { + tloge("buffer is too short for storing result"); + *buf_len = strlen(version) + 1; + return TEE_ERROR_SHORT_BUFFER; + } + errno_t err = strncpy_s(buffer, *buf_len, version, strlen(version) + 1); + if (err != EOK) + return TEE_ERROR_SECURITY; + *buf_len = strlen(version) + 1; + return TEE_SUCCESS; +} +/* TA构造函数 */ +TEE_Result TA_CreateEntryPoint(void) +{ + TEE_Result ret; + tlogd("----- TA entry point ----- "); + tlogd("TA version: %s", TA_TEMPLATE_VERSION); + ret = AddCaller_CA_exec("/vendor/bin/teec_hello", 0); + if (ret == TEE_SUCCESS) { + tlogd("TA entry point: add ca whitelist success"); + } else { + tloge("TA entry point: add ca whitelist failed"); + return TEE_ERROR_GENERIC; + } + return TEE_SUCCESS; +} +/* CA打开与TA会话时,框架会调用此接口 */ +TEE_Result TA_OpenSessionEntryPoint(uint32_t parm_type, + TEE_Param params[PARAM_COUNT], void** session_context) +{ + (void)parm_type; + (void)params; + (void)session_context; + tlogd("---- TA open session -------- "); + return TEE_SUCCESS; +} +/* TEE侧针对CA侧命令字响应函数 */ +TEE_Result TA_InvokeCommandEntryPoint(void* session_context, uint32_t cmd, + uint32_t parm_type, TEE_Param params[PARAM_COUNT]) +{ + TEE_Result ret; + (void)session_context; + tlogd("---- TA invoke command ----------- "); + switch (cmd) { + case CMD_GET_TA_VERSION: + if (!check_param_type(parm_type, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_MEMREF_OUTPUT)) { + tloge("Bad expected parameter types"); + return TEE_ERROR_BAD_PARAMETERS; + } + if (params[OUT_BUFFER_INDEX].memref.buffer == NULL || + params[OUT_BUFFER_INDEX].memref.size == 0) { + tloge("InvokeCommand with bad, cmd is %u", cmd); + return TEE_ERROR_BAD_PARAMETERS; + } + ret = get_ta_version(params[OUT_BUFFER_INDEX].memref.buffer, ¶ms[OUT_BUFFER_INDEX].memref.size); + if (ret != TEE_SUCCESS) { + tloge("InvokeCommand Failed 0x%x. cmd is %u", ret, cmd); + return ret; + } + break; + default: + tloge("Unknown cmd is %u", cmd); + ret = TEE_ERROR_BAD_PARAMETERS; + } + return ret; +} +/* CA关闭与TA会话时,TEE侧关闭会话 */ +void TA_CloseSessionEntryPoint(void* session_context) +{ + (void)session_context; + tlogd("---- close session ----- "); +} +/* TA析构函数 */ +void TA_DestroyEntryPoint(void) +{ + tlogd("---- destroy TA ---- "); +} +``` diff --git a/zh-cn/application-dev/tee/feature-development-guideline.md b/zh-cn/application-dev/tee/feature-development-guideline.md new file mode 100644 index 00000000000..4f35c692189 --- /dev/null +++ b/zh-cn/application-dev/tee/feature-development-guideline.md @@ -0,0 +1,814 @@ +# CA/TA特性开发指导 + +- [CA鉴权](#CA鉴权) +- [主动防护度量](#主动防护度量) +- [TA多线程支持](#TA多线程支持) +- [SE(安全器件)](#SE(安全器件)) +- [安全存储](#安全存储) +- [独立服务开发](#独立服务开发) + +## CA鉴权 +由于CA位于REE侧,TA位于TEE侧,因此CA是不可信的。为了防止恶意CA攻击安全OS,需要对访问安全OS的CA进行一系列认证,以识别合法CA,拦截恶意CA,这个过程叫CA鉴权。 + +### TEE hash鉴权 +**添加鉴权方法** +1. 配置ca鉴权文件 +− vendor侧CA:vendor/huaweiplatform/itrustee/itrustee_build_tools/ca_signtools/data/vendor_native_ca.xml +− system侧CA:vendor/huaweiplatform/itrustee/itrustee_build_tools/ca_signtools/data/system_native_ca.xml +2. 准备待签名文件 +− native_packages.xml:从手机上拉取对应的native_packages.xml文件并放到指定工程目录 +* vendor侧CA:/vendor/etc/native_packages.xml -----> out/vendor/etc/ +* system侧CA:/system/etc/native_packages.xml -----> out/system/etc/ +− ca二进制:放到对应目录下,确保与xxx_native_ca.xml文件中path路径配置保证一致 +* vendor侧CA: out/vendor/bin/ +* system侧CA: out/system/bin/ +3. 执行签名 +− vendor侧CA:./vendor/huaweiplatform/itrustee/itrustee_build_tools/ca_signtools/transXmlToBin.py vendor out/ tools/signcenter/NativeCASign.jar +− system侧CA:./vendor/huaweiplatform/itrustee/itrustee_build_tools/ca_signtools/transXmlToBin.py system out/ tools/signcenter/NativeCASign.jar +4. 替换native_packages.xml到手机,并重启: +− vendor侧CA:out/vendor/etc/native_packages.xml——》/vendor/etc/native_packages.xml +− system侧CA:out/system/etc/native_packages.xml——》/system/etc/native_packages.xml + +### TA caller鉴权 +添加CA鉴权方法:在TA源码中的TA_CreateEntryPoint接口内,调用AddCaller_CA接口来添加caller权限。 +AddCaller_CA +TEE_Result AddCaller_CA(const uint8_t *cainfo_hash, uint32_t length); +1. 功能: +在TA里添加CA鉴权白名单。 +2. 参数: +− cainfo_hash:CA info hash值; +− length:CA info hash字节长度,取值需为SHA256_DIGEST_LENGTH(32U); +3. 返回值:TEE_SUCCESS为添加成功,其他值为异常。 +4. CA info hash计算方法: +− 工具路径:itrustee_sdk/tools/ca_auth_hash_tools +− 配置需要计算ca info信息:在ca_caller_info.xml文件里配置添加需要计算的ca info信息,示例如下: +```xml + + + + + + +``` +− 执行脚本:python3 calc_ca_caller_hash.py + +5. Native CA TA_caller鉴权示例: +```c +#define SHA256_DIGEST_LENGTH 32 +static uint8_t g_caller_hash[][SHA256_DIGEST_LENGTH] = { + /* cmdine = "/vendor/bin/teec_hello", uid = 0 */ + { 0xca, 0x9f, 0x5e, 0xd7, 0x6d, 0x07, 0x0d, 0x66, 0xe7, 0xb2, 0xab, 0xb3, 0x55, 0xfc, 0xb0, 0xbf, + 0xc8, 0x16, 0x52, 0x37, 0x5f, 0xfe, 0x99, 0xfc, 0x34, 0x43, 0xf6, 0x5f, 0x0c, 0x70, 0x44, 0x48 }, +}; + +TEE_Result TA_CreateEntryPoint(void) +{ + TEE_Result ret; + for (uint32_t i = 0; i < sizeof(g_caller_hash) / SHA256_DIGEST_LENGTH; i++) { + ret = AddCaller_CA(g_caller_hash[i], SHA256_DIGEST_LENGTH); + if (ret != 0) + return ret; + } + return TEE_SUCCESS; +} +``` + + + + +## 主动防护度量 +### 概述 +主动防护度量是通过软硬件独立运行的MSPC独立核安全系统实时度量保护关键系统组件的运行环境,通过按需度量和周期度量关键资源(只读代码段、rodata)来检测和防御非法入侵: +* 安全OS主动防护分层度量框架:由MSPC对TEEOS关键模块(kernel、sysmgr、filemgr的代码段和rodata)做度量,由安全OS主动防护服务apm service对关键业务TA(TA的代码段和rodata)做运行时度量,保证业务TA运行时安全可信。 +* TA主动防护度量流程:开启主动防护度量的TA在首次加载时apm service会建立该TA的度量基线,TA invoke运行期间apm service会启动度量,度量异常时按对应TA的威胁处理策略做异常处理。 + +### 使用说明 +1. TA使能主动防护度量配置方式 +在TA的manifest.txt文件新增配置gpd.ta.apm_level字段,字段取值说明如下表。 + +| 字段 | 描述 | +|--------------------------|----------------------------------------------------------------------------------------------| +| `gpd.ta.apm_level: 0` | TA关闭主动防护度量。 | +| `gpd.ta.apm_level: 1` | TA开启主动防护度量,度量异常时威胁处理策略为弱策略,弱策略为只打印异常日志不修改TA invoke返回值。 | +| `gpd.ta.apm_level: 2` | CPRO 车机平台:TA开启主动防护度量,度量异常时威胁处理策略为强策略,强策略为打印异常日志且修改TA invoke返回值为`TEE_ERROR_APM`错误码;
CLT 121及后续平台:同 `gpd.ta.apm_level: 1` 功能一致。 | +| `gpd.ta.apm_level: xxx` | 配置为其他值时属于无效值,TA运行会报错。 | +| 无 `gpd.ta.apm_level` 字段 | 默认TA关闭主动防护度量。 | + + +2. 主动防护度量失败错误码 +TEE_ERROR_APM = 0xFFFF9115 +### 限制与约束 +* 平台约束 +该功能仅向Harmony OS Next版本华为内部开发者开放。 +* 使用限制 + + a. 开启主动防护度量的TA数量上限为8个,新增开启主动防护度量的TA需要通知itrustee; + + b. 只支持通过ROM升级的TA使能主动防护度量,不支持单独升级的TA使能主动防护度量(不支持更新TA度量基线)。 + +查询度量报告使用指导: +TEE提供接口支持查询TEEOS对TA的度量报告。 +TEE提供接口支持TA查询MPSC对所有子系统(TEEOS\BL2等)度量报告。 + +权限配置: +查询度量报告接口权限配置方式:TA需要在manifest.txt中增加如下配置,未配置权限时TA查询度量报告会报TEE_MEASURE_ERROR_PERMISSION_DENY(0x00000005)错误码。 +gpd.ta.apm_perm: true + +查询TA度量报告: +开发者可通过调用tee_query_ta_measure_report()查询TA调度报告。 + +查询MSPC度量报告: +开发者可通过调用tee_query_mspc_measure_report()查询MSPC度量报告。 + +## TA多线程支持 + +### 标准Posix接口 +| 序号 | 接口名称 | +|------|-----------------------------| +| 1 | pthread_create | +| 2 | pthread_join | +| 3 | pthread_attr_init | +| 4 | pthread_attr_setstack | +| 5 | pthread_attr_setstacksize | +| 6 | pthread_cond_broadcast | +| 7 | pthread_cond_destroy | +| 8 | pthread_cond_init | +| 9 | pthread_cond_signal | +| 10 | pthread_cond_wait | +| 11 | pthread_mutexattr_destroy | +| 12 | pthread_mutexattr_init | +| 13 | pthread_mutexattr_settype | +| 14 | pthread_mutex_init | +| 15 | pthread_mutex_lock | +| 16 | pthread_mutex_trylock | +| 17 | pthread_mutex_unlock | +| 18 | pthread_mutex_destroy | +| 19 | pthread_key_create | +| 20 | pthread_key_delete | +| 21 | pthread_getspecific | +| 22 | pthread_setspecific | +| 23 | pthread_equal | +| 24 | pthread_once | +| 25 | pthread_self | + +### 扩展接口 +**pthread_attr_settee**
+```int pthread_attr_settee(pthread_attr_t *a, int ca, int task_id, int shadow);``` +1. 功能: +设置线程并行属性。 +2. 参数: + +| 方向 | 类型 | 名称 | 说明 | +|-------|----------------|----------|--------------------------------------------------------------| +| in | int | ca | 默认为 `TEESMP_THREAD_ATTR_CA_INHERIT` | +| in | int | task_id | 默认为 `TEESMP_THREAD_ATTR_TASK_ID_INHERIT` | +| in | int | shadow | 默认为 `TEESMP_THREAD_ATTR_HAS_SHADOW`,可为该线程分配一个新核 | +| out | pthread_attr_t*| a | 线程属性 | +| return| int | - | `EINVAL`:操作失败,a == NULL
`0`:成功 | + +注:在TA调用pthread_create创建线程,建议调用pthread_attr_settee设置影子线程属性,让TEE拉入一个新的核,并行跑创建的线程,用法见1.3.8.2 TA多线程限制。 + +### TA多线程限制 +1. 在TA中创建的thread不能使用所有的agent服务以及TEE_Wait。其中hm native和posix兼容的是可以使用的,但sleep例外(因为其内部是用TEE_wait实现的)。 +2. 创建线程时,只有添加TEESMP_THREAD_ATTR_HAS_SHADOW属性,才会为本线程拉入一个新的核,提供新的计算能力,否则容易出现hang的现象。代码如下: +```c +#define TEESMP_THREAD_ATTR_CA_INHERIT (-1U) +#define TEESMP_THREAD_ATTR_TASK_ID_INHERIT (-1U) +#define TEESMP_THREAD_ATTR_HAS_SHADOW 0x1 +pthread_attr_init(&attr); +pthread_attr_settee(&attr,TEESMP_THREAD_ATTR_CA_INHERIT, TEESMP_THREAD_ATTR_TASK_ID_INHERIT, TEESMP_THREAD_ATTR_HAS_SHADOW); +pthread_create(&thread, &attr, thread_func, i) +``` +### TA shadow线程优先级设置 +int spi_notify_set_shadow_priority(uint32_t priority_ree); +1. 功能:支持TA调用该接口设置shadow线程优先级 +2. 参数: +priority_ree:新的优先级(这个优先级是REE侧的,而非TEE侧的),取值范围20到41,20为normal,41为vip,数值越大优先级越高。 +3. 返回值: +成功返回0 +此接口只用于设置shadow线程优先级,TA如果调用此接口设置非shadow线程优先级,则报错(返回TEE_ERROR_ACCESS_DENIED) +priority_ree入参不在[20,41]范围内,则报错(返回TEE_ERROR_BAD_PARAMETERS ) +其他错误返回-1 +4. 接口所在头文件:tee_notify_set_priority.h +5. 参考代码 +```c +pthread_create(&thread, &attr, thread_func, NULL); +void *thread_func(void *arg) +{ + uint32_t priority_ree = 41u; + int ret = spi_notify_set_shadow_priority(priority_ree); + tlogi("call spi_notify_set_shadow_priority, ret=%d\n", ret); +} +``` + +## SE(安全器件) + +安全器件(SE)连接在TEE上,对于连接到TEE的SE的访问,只能通过TA来进行,由于不通过REE,因此这种TA与SE的通信被认为是可信的。 +### 限制与约束 +目前Harmony OS Next版本支持该特性,具体硬件平台支持情况可咨询iTrustee华为工程师。 +### 开发步骤 +SE API接口主要分为以下四个层级。 +| 类名(Class) | 描述 | +|---------------------|----------------------------------------------------------------------| +| SEService class | SE API 的总入口,要访问 SE,必须首先获取 SEService 的访问权限。 | +| SEReader class | 在 SEService 下获取的 reader handle,通过它才能建立 SE 的连接。 | +| SESession class | 获取 SE 的 ATR,并且打开 SE 通道。 | +| SEChannel class | 通过 channel 向 SE 发送 APDU。 | + + +一个安全器件访问主要开发步骤如下: +1. 首先通过TEE_SEServiceOpen获取一个访问安全元件的Service Handle; +2. 通过TEE_SEServiceGetReaders获取当前安全元件的可用Reader; +3. 通过TEE_SESessionOpenLogicalChannel打开对应session指向的下一个逻辑通道并调用TEE_SEChannelTransmit发送消息; +4. 消息发送成功后,通过TEE_SEChannelClose关闭channel指向的通道; +5. 调用TEE_SESessionCloseChannels关闭session指向连接的所有通道,并通过TEE_SESessionClose关闭session指向的连接; +6. 通过TEE_SEReaderCloseSessions关闭reader所指向的安全元器件建立的所有连接; +7. 最后通过TEE_SEServiceClose关闭已打开的访问安全元件的service。 + +以下为示例代码: +```c +#include "tee_internal_se_api.h" +#include "tee_log.h" +#include +int use_which_se = 0; +int SE_test_demo(void) +{ + TEE_SEServiceHandle service = NULL; + TEE_SEReaderHandle reader[6] = {0}; + uint32_t reader_len = 6; + TEE_SESessionHandle session; + TEE_SEChannelHandle channel; + /* 获取一个访问安全元件的Service Handle */ + if (TEE_SEServiceOpen(&service)) { + tloge("open se service failed\n"); + return -1; + } + /* 获取当前安全元件的可用的reader */ + if (TEE_SEServiceGetReaders(service, reader, &reader_len) != TEE_SUCCESS) + goto clean; + /* 与reader指向的安全元件建立一个连接 */ + if (TEE_SEReaderOpenSession(reader[use_which_se], &session)) { + tloge("open session failed\n"); + goto clean; + } + uint8_t ppse_id[] = {0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31}; + uint8_t crs_id[] = {0xA0, 0x00, 0x00, 0x01, 0x51, 0x43, 0x52, 0x53, 0x00}; + uint8_t ara_m_id[] = {0xA0, 0x00, 0x00, 0x01, 0x51, 0x41, 0x43, 0x4C, 0x00}; + uint8_t main_s_id[] = {0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00}; + TEE_SEAID aids[] = { + {ppse_id, sizeof(ppse_id)}, + {crs_id, sizeof(crs_id)}, + {ara_m_id, sizeof(ara_m_id)}, + {main_s_id, sizeof(main_s_id)} + }; + unsigned char hex_cmd[] = {0x00, 0xA4, 0x04, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00}; + char rsp[256] = {0}; + unsigned int rsp_len = 256; + char cmd_suc[20] = {0x6F, 0x10, 0x84, 0x08, 0xA0, 0x00, 0x00, 0x01, 0x51, 0x00, 0x00, 0x00, 0xA5, 0x04, + 0x9F, 0x65, 0x01, 0xFF, 0x90, 0x00}; + int i; + for (i = 0; i < 4; i++) { + /* 打开session指向的连接下的一个逻辑通道 */ + if (!TEE_SESessionOpenLogicalChannel(session, &aids[i], &channel)) { + tlogd("open logical channel %d success\n", i); + goto transmit; + } + tloge("open logical channel %d failed\n", i); + } + goto clean; +transmit: + /* 通过channel指向的通道发送命令 */ + if (TEE_SEChannelTransmit(channel, hex_cmd, sizeof(hex_cmd), rsp, &rsp_len)) { + tloge("send test cmd to logical channel failed\n"); + goto clean; + } + if (memcmp(rsp, cmd_suc, sizeof(cmd_suc))) { + tloge("test cmd receive wrong rsp\n"); + goto clean; + } + /* 关闭channel指向的通道 */ + TEE_SEChannelClose(channel); + tlogd("close channel in session\n"); + /* 关闭session指向的连接下的所有通道 */ + TEE_SESessionCloseChannels(session); + tlogd("close channels in session\n"); + /* 关闭session指向的连接 */ + TEE_SESessionClose(session); + + /* 关闭reader指向的安全元件建立的所有连接 */ + if (reader[use_which_se]) + TEE_SEReaderCloseSessions(reader[use_which_se]); + /* 关闭已经打开的访问安全元件的service */ + if (service) + TEE_SEServiceClose(service); + tloge("%s --- \n", __FUNCTION__); + return TEE_SUCCESS; +clean: + if (reader[use_which_se]) + TEE_SEReaderCloseSessions(reader[use_which_se]); + if (service) + TEE_SEServiceClose(service); + return -1; +} +``` + + + +## 安全存储 +安全存储可以用于加密保存以下两种形式的数据: +1. 恢复出厂设置时不会被删除的数据,该类数据一般保存在persistent分区(/sec_storage),该分区需要开发者自行适配; +2. TA运行时生成,关机后还需要保存、但恢复出厂设置会被清除的数据,通常存储在data分区(/data/service/el1/public/sec_storage_data)。 +在指定storageID为TEE_OBJECT_STORAGE_CE时,上述两类数据也可以保存在CE类路径(data/vendor_ce)下。 +对于利用安全存储保存的数据,都需要开发具体的TA才能实现数据的保存。对于数据的来源视方案设计而定,可以是由REE的CA传入给TA,再由TA调用安全存储接口保存;也可以是TA内部生成再保存。对于需要在产线上写入的数据,同样也需要有TA配合单板软件和装备工具一起实现相应功能。 + +### 存储密钥样例 +**实例场景** +* 为特定密钥申请内存空间 +* 使用随机数生成函数来生成密钥信息对称加解密 +* 创建永久Object用于存储密钥 + +**实例步骤** +1. 为AES密钥分配存储空间,AES密钥最长长度为32字节; +2. 调用随机数生成函数生成密钥信息,长度为32字节的随机数组; +3. 将密钥信息存入临时内存空间中,用于密钥存取的验证; +4. 创建永久Object,并将密钥存入物理介质中; +5. 打开存储密钥的Object; +6. 将读取的密钥与之前已存储的密钥进行比较,如果相同则证明存储成功; +7. 关闭并删除存储AES密钥的Object。 + +**实例代码** +```c +#include +#include "tee_defines.h" +#include "tee_log.h" +#include "tee_object_api.h" +#include "tee_trusted_storage_api.h" +#include "tee_mem_mgmt_api.h" +#include "tee_crypto_api.h" +TEE_Result store_key_sample(void) +{ + uint32_t storageID = TEE_OBJECT_STORAGE_PRIVATE; + TEE_ObjectHandle transient_key = NULL; + TEE_ObjectHandle persistent_key = NULL; + TEE_Result ret; + uint32_t w_flags = TEE_DATA_FLAG_ACCESS_WRITE; + uint32_t r_flags = TEE_DATA_FLAG_ACCESS_READ; + void *objectID = "store_key_sample.txt"; + void *aes_key = NULL; + tlogd("store_key_sample start:============\n"); + /* 为密钥分配存储空间 */ + ret = TEE_AllocateTransientObject(TEE_TYPE_AES, 0x00000800, (&transient_key)); + if (ret != TEE_SUCCESS) { + tloge("Failed to execute TEE_AllocateTransientObject:ret = 0x%x\n", ret); + return ret; + } + TEE_GenerateRandom(transient_key->Attribute->content.ref.buffer, transient_key->Attribute->content.ref.length); + /* 将密钥信息存入临时内存空间中 */ + aes_key = TEE_Malloc(transient_key->Attribute->content.ref.length, 0); + if (aes_key == NULL) { + tloge("malloc fail, len:%u", transient_key->Attribute->content.ref.length); + TEE_FreeTransientObject(transient_key); + return TEE_ERROR_OUT_OF_MEMORY; + } + int32_t rc = memmove_s(aes_key, transient_key->Attribute->content.ref.length, + transient_key->Attribute->content.ref.buffer, + transient_key->Attribute->content.ref.length); + if (rc != 0) { + tloge("memmove fail"); + TEE_Free(aes_key); + TEE_FreeTransientObject(transient_key); + return TEE_ERROR_SECURITY; + } + /* 创建永久Object:密钥存储Object persistent_key */ + ret = TEE_CreatePersistentObject(storageID, objectID, strlen(objectID), w_flags, transient_key, NULL, 0, + (&persistent_key)); + if (ret != TEE_SUCCESS) { + tloge("Failed to create object:ret = 0x%x\n", ret); + TEE_Free(aes_key); + TEE_FreeTransientObject(transient_key); + return ret; + } + TEE_CloseObject(persistent_key); + TEE_FreeTransientObject(transient_key); + /* 打开前面创建的存储密钥的Object */ + ret = TEE_OpenPersistentObject(storageID, objectID, strlen(objectID), + r_flags | TEE_DATA_FLAG_ACCESS_WRITE_META, (&persistent_key)); + if (ret != TEE_SUCCESS) { + tloge("Failed to execute TEE_OpenPersistentObject:ret = %x\n", ret); + TEE_Free(aes_key); + return ret; + } + /* 判断读入的密钥是否正确 */ + if (TEE_MemCompare(aes_key, persistent_key->Attribute->content.ref.buffer, + persistent_key->Attribute->content.ref.length)) { + tloge("The persistent_key stored is broken.\n"); + TEE_Free(aes_key); + return TEE_FAIL; + } + /* 删除存储AES密钥的永久Object */ + TEE_CloseAndDeletePersistentObject1(persistent_key); + TEE_Free(aes_key); + return TEE_SUCCESS; +} +``` + +**用到的资源** + +使用安全存储相关接口以及内存申请相关接口时需用到如下头文件:tee_mem_mgmt_api.h、tee_trusted_storage_api.h、tee_crypto_api.h、tee_internal_api.h。 +存储密钥的类型仅支持GlobalPlatform所列类型,如下表。不支持的密钥可做为普通数据存储(存储方法见下节存储文件样例)。 + +| Object Type | Possible Object Sizes | +|---------------------------|----------------------------------------------------------------------------------------| +| TEE_TYPE_AES | 128, 192, or 256 bits | +| TEE_TYPE_DES | Always 56 bits | +| TEE_TYPE_DES3 | 112 or 168 bits | +| TEE_TYPE_HMAC_MD5 | Between 64 and 512 bits, multiple of 8 bits | +| TEE_TYPE_HMAC_SHA1 | Between 80 and 512 bits, multiple of 8 bits | +| TEE_TYPE_HMAC_SHA224 | Between 112 and 512 bits, multiple of 8 bits | +| TEE_TYPE_HMAC_SHA256 | Between 192 and 1024 bits, multiple of 8 bits | +| TEE_TYPE_HMAC_SHA384 | Between 256 and 1024 bits, multiple of 8 bits | +| TEE_TYPE_HMAC_SHA512 | Between 256 and 1024 bits, multiple of 8 bits | +| TEE_TYPE_RSA_PUBLIC_KEY | Object size is the number of bits in the modulus. All key sizes up to 2048 bits must be supported. Support for bigger key sizes is implementation-dependent. Minimum key size is 256 bits. | +| TEE_TYPE_RSA_KEYPAIR | Same as for RSA public key size | +| TEE_TYPE_DSA_PUBLIC_KEY | Between 512 and 1024 bits, multiple of 64 bits | +| TEE_TYPE_DSA_KEYPAIR | | +| TEE_TYPE_DH_KEYPAIR | From 256 to 2048 bits | +| TEE_TYPE_GENERIC_SECRET | Multiple of 8 bits, up to 4096 bits. This type is intended for secret data that is not directly used as a key in a cryptographic operation, but participates in a key derivation. | + + +### 存储文件样例 +**实例场景** +* 创建永久Object,为永久Object分配内存空间。 +* 将数据存入永久Object中,并写入物理介质。 +* 打开永久Object,将写入的数据读出,判断是否写入成功。 + +**实例步骤** +1. 通过TEE_CreatePersistentObject创建永久Object,申请内存空间,并通过TEE_WriteObjectData将数据写入永久Object,即可写入物理介质中; +2. 通过TEE_OpenPersistentObject打开已经创建的文件,为打开的永久Object分配内存空间; +3. 通过TEE_ReadObjectData读取存储的数据,并与之前写入的进行对比; +4. 如果相同,则证明写入成功; +5. 通过TEE_CloseObject关闭并删除已存储的数据文件。 + +**实例代码** + +```c +#include +#include "tee_defines.h" +#include "tee_log.h" +#include "tee_object_api.h" +#include "tee_trusted_storage_api.h" +#include "tee_mem_mgmt_api.h" +TEE_Result store_data_sample(void) +{ + uint32_t storageID = TEE_OBJECT_STORAGE_PRIVATE; + uint32_t r_flags = TEE_DATA_FLAG_ACCESS_READ; + uint32_t w_flags = TEE_DATA_FLAG_ACCESS_WRITE; + void *create_objectID = "store_data_sample.txt"; + TEE_ObjectHandle persistent_data = NULL; + TEE_Result ret; + char *write_buffer = "It is a test for persist object!"; + uint32_t pos = 0; + uint32_t len = 0; + char *read_buffer = NULL; + uint32_t count = 0; + tlogd("store_data_sample start:============\n"); + /* 创建永久Object: flag为写操作(会将initialData和objectinfo和attributes写入物理介质) */ + ret = TEE_CreatePersistentObject(storageID, create_objectID, strlen(create_objectID), w_flags, + TEE_HANDLE_NULL, NULL, 0, (&persistent_data)); + if (ret != TEE_SUCCESS) { + tloge("Failed to create file: ret = 0x%x\n", ret); + return ret; + } + ret = TEE_WriteObjectData(persistent_data, write_buffer, strlen(write_buffer)); + if (ret != TEE_SUCCESS) { + tloge("Failed to write file: ret = 0x%x\n", ret); + /* 打开或创建文件之后异常分支需要关闭文件,否则会产生内存泄漏 */ + TEE_CloseObject(persistent_data); + return ret; + } + TEE_CloseObject(persistent_data); + /* 打开已经创建的文件,进行数据的读取 */ + ret = TEE_OpenPersistentObject(storageID, create_objectID, strlen(create_objectID), + r_flags, (&persistent_data)); + if (ret != TEE_SUCCESS) { + tloge("Failed to open file:ret = 0x%x\n", ret); + return ret; + } + ret = TEE_InfoObjectData(persistent_data, &pos, &len); + if (ret != TEE_SUCCESS) { + tloge("Failed to open file:ret = 0x%x\n", ret); + TEE_CloseObject(persistent_data); + return ret; + } + read_buffer = TEE_Malloc(len + 1, 0); + if (read_buffer == NULL) { + tloge("Failed to open file:ret = 0x%x\n", ret); + TEE_CloseObject(persistent_data); + return ret; + } + /* 读取已存入的数据 */ + ret = TEE_ReadObjectData(persistent_data, read_buffer, len, &count); + if (ret != TEE_SUCCESS) { + TEE_CloseObject(persistent_data); + TEE_Free(read_buffer); + return ret; + } + if (TEE_MemCompare(write_buffer, read_buffer, strlen(write_buffer)) != 0) { + TEE_CloseObject(persistent_data); + TEE_Free(read_buffer); + return TEE_FAIL; + } + /* 关闭永久 object */ + TEE_CloseObject(persistent_data); + TEE_Free(read_buffer); + return TEE_SUCCESS; +} +``` + +## 独立服务开发 +### 概述 +服务开发分为两个部分: +* client:端提供接口给TA,TA可以通过该部分接口和服务进行通信。 +* server:服务的主体,会等待和处理TA的消息完成相应的功能处理。 + +client、server开发所依赖的编译工具链,签名工具等与TA保持一致。 + + +### 开发步骤 + +1. manifest.txt文件配置 + +a. client的manifest文件: +* gpd.ta.appID: e3d37f4a-f24c-48d0-8884-000000000000 +* gpd.ta.service_name: client_demo +* gpd.ta.singleInstance: true +* gpd.ta.multiSession: false +* gpd.ta.instanceKeepAlive: false +* gpd.ta.dataSize: 81920 +* gpd.ta.stackSize: 8192 +* gpd.ta.target_type: 4 + +在编译client时,gpd.ta.service_name,gpd.ta.target_type必须为有效值,gpd.ta.appID填写对应service的uuid, 其他字段不可以缺省,但是填充值不关心。 + +参数要求: +* gpd.ta.service_name长度必须小于32字节,并且名字定义需要保证唯一性。 +* client对应的target type为4。 + +b. server的manifest文件: +* gpd.ta.appID: e3d37f4a-f24c-48d0-8884-3bdd6c44e988 +* gpd.ta.service_name: task_demo +* gpd.ta.singleInstance: true +* gpd.ta.multiSession: false +* gpd.ta.instanceKeepAlive: true +* gpd.ta.dataSize: 81920 +* gpd.ta.stackSize: 8192 +* gpd.ta.target_type: 3 +* gpd.srv.is_need_release_ta_res: false +* gpd.srv.crash_callback: false +* gpd.srv.is_need_create_msg: false +* gpd.srv.is_need_release_msg: false + +server的manifest中通用字段的描述与TA保持一致。 + +针对服务的参数要求: +* gpd.ta.instanceKeepAlive目前仅支持为true。 +* server对应的target type为3。 +* gpd.ta.service_name长度不超过32字节。 +* gpd.srv.is_need_release_ta_res 该字段置为true时,在ta session退出时,gtask会给服务发送一个消息,服务中添加该消息对应的处理函数即可触发调用。该消息的处理函数需要服务自己添加,服务开发者可以按需实现。该场景不需要触发操作则置为false。 +* gpd.srv.crash_callback 该字段置为true时,在服务crash之后会被重新拉起。置为false则不会被重新拉起。需要注意,TEE中存在审计机制,服务crash 3次之后将无法被拉起。 +* gpd.srv.is_need_create_msg 该字段置为true时,在ta加载时,gtask会给服务发送一个消息,服务中添加该消息对应的处理函数即可触发调用。该消息的处理函数需要服务自己添加,服务开发者可以按需实现。该场景不需要触发操作则置为false。 +* gpd.srv.is_need_release_msg 该字段置为true时,在ta退出时,gtask会给服务发送一个消息,服务中添加该消息对应的处理函数即可触发调用。该消息的处理函数需要服务自己添加,服务开发者可以按需实现。该场景不需要触发操作则置为false。 +2. 权限配置 +− 映射TA内存权限 +在client使用新的共享内存申请接口,接口需要指定uuid。TA调用该接口申请共享内存,指定uuid的服务可以直接映射,不需要再单独配置相关权限。 +新的共享内存申请接口如下: + +void *tee_alloc_sharemem_aux(const struct tee_uuid *uuid, uint32_t size) ,定义在tee_sharemem_ops.h。 + +− 获取ta uuid权限 +仍在代码中静态配置,AC_DEFINE_SUBJ_BEG(get_uuid)中增加task的uuid。 + +− TA权限控制 +服务中自己维护白名单控制TA的访问权限 + +3. 独立服务client端开发 +```c +#include "securec.h" +#include "string.h" +#include "tee_defines.h" +#include "tee_service_public.h" +#include "tee_log.h" +#include "tee_sharemem_ops.h" +#define DEMO_TASK_NAME "task_demo" +#define DEMO_UUID \ + { \ + 0xe3d37f4a, 0xf24c, 0x48d0, \ + { \ + 0x88, 0x84, 0x3b, 0xdd, 0x6c, 0x44, 0xe9, 0x88 \ + } \ + } +#define MSG_LEN 10 +enum srv_cmd { + SRV_CMD_DEMO_0 = 0x0, + SRV_CMD_DEMO_1 = 0x1, +}; +TEE_Result tee_srv_demo() +{ + tee_service_ipc_msg msg = {{0}}; + tee_service_ipc_msg_rsp rsp = {0}; + TEE_Result ret; + uint8_t *buffer_local = NULL; + const char *temp = "my_demo"; + TEE_UUID srv_uuid = DEMO_UUID; + buffer_local = tee_alloc_sharemem_aux(&srv_uuid, MSG_LEN); + if (buffer_local == NULL) { + tloge("buffer_local is null"); + return TEE_ERROR_OUT_OF_MEMORY; + } + int rc = memcpy_s(buffer_local, MSG_LEN, temp, strlen(temp)); + if (rc != 0) { + tee_free_sharemem(buffer_local, MSG_LEN); + return TEE_ERROR_SECURITY; + } + rsp.ret = TEE_FAIL; + msg.args_data.arg0 = (uint64_t)(uintptr_t)(buffer_local); + msg.args_data.arg1 = MSG_LEN; + msg.args_data.arg3 = 0x123456; + tee_common_ipc_proc_cmd(DEMO_TASK_NAME, SRV_CMD_DEMO_0, &msg, SRV_CMD_DEMO_0, &rsp); + ret = rsp.ret; + if (ret == TEE_SUCCESS) { + tloge("tee_srv_demo done success!"); + } else { + tloge("tee_srv_demo done fail!"); + } + tee_free_sharemem(buffer_local, MSG_LEN); + return ret; +} +TEE_Result tee_srv_iomap_test() +{ + tee_service_ipc_msg msg = {{0}}; + tee_service_ipc_msg_rsp rsp = {0}; + TEE_Result ret; + rsp.ret = TEE_FAIL; + tee_common_ipc_proc_cmd(DEMO_TASK_NAME, SRV_CMD_DEMO_1, &msg, SRV_CMD_DEMO_1, &rsp); + ret = rsp.ret; + if (ret == TEE_SUCCESS) { + tloge("tee_srv_iomap_test done success!"); + } else { + tloge("tee_srv_iomap_test done fail!"); + } + return ret; +} +``` +4. 独立服务service端开发 +```c +#include "securec.h" +#include "tee_defines.h" +#include "tee_ext_api.h" +#include "tee_log.h" +#include "tee_dynamic_srv.h" +#include "tee_drv_client.h" +static int64_t drv_iomap_test(void) +{ + const char *drv_name = "drv_test_module"; + uint32_t args = (uint32_t)(uintptr_t)(&drv_name); + tloge("%s iomap begin args:0x%x\n", drv_name, args); + int64_t fd = tee_drv_open(drv_name, &args, sizeof(args)); + if (fd <= 0) { + tloge("open fd failed\n"); + return -1; + } + tloge("iomap test ioctl begin args:0x%x fd:%d\n", args, (int32_t)fd); + int64_t ret = tee_drv_ioctl(fd, 0x1a, &args, sizeof(args)); + if (ret != 0) + tloge("iomap test ioctl fd:%d failed\n", (int32_t)fd); + ret |= tee_drv_close(fd); + if (ret != 0) + tloge("===================== iomap test DRV_FAIL ==================\n"); + else + tloge("===================== iomap test DRV_SUCC ==================\n"); + return ret; +} +#define MSG_LEN 10 +static void demo_fn(tee_service_ipc_msg *msg, uint32_t sndr, tee_service_ipc_msg_rsp *rsp) +{ + const char *result = "my_demo"; + char temp[MSG_LEN] = {0}; + if (rsp == NULL) + return; + if (msg == NULL) { + rsp->ret = TEE_ERROR_BAD_PARAMETERS; + return; + } + tloge("!!!!!!!!demo_fn enter!!!!!!!"); + uint32_t vaddr = 0; + uint32_t size = msg->args_data.arg1; + int ret = tee_srv_map_from_task(sndr, msg->args_data.arg0, size, &vaddr); + if (ret != 0) { + rsp->ret = TEE_ERROR_GENERIC; + return; + } + int32_t rc = memcpy_s(temp, sizeof(temp), (uint8_t *)(uintptr_t)vaddr, size); + if (rc != 0) { + tloge("copy fail"); + tee_srv_unmap_from_task(vaddr, size); + rsp->ret = TEE_ERROR_SECURITY; + return; + } + if (memcmp((const char *)temp, result, strlen(result)) != 0) { + tloge("compare fail!"); + tee_srv_unmap_from_task(vaddr, size); + rsp->ret = TEE_ERROR_GENERIC; + return; + } + TEE_UUID uuid; + rsp->ret = tee_srv_get_uuid_by_sender(sndr, &uuid); + if (ret != 0) { + tloge("get uuid fail!"); + tee_srv_unmap_from_task(vaddr, size); + return; + } + tee_srv_unmap_from_task(vaddr, size); + rsp->ret = TEE_SUCCESS; + return; +} +static void demo_io_map_test(tee_service_ipc_msg *msg, uint32_t sndr, tee_service_ipc_msg_rsp *rsp) +{ + (void)sndr; + if (rsp == NULL) + return; + if (msg == NULL) { + rsp->ret = TEE_ERROR_BAD_PARAMETERS; + return; + } + tloge("!!!!!!!!demo_io_map_test enter!!!!!!!"); + int64_t rc = drv_iomap_test(); + if (rc != 0) { + rsp->ret = TEE_ERROR_GENERIC; + tloge("drv_iomap_test fail!"); + return; + } + rsp->ret = TEE_SUCCESS; + return; +} +enum srv_cmd { + SRV_CMD_DEMO_0 = 0x0, + SRV_CMD_DEMO_1 = 0x1, +}; +struct srv_dispatch_t dispatch_fns[] = { + {SRV_CMD_DEMO_0, demo_fn}, + {SRV_CMD_DEMO_1, demo_io_map_test}, +}; +void task_init(void) +{ + tloge("!!!!!!!!task init!!!!!!!!!"); +} +void tee_task_entry(void) +{ + task_init(); + tee_srv_cs_server_loop("task_demo", dispatch_fns, sizeof(dispatch_fns) / sizeof(dispatch_fns[0]), NULL); +} +``` + +5. TA加载服务client库 +```c +#include "dcm_verify_hal.h" +#include +#include +#include +#include + +#define DCM_VERIFIER_SDK_SO_NAME "/tafs/dcm_verifier_sdk.so" + +typedef TEE_Result (*dcm_attn_verifier_verify_func) + (hm_attestation_info_t *attestInfo, const hm_attest_blob_t *cert_chain); + +TEE_Result dcm_cert_verify_hal_demo(hm_attestation_info_t *attestInfo, const hm_attest_blob_t *cert_chain) +{ + TEE_Result ret = TEE_ERROR_GENERIC; + void *dcm_sdk_lib_handle = dlopen(DCM_VERIFIER_SDK_SO_NAME, RTLD_NOW | RTLD_GLOBAL); + if (dcm_sdk_lib_handle == NULL) { + tloge("dlopen failed: %s, %s\n", DCM_VERIFIER_SDK_SO_NAME, dlerror()); + (void)pthread_mutex_unlock(&g_dcm_sdk_lib_lock); + return TEE_ERROR_GENERIC; + } + + dcm_attn_verifier_verify_func dcm_attn_verifier_verify = dlsym(dcm_sdk_lib_handle, "DcmAttnVerifierVerify"); + if (dcm_attn_verifier_verify == NULL) { + tloge("dlsym DcmAttnVerifierVerify failed\n"); + ret = TEE_ERROR_GENERIC; + goto free_handle; + } + + ret = dcm_attn_verifier_verify(attestInfo, cert_chain); + if (ret != TEE_SUCCESS) + tloge("DcmAttnVerifierVerify failed, ret=0x%x\n", ret); + else + tlogi("DcmAttnVerifierVerify success\n"); +free_handle: + dlclose(dcm_sdk_lib_handle); + dcm_sdk_lib_handle = NULL; + return ret; +} +``` +关于dlopen时传入client库路径的/tafs/dcm_verifier_sdk.so的说明:1. /tafs/是服务client库文件在TEE中的挂载路径;2. dcm_verifier_sdk是manifest文件中gpd.ta.service_name \ No newline at end of file diff --git a/zh-cn/application-dev/tee/figures/ca-ta.png b/zh-cn/application-dev/tee/figures/ca-ta.png new file mode 100644 index 0000000000000000000000000000000000000000..a1554ebe6dc9297abe90e8488e225a6c72d1f46b GIT binary patch literal 51479 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 literal 0 HcmV?d00001 diff --git a/zh-cn/application-dev/tee/figures/overview-of-opentrustee.png b/zh-cn/application-dev/tee/figures/overview-of-opentrustee.png new file mode 100644 index 0000000000000000000000000000000000000000..adb98a01a99b4ccbcf98a6a98573f31b50fd24b0 GIT binary patch literal 26750 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 UiFR4P^nbRy9Ctq4?)}?;05AT+ +TEE基于TrustZone技术提供可信执行环境。TrustZone技术将中央处理器的工作状态划分为正常模式和安全模式,富执行环境(Rich Execution Environment,REE)运行在正常模式下,TEE运行在安全模式下。在安全模式下,提供了对外围硬件资源的保护和隔离,包括内存、外设等,保障REE和TEE的隔离,保障TEE的安全性。
+iTrustee是一套完整的TEE解决方案,包含正常模式的客户端应用(Client Application,CA)、安全模式的可信应用(Trusted Application,TA)、安全模式下的可信操作系统。 + + +## 一、能力范围 +iTrustee提供了一个可信执行环境(TEE),运行在受硬件隔离的安全区域中,与常规操作系统如OpenHarmony同时运行但互相隔离,OpenTrustee比常规操作系统拥有更高的安全性,可以为设备上的机密数据提供保护。OpenTrustee是一套完整的TEE解决方案,包含多个部件,系统架构如下图所示: + +![TEE架构](figures/overview-of-opentrustee.png) + +在终端设备越来越智能化的今天,人们正在把越来越多的个人应用和数据放到设备上,安全性成为一个很重要的命题。可信执行环境(TEE)已逐渐成为终端设备必备的安全技术。OpenTrustee具备丰富的安全特性,可以支持开发者灵活部署安全应用,应用场景也非常广泛,例如:移动支付、生物认证、版权保护等,也可以为系统安全提供保护,如安全启动、系统完整性检测等。 + +iTrustee是一套完整的TEE解决方案,包含正常模式的客户端应用(Client Application,CA)、安全模式的可信应用(Trusted Application,TA)、安全模式下的可信操作系统, + +iTrusteeOpenTrustee可以为应用提供以下功能: + +### 可信存储 +iTrusteeOpenTrustee提供对数据的可信存储,并保证数据机密性、完整性和原子性。 +* 机密性:存储的数据为密文,保证数据不被泄露; +* 完整性:存储的数据有完整性保护,保证数据不被篡改; +* 原子性:保证数据的一致性; +* 隔离性:TA间存储的数据互相隔离,不能互相访问; +* 不可复制性:数据与设备进行绑定,复制到其它设备后无法访问。 + +### 加解密 +iTrusteeOpenTrustee支持以下多种加解密算法 +* 摘要算法:SHA1、SHA224、SHA256、SHA384、SHA512; +* MAC: HMAC、AES-CMAC; +* 对称加解密算法:AES、DES、3DES; +* 非对称加解密算法:RSA 。 + +其中,下表中的算法不推荐开发者使用。 + +| 算法及场景 | 算法 | +|----------------|--------------------------------------------------------| +| 对称加密算法 | DES、3DES | +| 哈希算法 | SHA1、MD5、SHA224、HMAC-SHA1、HMAC_SHA224 | +| 非对称加密 | RSA(密钥小于3072,不包含3072) | +| 数字签名 | RSA(密钥小于3072,不包含3072)、ECDSA-SHA1、ECDSA-SHA224、DSA | +| 密钥协商 | ECDH(<256bit) | + + +### 可信时间 +iTrusteee提供可信的基准时间,该时间不能被TA或REE应用修改,因此可以更好的防范恶意软件。 + +### 可信显示与输入 +iTrustee提供安全的显示和输入,确保恶意程序既看不到显示屏上的信息,也无法访问触摸屏。它可以保护密码或PIN输入等功能,且不会将凭证暴露给恶意程序。应用程序还向用户显示安全消息,从而避免应用程序被窥探。 + +### 第三方安全应用开发 +第三方应用开发者可基于iTrusteeOpenTrustee进行TA和服务的开发和调试。 + + +## 二、基本概念 + +### TrustZone +在ARM处理器内核的一个安全扩展,该项技术可以保护安全内存、加密块、键盘和屏幕等外设,从而可确保它们免遭软件攻击。 +### 安全存储 +安全存储,提供了一个安全的存储环境,加密保存用户的私有数据。 +### REE +富执行环境(Rich Execution Environment,REE)运行在正常模式下。 +### TEE +可信执行环境TEE(Trusted Execution Environment,TEE)运行在安全模式下。 +### CA +正常模式的客户端应用(Client Application,CA)。 +### TA +安全模式的可信应用(Trusted Application,TA)。 + +## 三、应用场景 +基于TEE KIT提供的安全应用开发能力,可构建如下安全应用场景。 +| 服务 | 具体应用 | +|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 内容保护 | 用于 DRM(Digital Rights Management)领域,保证视频在播放过程中的安全性,无法被窃取和监听。 | +| BYOD | 用于 BYOD(Bring Your Own Devices)领域,企业需要移动平台更高的安全性。例如使用安全存储对用户登录的密钥进行保存。iTrustee/OpenTrustee 可保证用户登录密钥的安全,防止操作系统中恶意程序窃取到用户密钥数据。 | +| 移动支付 | 用于移动支付领域,保证输入信息(PIN)的安全性,同时可配合 NFC 进行使用。iTrustee/OpenTrustee 可保护用户输入信息的安全,防止被恶意程序窃取。 | +| 应用逻辑保护 | iTrustee/OpenTrustee 可保护关键应用逻辑不被窃取或篡改。 | \ No newline at end of file -- Gitee