SEliteOS 软件开发指南
文档说明
本文档介绍如何理解 SEliteOS、选择合适的安全开发方式,并完成 REE 侧客户端应用(CA)与 TEE 侧可信应用(TA)的最小通信闭环。 SELiteOS 是基于海思 RISC-V 芯片构建的安全 OS,与运行普通业务的 LiteOS 隔离。它将安全业务、密钥和敏感数据放在可信执行环境中处理,并提供以下核心能力:
| 核心能力 | 说明 |
|---|---|
| 硬件级隔离 | 通过 RISC-V TES 硬件实现内存和 CPU 运行环境的隔离。 |
| 数据机密性 | 敏感数据在 SEliteOS 内部生成、存储和使用,外部无法直接访问。 |
| 数据完整性 | 确保 SEliteOS 的代码及数据不能被 REE 篡改。 |
SEliteOS 背景知识
基本概念
| 名称 | 含义 | 开发者需要知道什么 |
|---|---|---|
| REE | 富执行环境,运行 LiteOS 主应用 | 普通业务、网络和界面通常在这里运行 |
| TEE | 可信执行环境,运行 SEliteOS | 用于隔离密钥、敏感算法和安全数据 |
| CA | Client Application,REE 侧客户端 | 调用 libteec 请求 TA 执行安全业务 |
| TA | Trusted Application,TEE 侧可信应用 | 实现敏感逻辑,不能被 REE 直接读写 |
| GP | GlobalPlatform | 定义 CA 与 TA 交互所遵循的 TEE 客户端 API 与相关规范 |
| TES | Trusted Execution State | RISC-V 的可信执行状态,用于实现安全与非安全执行环境隔离 |
| PSA | Platform Security Architecture | 提供统一的密码、安全存储和证明接口 |
| UUID | TA 的唯一标识 | CA 和 TA 必须使用完全相同的 UUID |
什么时候使用哪种方式
| 您的需求 | 推荐方式 | 原因 |
|---|---|---|
| 让安全世界执行一段自定义敏感业务 | CA + TA | 逻辑和数据可放在 TEE 中隔离 |
| 加密、哈希、签名、随机数或密钥管理 | PSA Crypto | 不需要自行实现 TA 或密码算法 |
| 保存令牌、配置或其他敏感小数据 | PSA Protected Storage | 具有安全属性且接口简单 |
CA/TA 的调用流程
- CA 调用
TEEC_InitializeContext初始化客户端上下文。 - CA 调用
TEEC_OpenSession,使用 UUID 打开目标 TA 会话。 - TEE 首次创建 TA 时调用
TA_CreateEntryPoint,打开会话时调用TA_OpenSessionEntryPoint。 - CA 调用
TEEC_InvokeCommand发送命令和参数;TA 在TA_InvokeCommandEntryPoint中处理业务。 - CA 调用
TEEC_CloseSession,TEE 调用TA_CloseSessionEntryPoint。 - CA 调用
TEEC_FinalizeContext;TA 实例销毁时调用TA_DestroyEntryPoint。
软件架构与代码目录
软件架构
REE 与 TEE 的地址空间相互隔离。CA 通过 libteec 把 TA 请求交给通信层;SELiteOS 内核负责会话管理、TA 调度和 TEE 侧 PSA 安全服务分发。TA 用于自定义可信业务;标准密码、安全存储和证明能力则由 REE 侧 CA 通过 PSA API 请求 TEE 侧 PSA 安全服务。
SDK 代码目录
| 内容 | 路径 |
|---|---|
| SEliteOS 内核 | src/kernel/seliteos/kernel/ |
| TA 示例和注册 | src/kernel/seliteos/application/boxa/ |
| 当前 TA 示例 | src/kernel/seliteos/application/boxa/hello_world_ta.c |
| TA UUID 和命令 | src/kernel/seliteos/application/boxa/include/boxa_header.h |
| CA 调用库 | src/middleware/utils/libteec/libteec/ |
| TEE 侧 PSA 安全服务 | src/kernel/seliteos/kernel/service/psa/ |
| 3322 构建配置 | src/build/config/target_config/3322/config.py |
须知: 3322 的
3322-seliteos-release目标默认包含boxA、tee_psa_crypto、tee_psa_storage、tee_psa_attestation等组件,并启用LOSCFG_SUPPORT_TA和CONFIG_SUPPORT_TA。不要只编译 LiteOS 主应用,否则 TA 不会进入最终固件。
快速跑通 TEE Hello Demo
功能说明
seliteos_tee_hello 验证完整的 REE/TEE 通信链路:CA 打开 hello_world TA 会话,发送 TA_HELLO_TEST 命令,TA 在安全侧打印 hello 日志,CA 检查返回值并释放资源。
准备工作
说明:本文示例命令以一站式 CLI 为主。实际开发可从以下三种环境中选择,推荐使用一站式 CLI。
| 开发环境 | 适用场景 | 使用指南 |
|---|---|---|
| 一站式 CLI(推荐) | 快速完成目标选择、构建、烧录和串口监视 | 一站式 CLI 开发环境使用指南 |
| HiSpark Studio for VS Code | 图形化编辑、编译、烧录和调试 | HiSpark Studio for VS Code 开发环境使用指南 |
| WSL 与 Docker | 在 Windows 上使用一致的 Linux 容器构建环境 | WSL 与 Docker 环境使用指南 |
- 按一站式 CLI 开发环境使用指南安装并验证开发环境。
- 使用 3322 EVB,确保可以查看串口日志。
- 使用包含
3322-seliteos-release的构建组合,例如pack_diting_community。 - 确认启动后串口出现
Welcome to SEliteOS!。
操作步骤
默认版本 Hello World TA 已由 SEliteOS 镜像支持,所以只需要集成REE 侧 CA:
- 在 samples/native_samples/CMakeLists.txt 和 src/build/config/target_config/3322/config.py 中启用
CONFIG_ENABLE_SELITEOS_TEE_HELLO_SAMPLE,并将seliteos_tee_hello_sample加入diting-community的ram_component。 - 构建
diting-community并烧录最终固件;CA 会随 LiteOS 主应用镜像部署,默认 TA 随3322-seliteos-release镜像部署。
接口列表
当前 API Reference 未提供 TEEC_* 和 TA 生命周期接口的独立页面,以下接口链接到 SDK 头文件;PSA 与安全存储接口链接到具体 API 锚点。
CA 调用的 TEEC_* 接口可参考 SDK 头文件 middleware/utils/libteec/libteec/tee_client_api.h,或 GlobalPlatform TEE Client API Specification。
| 接口 | 使用侧 | 作用 |
|---|---|---|
| TEEC_InitializeContext | CA | 初始化客户端上下文 |
| TEEC_OpenSession | CA | 根据 UUID 打开 TA 会话 |
| TEEC_InvokeCommand | CA | 向 TA 发起命令调用 |
| TEEC_CloseSession | CA | 关闭 TA 会话 |
| TEEC_FinalizeContext | CA | 释放客户端上下文 |
| TEEC_AllocateSharedMemory | CA | 分配 REE/TEE 共享内存 |
| TEEC_ReleaseSharedMemory | CA | 释放共享内存 |
| TA_CreateEntryPoint | TA | 创建 TA 实例 |
| TA_OpenSessionEntryPoint | TA | 打开会话 |
| TA_InvokeCommandEntryPoint | TA | 处理 CA 命令 |
| Protected Storage API | REE 侧 CA | 安全存储数据的写入、读取和删除 |
| PSA Crypto | REE 侧 CA | 密钥、加密、哈希、签名 |
| PSA Attestation | REE 侧 CA | 获取设备证明令牌 |
PSA 接口使用说明(REE 侧 CA)
PSA API 由 REE 侧 CA 调用。CA 通过 PSA API 发起密码、密钥管理、安全存储或设备证明请求;实际的加解密、密钥管理和安全数据处理由 TEE 侧的 PSA 安全服务及安全驱动完成。
PSA API不涉及 TA 应用开发:如果业务只需要标准密码或安全存储能力,不需要创建 TA、定义 TA UUID 或调用 TEEC_OpenSession。只有需要在 TEE 中实现自定义隔离业务逻辑时,才选择 CA + TA 开发模式。
文件结构与代码走读
CA 参考文件
REE 侧 CA 已实现为 SELiteOS TEE Hello Demo。执行 AT+TEEHELLO 后,CA 依次调用 TEEC_InitializeContext、TEEC_OpenSession、TEEC_InvokeCommand、TEEC_CloseSession 和 TEEC_FinalizeContext,连接当前 boxA Hello World TA;构建与部署步骤请参见本文“编译、烧录、运行与调试”章节。
TA 参考文件
src/kernel/seliteos/application/boxa/
├── CMakeLists.txt # 将 hello_world_ta.c 加入 SEliteOS 组件
├── hello_world_ta.c # TA 五个入口、命令分发、TA 注册
└── include/
└── boxa_header.h # TA_UUID 和 TA_HELLO_TEST
boxa_header.h:定义 UUID 和命令
CA 使用 UUID 定位 TA,TA 使用命令号分发业务,因此两个值必须在双方保持一致。
#define TA_UUID { 0x426e8f6f, 0xec74, 0x427b, \
{ 0x94, 0x24, 0x0d, 0xd8, 0xf3, 0xf3, 0x37, 0x06 } }
enum ta_command {
TA_HELLO_TEST = 0,
};
hello_world_ta.c:TA 生命周期
TA 必须提供五个入口:创建、销毁、打开会话、关闭会话、处理命令。新手可以先保留创建、销毁和会话函数的最小实现,把业务代码集中到 TA_InvokeCommandEntryPoint。
static TEE_Result TA_InvokeCommandEntryPoint(void *session, uint32_t command,
uint32_t param_types, TEE_Param params[4])
{
(void)session;
(void)param_types;
(void)params;
switch (command) {
case TA_HELLO_TEST:
LOS_Printf("Hello world!\\n");
return TEE_SUCCESS;
default:
return TEE_ERROR_NOT_IMPLEMENTED;
}
}
ta_register:注册 TA
ta_register 将 TA 名称、UUID、入口函数和内存布局注册到 SEliteOS。现有示例使用 BOXA_DATA_START、BOXA_HEAP_SIZE、BOXA_STACK_START 等宏;这些宏来自板级 ta_memory_config.h。
注意: TA 的地址和长度受 PMP 对齐限制。修改内存布局前,必须检查链接脚本和
ta_memory_config.h;当前框架最多支持两个 TA。不要从其他板型直接复制地址。
基于 Demo 新建自己的 TA/CA
TA 可使用的接口
TA 可以使用安全 OS TEE API、TEE 驱动 API 和 TEE 安全存储 API。TA 不调用 REE 侧的 PSA API;标准 PSA 能力由 REE 侧 CA 调用。除业务接口外,TA 还需要实现生命周期入口、校验 CA 参数并完成 TA 注册。
安全 OS TEE API
| 接口 | 用途 |
|---|---|
| LOS_Printf | 输出不含敏感数据的调试日志 |
| los_usr_malloc、los_usr_free | 为 TA 申请和释放用户态堆内存 |
内存使用
使用 los_usr_malloc 申请的内存必须在所有成功和失败路径中使用 los_usr_free 释放。不要把 CA 传入的指针交给 los_usr_free,也不要在日志中输出敏感缓冲区内容。
TEE 驱动 API
TEE 驱动 API 位于 kernel/seliteos/common/usrdrvlib/security_unified/drvbox_call.h,为 TA 提供受控的安全硬件驱动调用入口。常用能力包括:
| 能力 | 示例接口 |
|---|---|
| 哈希 | uapi_drv_cipher_sha256、uapi_drv_cipher_sm3 |
| 对称加密 | uapi_drv_cipher_symc_crypt、uapi_drv_cipher_symc_gcm_encrypt |
| 真随机数 | uapi_drv_cipher_trng_get_random、uapi_drv_cipher_trng_get_random_bytes |
| 公钥密码 | uapi_drv_cipher_pke_ecdsa_sign、uapi_drv_cipher_pke_ecdsa_verify、uapi_drv_cipher_pke_rsa_sign |
使用驱动接口前,应根据具体接口的头文件声明校验算法、密钥、输入输出缓冲区和长度;驱动返回失败时立即停止后续安全操作。
TEE 安全存储 API
TEE 安全存储 API 面向 TA,用于保存 TA 私有的令牌、状态或其他业务敏感小数据;它与 REE 侧 CA 使用 PSA API 的 Protected Storage API 是两套不同的调用入口。
TA 调用入口位于 kernel/seliteos/common/usrsyslib/sec_storage_adapter/box_sec_storage_adapter.h,底层服务接口位于 /src/kernel/seliteos/kernel/base/sec_storage/include/sec_storage.h。sec_storage_id 的有效取值范围为 [0, 0x100);每个 TA 应在该范围内规划互不冲突的 ID。
该适配头文件将以下 TA 调用入口映射到 TEE 安全存储实现:
| 接口 | 用途 |
|---|---|
uapi_sec_storage_write |
写入 TA 私有安全数据 |
uapi_sec_storage_read |
读取 TA 私有安全数据 |
uapi_sec_storage_delete |
删除 TA 私有安全数据 |
写入测试数据后应读回验证,并在测试结束后删除。需要扩展属性或长度查询时,请以底层 /src/kernel/seliteos/kernel/base/sec_storage/include/sec_storage.h 的接口声明为准。
#include "sec_storage.h"
#define TA_STORAGE_ID 0x0000 /* 有效范围:0, 0x100) */
errcode_t ret = uapi_sec_storage_write(TA_STORAGE_ID, data, data_len);
if (ret == ERRCODE_SUCC) {
uint16_t actual_len = 0;
ret = uapi_sec_storage_read(TA_STORAGE_ID, sizeof(read_buf),
&actual_len, read_buf);
}
日志只打印 sec_storage_id、长度和错误码,不能打印令牌、密钥或数据内容。
新建TA和CA步骤
- 复制
hello_world_ta.c的最小结构,不复制其 UUID。 - 为 TA 生成新的 UUID,并让 CA 引用相同 UUID。
- 在共享头文件中定义命令枚举;每增加一个命令,在 TA 的
switch中增加一个分支。 - 对每种参数类型定义明确规则:输入/输出方向、最大长度、可接受的枚举值和错误码。
- 先实现无参数 hello 命令,再实现复杂的值参数和内存参数传递。
- 将 TA 加入 SEliteOS 目标,将 CA 加入
native_samples;不要把 TA 当作普通 LiteOS sample 编译。
编译、烧录、运行与调试
编译
-
检查 REE 侧 CA 源码存在。
检查 seliteos_tee_hello.c 和 /samples/native_samples/seliteos_tee_hello/seliteos_tee_hello.h。源码包含
tee_client_api.h,实现 CA 的初始化、打开会话、调用TA_HELLO_TEST、关闭会话和错误日志。 -
在 native samples 总入口中按配置宏纳入子目录。
-
将 CA 组件加入 LiteOS 目标。
在 src/build/config/target_config/3322/config.py 的 diting-community 目标中,同时增加配置宏和 RAM 组件:
```python
'CONFIG_ENABLE_SELITEOS_TEE_HELLO_SAMPLE',
```
前者使 native samples 的 CMake 进入 seliteos_tee_hello 子目录,后者使该 CA 组件链接到 LiteOS 主应用镜像。diting-community 目标已有 teec_component 和 libteec,CA 才能解析 TEEC_* 符号。
-
执行完整打包。
完成一站式 CLI 环境配置后,在工程目录运行:
该构建组合会同时构建
diting-community(LiteOS 主应用,包含 CA)和3322-seliteos-release(SELiteOS,包含 TA)。若只构建diting-community,CA 虽会进入 LiteOS 镜像,但无法验证 TA 调用。 -
确认产物和部署关系。
- LiteOS 主应用镜像包含
seliteos_tee_hello_sample;可在应用映像的 map 文件中搜索该组件或TEEC_OpenSession。 output/3322/acore/3322-seliteos-release/seliteos_signed.bin包含 TA。- 打包产物
diting-community.fwpkg同时包含 LiteOS 主应用和 SEliteOS 镜像;烧录该完整包后,REE 侧 CA 才能调用 TEE 侧 TA。
- LiteOS 主应用镜像包含
运行与预期结果
-
使用一站式 CLI 烧录完整固件并打开 UART2 日志串口;将
COM3替换为实际端口: -
重启后确认日志包含
Welcome to SEliteOS!。 - 在 AT 串口发送
AT+TEEHELLO。 -
REE 侧输出
[TEE_HELLO] CA-to-TA invocation PASS并由 AT 框架返回OK;TEE 侧可见类似日志:
若 AT 侧返回 ERROR,先根据 [TEE_HELLO] 日志中的 initialize failed、open session failed 或 invoke failed 判断失败阶段,再结合 result 和 origin 排查,并确认完整固件中同时包含 LiteOS CA 与 SEliteOS TA。
调试方法

按以下顺序排查,前一步未通过时不要跳到后一步:
- 构建:是否同时包含 LiteOS 主应用和
3322-seliteos-release。 - 镜像:是否生成
seliteos_signed.bin,并进入最终固件。 - 启动:是否出现 SEliteOS 欢迎日志。
- 会话:UUID 是否一致;记录
TEEC_Result和returnOrigin。 - 命令:CA 命令号是否与 TA
switch分支一致。 - 参数:检查参数类型、输入输出方向、缓冲区长度和共享内存释放时机。
推荐日志格式:
[tee_demo] OpenSession failed: result=0xFFFF0006, origin=0x2
[tee_demo] InvokeCommand: command=0, input_len=32
注意事项
- CA 输入不可信。TA 必须校验所有命令、参数类型、指针、长度和枚举值。
TEEC_Operation最多支持四个参数;小数据使用值参数,大数据使用受控共享内存。- 共享内存、会话和上下文必须按逆序释放;失败分支也不能遗漏释放。
- 不在日志中打印密钥、明文密码、完整证明令牌或敏感缓冲区。
- 安全存储不适合日志和大文件;对密码密钥优先使用 PSA Crypto 密钥对象。
常见错误
| 现象 | 常见原因 | 排查方法 |
|---|---|---|
| 看不到 SEliteOS 欢迎日志 | SEliteOS 镜像未构建、未打包或未烧录 | 检查构建组合和 seliteos_signed.bin |
TEEC_OpenSession 失败 |
UUID 不一致、TA 未注册 | 对比 CA/TA UUID,检查 ta_register 和组件列表 |
TEEC_InvokeCommand 返回未实现 |
命令枚举不一致 | 对比 CA 命令和 TA switch 分支 |
TEEC_ERROR_BAD_PARAMETERS |
参数类型、指针、长度或 flags 错误 | 打印参数类型、长度和 returnOrigin |
TEEC_* 链接失败 |
CA 未链接 libteec |
检查 teec_component、libteec 是否加入 REE 目标 |
| TA 启动异常 | 内存布局或 PMP 对齐错误 | 检查 ta_memory_config.h、链接脚本和 TA 数量 |
| 安全存储无法覆盖 | 使用了 PSA_STORAGE_FLAG_WRITE_ONCE |
确认业务是否需要一次写入属性 |
| PSA 调用失败 | 未初始化服务或算法/组件未启用 | 先检查 psa_crypto_init() 返回值和 SEliteOS 组件 |