跳转至

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 的调用流程

  1. CA 调用 TEEC_InitializeContext 初始化客户端上下文。
  2. CA 调用 TEEC_OpenSession,使用 UUID 打开目标 TA 会话。
  3. TEE 首次创建 TA 时调用 TA_CreateEntryPoint,打开会话时调用 TA_OpenSessionEntryPoint
  4. CA 调用 TEEC_InvokeCommand 发送命令和参数;TA 在 TA_InvokeCommandEntryPoint 中处理业务。
  5. CA 调用 TEEC_CloseSession,TEE 调用 TA_CloseSessionEntryPoint
  6. CA 调用 TEEC_FinalizeContext;TA 实例销毁时调用 TA_DestroyEntryPoint

CA/TA 调用时序

软件架构与代码目录

软件架构

SELiteOS CA/TA 软件架构

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 目标默认包含 boxAtee_psa_cryptotee_psa_storagetee_psa_attestation 等组件,并启用 LOSCFG_SUPPORT_TACONFIG_SUPPORT_TA。不要只编译 LiteOS 主应用,否则 TA 不会进入最终固件。

快速跑通 TEE Hello Demo

功能说明

seliteos_tee_hello 验证完整的 REE/TEE 通信链路:CA 打开 hello_world TA 会话,发送 TA_HELLO_TEST 命令,TA 在安全侧打印 hello 日志,CA 检查返回值并释放资源。

TEE Hello Demo 流程

准备工作

说明:本文示例命令以一站式 CLI 为主。实际开发可从以下三种环境中选择,推荐使用一站式 CLI。

开发环境 适用场景 使用指南
一站式 CLI(推荐) 快速完成目标选择、构建、烧录和串口监视 一站式 CLI 开发环境使用指南
HiSpark Studio for VS Code 图形化编辑、编译、烧录和调试 HiSpark Studio for VS Code 开发环境使用指南
WSL 与 Docker 在 Windows 上使用一致的 Linux 容器构建环境 WSL 与 Docker 环境使用指南
  1. 一站式 CLI 开发环境使用指南安装并验证开发环境。
  2. 使用 3322 EVB,确保可以查看串口日志。
  3. 使用包含 3322-seliteos-release 的构建组合,例如 pack_diting_community
  4. 确认启动后串口出现 Welcome to SEliteOS!

操作步骤

默认版本 Hello World TA 已由 SEliteOS 镜像支持,所以只需要集成REE 侧 CA:

  1. samples/native_samples/CMakeLists.txtsrc/build/config/target_config/3322/config.py 中启用 CONFIG_ENABLE_SELITEOS_TEE_HELLO_SAMPLE,并将 seliteos_tee_hello_sample 加入 diting-communityram_component
  2. 构建 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 开发模式。

REE 侧 PSA API 到 TEE 侧 PSA 安全服务的调用路径

文件结构与代码走读

CA 参考文件

REE 侧 CA 已实现为 SELiteOS TEE Hello Demo。执行 AT+TEEHELLO 后,CA 依次调用 TEEC_InitializeContextTEEC_OpenSessionTEEC_InvokeCommandTEEC_CloseSessionTEEC_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_STARTBOXA_HEAP_SIZEBOXA_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_malloclos_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_sha256uapi_drv_cipher_sm3
对称加密 uapi_drv_cipher_symc_cryptuapi_drv_cipher_symc_gcm_encrypt
真随机数 uapi_drv_cipher_trng_get_randomuapi_drv_cipher_trng_get_random_bytes
公钥密码 uapi_drv_cipher_pke_ecdsa_signuapi_drv_cipher_pke_ecdsa_verifyuapi_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.hsec_storage_id 的有效取值范围为 [0, 0x100);每个 TA 应在该范围内规划互不冲突的 ID。

TEE 安全存储读写流程

该适配头文件将以下 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步骤

  1. 复制 hello_world_ta.c 的最小结构,不复制其 UUID。
  2. 为 TA 生成新的 UUID,并让 CA 引用相同 UUID。
  3. 在共享头文件中定义命令枚举;每增加一个命令,在 TA 的 switch 中增加一个分支。
  4. 对每种参数类型定义明确规则:输入/输出方向、最大长度、可接受的枚举值和错误码。
  5. 先实现无参数 hello 命令,再实现复杂的值参数和内存参数传递。
  6. 将 TA 加入 SEliteOS 目标,将 CA 加入 native_samples;不要把 TA 当作普通 LiteOS sample 编译。

编译、烧录、运行与调试

编译

  1. 检查 REE 侧 CA 源码存在。

    检查 seliteos_tee_hello.c/samples/native_samples/seliteos_tee_hello/seliteos_tee_hello.h。源码包含 tee_client_api.h,实现 CA 的初始化、打开会话、调用 TA_HELLO_TEST、关闭会话和错误日志。

  2. 在 native samples 总入口中按配置宏纳入子目录。

    samples/native_samples/CMakeLists.txt 增加:

    if("CONFIG_ENABLE_SELITEOS_TEE_HELLO_SAMPLE" IN_LIST DEFINES)
        add_subdirectory_if_exist(seliteos_tee_hello)
    endif()
    
  3. 将 CA 组件加入 LiteOS 目标。

src/build/config/target_config/3322/config.pyditing-community 目标中,同时增加配置宏和 RAM 组件:

```python
'CONFIG_ENABLE_SELITEOS_TEE_HELLO_SAMPLE',
```
'seliteos_tee_hello_sample',

前者使 native samples 的 CMake 进入 seliteos_tee_hello 子目录,后者使该 CA 组件链接到 LiteOS 主应用镜像。diting-community 目标已有 teec_componentlibteec,CA 才能解析 TEEC_* 符号。

  1. 执行完整打包。

    完成一站式 CLI 环境配置后,在工程目录运行:

    fbb set-target pack_diting_community
    fbb build
    

    该构建组合会同时构建 diting-community(LiteOS 主应用,包含 CA)和 3322-seliteos-release(SELiteOS,包含 TA)。若只构建 diting-community,CA 虽会进入 LiteOS 镜像,但无法验证 TA 调用。

  2. 确认产物和部署关系。

    • 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。

运行与预期结果

  1. 使用一站式 CLI 烧录完整固件并打开 UART2 日志串口;将 COM3 替换为实际端口:

    fbb flash -f "$env:FBB_SDK_DIR\output\3322\fwpkg\diting-community.fwpkg" --chip 3322 -d --timeout 180
    fbb monitor --port COM3 --baud 750000
    
  2. 重启后确认日志包含 Welcome to SEliteOS!

  3. 在 AT 串口发送 AT+TEEHELLO
  4. REE 侧输出 [TEE_HELLO] CA-to-TA invocation PASS 并由 AT 框架返回 OK;TEE 侧可见类似日志:

    TA_CreateEntryPoint!
    TA_OpenSessionEntryPoint!
    Hello world!
    Hello boxA!
    TA_CloseSessionEntryPoint!
    

若 AT 侧返回 ERROR,先根据 [TEE_HELLO] 日志中的 initialize failedopen session failedinvoke failed 判断失败阶段,再结合 resultorigin 排查,并确认完整固件中同时包含 LiteOS CA 与 SEliteOS TA。

调试方法

安全轻量系统调试流程

按以下顺序排查,前一步未通过时不要跳到后一步:

  1. 构建:是否同时包含 LiteOS 主应用和 3322-seliteos-release
  2. 镜像:是否生成 seliteos_signed.bin,并进入最终固件。
  3. 启动:是否出现 SEliteOS 欢迎日志。
  4. 会话:UUID 是否一致;记录 TEEC_ResultreturnOrigin
  5. 命令:CA 命令号是否与 TA switch 分支一致。
  6. 参数:检查参数类型、输入输出方向、缓冲区长度和共享内存释放时机。

推荐日志格式:

[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_componentlibteec 是否加入 REE 目标
TA 启动异常 内存布局或 PMP 对齐错误 检查 ta_memory_config.h、链接脚本和 TA 数量
安全存储无法覆盖 使用了 PSA_STORAGE_FLAG_WRITE_ONCE 确认业务是否需要一次写入属性
PSA 调用失败 未初始化服务或算法/组件未启用 先检查 psa_crypto_init() 返回值和 SEliteOS 组件