第2章:TEE架构概览——GlobalPlatform标准与内部组件

做TEE开发这些年,我最大的感受是:不懂架构就动手,迟早要踩坑。记得我刚接触TEE时,上来就翻代码、调接口,结果被各种术语和组件关系搞得晕头转向。后来老老实实把GlobalPlatform标准啃了一遍,才真正看清了全局。

这一章,我们就来聊聊TEE的整体架构。说白了,就是搞清楚TEE里面到底有什么、它们怎么配合、以及GlobalPlatform这个组织为什么这么重要。

2.1 GlobalPlatform TEE标准——为什么绕不开它?

你可能会问:TEE又不是只有一家在做,为什么非要跟着GlobalPlatform(GP)的标准走?

嗯,这个问题我当年也问过。后来在项目里对接过三四种不同厂商的TEE,才明白GP标准的价值——它定义了TEE的“通用语言”。没有这个标准,每个厂商的TEE接口都不一样,上层应用得针对每个平台单独适配,那开发效率就太低了。

GP TEE标准的核心目标:

  • 定义TEE与REE(Rich Execution Environment,即普通操作系统)之间的通信机制
  • 规范TEE内部组件的接口和行为
  • 保证不同厂商的TEE实现可以运行相同的TA(Trusted Application,可信应用)
  • 提供安全存储、密码学服务等基础功能的标准化接口

GP标准把TEE分成了几个关键层次,我习惯用一张图来理解:

GlobalPlatform TEE 标准架构 REE(富执行环境) 普通操作系统(Linux/Android) CA(客户端应用) TEE Client API REE TEE 驱动 / 共享内存 SMC 调用 TEE(可信执行环境) 安全操作系统(OPTEE/Trusty/QSEE) TA(可信应用) TEE Internal API TEE 内核 / 安全监控 硬件安全基础:ARM TrustZone / Intel SGX / RISC-V 物理内存保护 安全存储 RPMB / 安全文件系统 安全时钟 可信时间源 / 防回滚 密码学服务 RSA / AES / ECC / HASH CA 通过 TEE Client API 发起请求 → 驱动转发 SMC 到 TEE → TEE 内核调度 TA 执行 TA 通过 TEE Internal API 使用安全存储、时钟、密码学等基础服务

我的经验:刚开始看GP标准文档时,别试图一口气读完。我建议先看 GPD_TEE_Internal_Core_API_Specification 和 GPD_TEE_Client_API_Specification 这两份,它们覆盖了90%的日常开发场景。

2.2 TEE内部组件详解

好,现在我们聚焦到TEE内部。一个完整的TEE系统,至少包含以下几个核心组件。我按从底层到上层的顺序来讲。

2.2.1 TEE内核——整个系统的“心脏”

TEE内核说白了就是一个精简的安全操作系统。它不像Linux那样功能齐全,但该有的都有:任务调度、内存管理、中断处理、进程间通信。

我在项目中遇到过一个问题:某个TA在运行时会莫名其妙地崩溃,查了两天才发现是TEE内核的栈空间配置太小,递归调用把栈撑爆了。嗯,这种问题在REE侧很少见,但在TEE里很常见——因为资源太有限了。

特性 TEE内核 普通OS内核
代码体积 通常 < 500KB 几MB到几十MB
内存占用 几MB到几十MB 几百MB到几GB
调度策略 简单优先级 + 时间片 CFS / 实时调度
安全模型 最小权限 + 白名单 用户态/内核态隔离
中断处理 快速FIQ处理为主 复杂IRQ分发

2.2.2 TEE API——CA和TA的“桥梁”

GP标准定义了两套API,我习惯叫它们“一内一外”:

  • TEE Client API(外部API):运行在REE侧,供CA调用。负责建立会话、传输数据、调用TA命令。
  • TEE Internal API(内部API):运行在TEE侧,供TA调用。提供密码学、安全存储、时钟等基础服务。

举个例子,一个支付CA要调用TA做签名,流程是这样的:

// REE侧:CA调用Client API
TEEC_Context context;
TEEC_Session session;
TEEC_Operation op;

TEEC_InitializeContext(NULL, &context);
TEEC_OpenSession(&context, &session, &uuid, 
                 TEEC_LOGIN_PUBLIC, NULL, NULL, NULL);
// 准备签名数据
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
                                 TEEC_MEMREF_TEMP_OUTPUT,
                                 TEEC_NONE, TEEC_NONE);
op.params[0].tmpref.buffer = input_data;
op.params[0].tmpref.size = input_size;
op.params[1].tmpref.buffer = output_data;
op.params[1].tmpref.size = &output_size;

TEEC_InvokeCommand(&session, CMD_SIGN, &op, NULL);
TEEC_CloseSession(&session);
TEEC_FinalizeContext(&context);

注意:CA和TA之间的数据传输,一定要考虑共享内存的大小限制。我曾经遇到过CA传了一个1MB的缓冲区给TA,结果TA侧内存不够直接崩溃。后来我们改成了分块传输,才解决了问题。

2.2.3 安全存储——数据保护的“保险柜”

安全存储是TEE最常用的功能之一。说白了,就是让TA能把密钥、证书这类敏感数据存起来,而且只有TA自己才能读。

GP标准定义的安全存储,底层通常依赖两种机制:

  1. RPMB(Replay Protected Memory Block):利用eMMC/UFS的物理安全分区,硬件防重放攻击。
  2. 安全文件系统:在REE侧的文件系统上,用TEE密钥加密存储。数据是密文,REE看不到内容。

我个人习惯在项目初期就确定用哪种方案。RPMB安全性高,但容量小、速度慢;安全文件系统容量大,但依赖TEE密钥管理。没有绝对的好坏,看场景选。

2.2.4 安全时钟——可信的“时间证人”

安全时钟这个组件,很多人容易忽略。但它在很多场景下至关重要——比如证书有效期校验、许可证管理、防回滚保护。

安全时钟和普通时钟最大的区别是:它不能被REE侧篡改。你想想看,如果攻击者把系统时间改回2019年,那过期的证书是不是就能用了?这就是为什么TEE需要自己的可信时间源。

实际项目中,安全时钟通常来自:

  • 硬件RTC(实时时钟),通过安全世界独占访问
  • 安全芯片(如SE)提供的可信时间
  • 网络时间协议(NTP)的签名时间戳(需要联网)

避坑指南:我曾经在一个项目里,TA依赖安全时钟做license校验。结果发现设备断电后,RTC电池没电了,时间重置到了1970年。从那以后,我每次都会加一个“时间合理性检查”——比如判断当前时间是否在固件编译日期之后。

2.3 组件之间的协作关系

这几个组件不是孤立的,它们配合起来才能干活。我画个简单的调用链:

CA (REE侧)
  │
  ├─ 调用 TEEC_OpenSession() → TEE Client API
  │     │
  │     ├─ REE驱动 → SMC陷入 → TEE内核
  │     │     │
  │     │     ├─ TEE内核加载TA镜像
  │     │     ├─ 验证TA签名
  │     │     └─ 创建TA实例
  │     │
  │     └─ 返回会话句柄给CA
  │
  ├─ 调用 TEEC_InvokeCommand() → TEE Client API
  │     │
  │     ├─ 参数传递到TA
  │     ├─ TA调用 TEE Internal API
  │     │     ├─ TEE_AllocateMemory() 分配安全内存
  │     │     ├─ TEE_ReadObjectData() 从安全存储读取密钥
  │     │     ├─ TEE_GetSystemTime() 获取安全时钟
  │     │     └─ TEE_AsymmetricSignDigest() 执行签名
  │     │
  │     └─ 结果返回给CA
  │
  └─ 调用 TEEC_CloseSession() 关闭会话

这个流程看起来简单,但每个环节都可能出问题。比如TA加载失败、共享内存不足、安全存储读写超时——我在调试时都遇到过。所以我的建议是:先把每个组件的边界测清楚,再集成。

2.4 本章小结

这一章我们聊了TEE的整体架构。核心就三件事:

  • GlobalPlatform标准定义了TEE的通用框架,让不同厂商的TEE能跑同样的TA
  • TEE内部组件包括内核、API、安全存储、安全时钟,各司其职
  • 组件协作通过Client API和Internal API完成CA到TA的完整调用链