4、可信执行环境(TEE)简介:TEE 与 REE 隔离,密钥在 TEE 中的保护

聊到密钥隔离,光靠软件"分家"是不够的。你想想看,整个操作系统都在一个地址空间里跑,root 权限的恶意软件想读你的密钥缓冲区,也就是几行代码的事。所以硬件级的隔离才是真隔离。可信执行环境(TEE)干的正是这件事——在 CPU 里划出一块"安全飞地",专门处理敏感数据。我用过好几个不同的 TEE 方案,折腾下来发现,理解 TEE 和普通执行环境(REE)的隔离关系,才是用好它的前提。

4.1 什么是 TEE?它的定位是什么?

TEE 是一个与 REE(Rich Execution Environment)并行的硬件隔离环境。它有自己的安全内存、安全外设,甚至安全的启动流程。REE 里跑的是完整的操作系统(比如 Android、Linux),应用生态丰富,但不可信;TEE 里跑的是一个微型安全 OS(如 OP-TEE、QSEE、Trusty),只执行经过验证的安全任务。

我曾经在一个机顶盒项目里用过 TEE。当时客户要求把 DRM 密钥和播放器解扰逻辑都放 TEE 里。说实话,第一次调通的时候,看到 REE 端怎么都读不到 TEE 内存里的数据,心里才踏实——硬件隔离是真的硬。

4.2 TEE 与 REE 的隔离机制

隔离不是一句空话,它体现在好几个层面。

隔离层面 具体机制 保护目标
CPU 模式隔离 ARM TrustZone 的 Secure/Non-Secure World 切换世界时自动刷新缓存和 TLB
内存隔离 TZASC(TrustZone Address Space Controller)划定安全内存区域 REE 无法直接访问安全内存
中断隔离 安全中断由 TEE 处理,REE 不可见 防止 REE 劫持敏感事务
I/O 隔离 安全外设(如安全键盘、指纹)仅 TEE 可访问 输入数据不被 REE 窃取
注意: 隔离不是的。REE 和 TEE 之间需要通过 SMC(Secure Monitor Call) 进行通信。这个通信通道的设计如果不严谨,会成为侧信道攻击的目标。我见过一个项目因为共享内存的边界检查漏了一处,导致 TEE 里的密钥被 REE 侧读走。嗯,不要以为硬件隔离了就能放松软件检查。

4.3 密钥在 TEE 中的全生命周期保护

好的,现在把密钥请进 TEE。但密钥从出生到销毁,每个阶段都有坑。我个人习惯把这套生命周期画成一条链:生成 → 存储 → 使用 → 销毁。这里每一步都需要依赖 TEE 的隔离能力来保障。

4.3.1 密钥生成

密钥不能在外面生成再导入 TEE——那样生成时就已经暴露了。正确的做法是在 TEE 内部调用硬件随机数发生器(TRNG),由安全 OS 直接生成密钥对象。我遇到过有的芯片厂商把 TRNG 放在 REE 侧,结果被攻击者预测了随机数序列,导致所有会话密钥可预计算。所以选平台时,先确认 TRNG 是不是走安全世界。

4.3.2 密钥存储

TEE 内部的非易失性存储(如 RPMB、eFuse、安全 NVRAM)用来持久化密钥。REE 侧的文件系统不可靠,必须用 TEE 提供的安全存储 API。很多 TEE OS 会基于设备唯一密钥对存储内容进行加密和完整性保护。我在调试一个金融 POS 设备时,发现 TEE 存储的密钥文件会被 REE 侧残留的缓存影响,后来强制要求每次写入后清空安全存储缓存才解决。

4.3.3 密钥使用

所有密码学操作(加密、签名、密钥协商)都在 TEE 内完成。密钥永远不离开 TEE 安全内存。REE 侧要加密数据时,只能把明文传进 TEE,在 TEE 里计算,再把密文取回。说白了,TEE 就是个黑盒子——你给它输入,它给你输出,但你看不见盒子里的钥匙。

4.3.4 密钥销毁

密钥过期或设备退役后,必须彻底擦除。TEE 内可以用硬件安全清零指令覆盖内存,并且要保证 NVRAM 中的密钥无法恢复。有的 TEE OS 支持安全删除后锁定存储块,防止磨损均衡还原旧数据。这事不能交给 REE 侧去调一个 delete(),因为 delete() 可能只删了文件条目,数据还在物理介质上。

4.4 密钥域隔离在 TEE 中的典型架构

下面这张图可以帮你直观理解 REE 和 TEE 的隔离关系,以及密钥在其中的位置。

TEE 与 REE 隔离架构及密钥域 REE(普通世界) TEE(安全世界) Rich OS (Linux/Android) REE 应用 (不可信) 共享内存缓冲区 (明文输入/密文输出) TEE 安全 OS (OP-TEE / Trusty) TEE 可信应用 (安全服务) 密钥域 私钥 / 密钥材料 安全存储 (NVRAM/RPMB) 硬件隔离边界(TrustZone / S-EL2) REE 只能通过共享内存做明文→密文交互 无法直接访问 TEE 内部任何密钥材料

从图里你能看到:REE 侧的应用通过共享内存与 TEE 可信应用通信。但共享内存只放待处理的密文或明文数据,真正的密钥永远锁在右边紫色区域里。我刚开始接触 TEE 时,以为共享内存也可以传密钥,后来在安全评审被猛批——密钥一旦流过共享内存,就进入了 REE 侧的视野,侧信道攻击就能跟着来。

4.5 常见 TEE 关键技术方案对比

当下主流的 TEE 方案有不少,但选型时要关注它对密钥域隔离的力度。

方案 硬件基础 密钥隔离方式 安全认证级别
ARM TrustZone ARM 架构(Cortex-A/R) 物理地址空间隔离 + TZASC GlobalPlatform TEE 认证
Intel SGX x86(Skylake+) CPU 封装内存加密(MEE) Intel 认证 + 安全版本号
RISC-V 物理内存保护(PMP) RISC-V(PMP 扩展) 物理地址区域锁定 按实现级别(多级 PMP)
我的建议: 如果项目只做手机或嵌入式 Linux,TrustZone 是最成熟的。不过要注意,TrustZone 的安全世界如果拥有一个错误的安全 OS(比如把调试接口留着没关),再硬的隔离也白搭。我曾经在一家外包公司的固件里发现,他们的 TEE 对外暴露了一个未授权的共享内存映射——攻击者直接通过 REE 侧的驱动就能读写 TEE 内部变量。所以除了选硬件方案,安全 OS 本身也要做纵深防护。

4.6 实操中的密钥域隔离实用技巧

纸上谈兵没用,我直接说几个我在调 TEE 时用过的实际策略。

  • 最小权限原则: 每个 TEE 可信应用只占用它需要的最小内存区域。不要在安全世界里开一个大 while(1) 监听所有 SMC,应该按业务拆分 Trustlet/TA。
  • 关键密钥绑定硬件: 设备唯一密钥(DUK)要用 eFuse 烧录,不能被 REE 侧读到。我曾经有个项目把 DUK 写在了 REE 侧的配置分区,后果是升级时攻击者直接 dump 了整个分区……
  • 会话密钥及时销毁: 每次密码学操作完成后,立即在 TEE 内清理临时密钥。不要为了性能把密钥缓存在安全内存里——很多侧信道攻击靠的是时间差和内存残留。
  • 安全通道验证 TEE 身份: 在 REE 与 TEE 建立通信前,REE 应验证 TEE 的签名确保没有被替换。说得直白点,你要确保调用的那个 TA 是正经签过名的,不是攻击者注入的假 TA。
小技巧: 在调试阶段可以打开 TEE 的日志输出查看密钥生命周期,但生产环境中一定要关闭所有 debug 接口。我记得有一次生产发布前忘了关串口日志,TEE 里的密钥派生过程被打印在了内核日志里,还好内测时发现了。

4.7 小结

TEE 通过硬件隔离为密钥域提供了强隔离基础。但注意,它并不是万能的——共享内存的边界管理、安全 OS 自身漏洞、调试接口遗留,都可能让密钥域形同虚设。我的经验是:把 TEE 当作最后一道防线,但不要因为有了 TEE 就放松了对 REE 侧的加固要求。后面几个章节会具体讲如何在芯片层面进一步做密钥域隔离,以及防侧信道攻击的工程手段。