第六章:TrustZone与OPTEE移植
好,咱们今天聊点硬核的——TrustZone 和 OPTEE。说实话,这章内容在系统移植里属于「天花板」级别的难度。我当年第一次接触 TrustZone 时,光理解「安全世界」和「非安全世界」这两个概念就绕了好几天。但别怕,咱们一步步拆开来看。
6.1 ARM TrustZone 技术原理
TrustZone 是什么?说白了,它是在硬件层面把一颗 CPU 虚拟成两个「世界」。
- 安全世界(Secure World):运行可信代码,比如指纹比对、支付密钥、DRM 解密。
- 非安全世界(Normal World):跑你的 Android/Linux 系统,应用随便崩,但碰不到安全世界的数据。
这两个世界通过一个叫 Monitor Mode 的桥梁切换。我习惯把 Monitor 想象成一个「安检门」——每次进出都要经过它,而且只有特定的指令(SMC)才能触发。
核心要点:TrustZone 不是软件模拟的,是 ARM 处理器硬件支持的。从 Cortex-A 系列开始,几乎都有这个特性。
为什么会需要两个世界?你想想看,Android 系统漏洞那么多,如果密钥直接存在 Linux 内核里,被 root 了就全完了。TrustZone 把敏感操作隔离到安全世界,就算非安全世界被攻破,攻击者也拿不到密钥。
6.2 安全世界与非安全世界的切换流程
切换过程其实不复杂,但细节容易踩坑。我画了一张图帮你理解:
流程其实就三步:
- 非安全世界调用
SMC指令,触发异常进入 Monitor Mode。 - Monitor 检查调用来源,保存上下文,切换到安全世界。
- 安全世界处理完,再通过 Monitor 切回来。
我的经验:刚开始移植时,我总在 Monitor 的上下文保存上出问题。记得有一次,安全世界返回后非安全世界直接死机,查了两天才发现是栈指针没保存对。嗯,这里要注意——Monitor 代码里必须完整保存所有通用寄存器和系统寄存器。
6.3 OPTEE 源码编译
OPTEE 是 Linaro 主导的开源 TEE 实现。我建议直接用官方源码,别自己魔改。
编译 OPTEE 需要三个组件:
| 组件 | 说明 | 源码路径 |
|---|---|---|
| OPTEE OS | 安全世界的操作系统内核 | optee_os/ |
| OPTEE Client | 非安全世界的用户态库 | optee_client/ |
| OPTEE Linux 驱动 | 内核驱动,负责 SMC 调用 | optee_linuxdriver/ |
编译步骤大致如下:
# 1. 设置交叉编译工具链
export CROSS_COMPILE=aarch64-linux-gnu-
export PLATFORM=imx-mx8mmevk
# 2. 编译 OPTEE OS
cd optee_os
make -j4
# 3. 编译 OPTEE Client
cd ../optee_client
make -j4
# 4. 编译 Linux 驱动
cd ../optee_linuxdriver
make -j4
避坑指南:我曾经在 i.MX8M 上编译时,发现 OPTEE OS 死活起不来。最后定位到是 PLATFORM 变量写错了——i.MX8M 的板级配置是 imx-mx8mmevk,不是 imx8m。差一个字母,启动就卡在安全世界初始化。
6.4 安全存储与密钥管理
安全存储是 OPTEE 最常用的功能之一。说白了,就是把密钥、证书这类敏感数据加密后存到 Flash 里。
OPTEE 的安全存储机制是这样的:
- 每个 TA(可信应用)有自己的存储空间,其他 TA 访问不了。
- 数据用硬件唯一密钥(HUK)加密,HUK 存在 SoC 的 eFuse 或 OTP 里。
- 即使攻击者把 Flash 拆下来读,拿到的也是密文。
我写一个简单的 TA 示例,演示如何存储密钥:
// TA 入口函数:存储 AES 密钥
TEE_Result TA_StoreKey(uint8_t *key, size_t key_len)
{
TEE_Result res;
TEE_ObjectHandle obj;
// 创建持久化存储对象
res = TEE_CreatePersistentObject(
TEE_STORAGE_PRIVATE, // 私有存储
&key_obj_id, // 对象 ID
TEE_DATA_FLAG_ACCESS_WRITE,
TEE_HANDLE_NULL,
NULL, 0,
&obj
);
if (res != TEE_SUCCESS) {
EMSG("创建存储对象失败: 0x%x", res);
return res;
}
// 写入密钥数据
res = TEE_WriteObjectData(obj, key, key_len);
TEE_CloseObject(obj);
return res;
}
关键点:TEE_STORAGE_PRIVATE 表示只有当前 TA 能读写。如果换成 TEE_STORAGE_SYSTEM,那就是全局共享存储,所有 TA 都能访问——但一般不建议这么用。
密钥管理方面,OPTEE 提供了 HUK(硬件唯一密钥) 和 SSK(安全存储密钥) 两级派生机制。HUK 是芯片出厂时烧录的,SSK 由 HUK 和 TA 的 UUID 派生。这样每个 TA 的加密密钥都不同,即使一个 TA 被攻破,其他 TA 的数据依然安全。
我记得有一次客户要求「密钥必须物理不可导出」。我直接告诉他们:用 OPTEE 的 HUK 派生密钥,密钥永远不出安全世界,只返回加密后的结果。这才是真正的「硬件级安全」。
6.5 移植要点总结
最后,我把 i.MX 平台上移植 OPTEE 的几个关键点列出来:
- 设备树配置:需要在设备树里预留安全世界的内存区域,一般 32MB 起步。
- ATF 集成:OPTEE 需要和 ARM Trusted Firmware 配合,启动顺序是:BootROM → ATF → OPTEE → U-Boot → Linux。
- 中断处理:安全世界的中断(比如安全定时器)要配置成 FIQ,非安全世界的中断用 IRQ。这个配置错了,系统会频繁死锁。
- 调试手段:OPTEE 的日志通过 UART 输出,但默认是关闭的。我习惯在
core/arch/arm/plat-imx/conf.mk里打开CFG_TEE_CORE_LOG_LEVEL=3,这样能看到完整的初始化流程。
一个小技巧:调试安全世界时,别用 JTAG——安全世界的调试接口默认是锁死的。我一般用 printk 加延时的方式,虽然土但有效。后来发现 OPTEE 支持 CFG_TEE_CORE_DEBUG=y 开启调试断言,这个更靠谱。
嗯,TrustZone 和 OPTEE 的内容就讲到这里。这部分确实需要多动手,光看文档是学不会的。我建议你拿一块 i.MX8M 开发板,按照上面的步骤把 OPTEE 跑起来,哪怕只是打印一句 "Hello Secure World",也算迈出了第一步。
公众号:蓝海资料掘金营,微信deep3321