4. TEE基础概念:TEE与REE的隔离机制、TEE内部组件、GP TEE标准简介
好,咱们进入正题。这一章聊的是TEE的基础概念。说白了,就是搞清楚TEE到底是个什么东西,它怎么跟普通的操作系统(也就是REE)隔离开来,里面又住了哪些“人”。
我刚开始接触TEE的时候,也觉得很神秘。感觉像是一个黑盒子,只知道它安全,但具体怎么安全,里面啥结构,完全没概念。后来踩了不少坑,才慢慢摸清楚。今天我就把这些经验掰开了揉碎了讲给你听。
4.1 TEE与REE的隔离机制:一堵“硬件墙”
先问个问题:为什么手机里既有Android(REE),又要搞一个TEE?
答案很简单:不信任。
REE(Rich Execution Environment)太复杂了。Android、Linux,动辄几千万行代码。你想想看,这么多代码,谁能保证没有漏洞?一旦被攻破,你的指纹、支付密码、人脸数据就全裸奔了。
TEE的思路就是:既然你不可信,那我就跟你物理隔离。
这个隔离,不是软件层面的“沙箱”,而是硬件层面的“墙”。
核心隔离机制:
- CPU模式隔离: TEE运行在“安全模式”(Secure Mode),REE运行在“非安全模式”(Normal Mode)。CPU硬件保证,安全模式下的内存、外设,非安全模式完全访问不到。
- 内存隔离: 物理内存被划分为“安全世界”和“非安全世界”。TEE的内存区域,REE的MMU(内存管理单元)根本看不到。我见过有新手试图在Linux里直接读取TEE的内存地址,结果直接触发硬件异常,系统重启。
- 外设隔离: 像指纹传感器、安全键盘这些敏感外设,直接由TEE控制。REE想读取指纹数据?门都没有。
嗯,这里要注意一点。这个隔离不是绝对的“老死不相往来”。它们之间还是需要通信的,这个通信接口就是Monitor Mode(监控模式)。
我曾经在一个项目里,因为REE和TEE的共享内存配置不对,导致TA(Trusted Application)拿到的数据全是乱码。排查了整整两天,最后发现是共享内存的物理地址对齐出了问题。所以,隔离是原则,通信是例外,但通信的细节往往是最容易出bug的地方。
4.2 TEE内部组件:TEE Core、TEE Supplicant、TA
好,隔离机制搞清楚了。那TEE这个“安全世界”里,到底住了哪些“居民”呢?
主要有三个角色:
4.2.1 TEE Core(内核)
TEE Core,你可以把它理解成一个微型的、精简的操作系统内核。它负责管理TEE里的所有资源。
- 任务调度: 管理TA的创建、销毁、切换。
- 内存管理: 分配和回收TEE内部的安全内存。
- IPC(进程间通信): 提供TA之间、TA与REE之间的通信机制。
- 密码学服务: 提供硬件加速的加解密、签名、哈希等操作。
我个人习惯把TEE Core比作一个“安全管家”。它不干具体的业务,但所有业务(TA)都得听它指挥。
4.2.2 TEE Supplicant(服务者)
这个角色有点特殊。它运行在REE侧(也就是Android/Linux里),但它是一个“内鬼”,专门为TEE服务的。
为什么需要它?因为TEE本身不能直接访问REE的文件系统、网络等资源。当TA需要读取一个文件,或者访问网络时,它就得通过TEE Core,向REE侧的Supplicant发起请求。
举个例子:
// TA请求读取一个文件
TEE_Result read_file(const char *filename, uint8_t *buffer, size_t *len) {
// 1. TA向TEE Core发送请求
TEE_Result res = TEE_InvokeSupplicant(TEE_SUPPLICANT_OPEN_FILE, filename, ...);
// 2. TEE Core将请求转发给REE侧的Supplicant
// 3. Supplicant在REE侧打开文件,读取内容
// 4. 将内容通过共享内存返回给TEE Core
// 5. TEE Core再将内容返回给TA
return res;
}
你看,这个过程挺绕的。但这是必要的,因为TEE不能直接信任REE的文件系统,必须通过Supplicant这个“中间人”来代理。
避坑指南: 我曾经遇到过一个性能问题,TA频繁地通过Supplicant读写小文件,导致整个系统响应变慢。后来我建议把频繁访问的数据缓存到TEE的安全内存里,性能一下就上来了。所以,Supplicant通信是有开销的,能少用就少用。
4.2.3 TA(可信应用)
TA,就是真正干活的“业务员”。
比如,你的手机支付,会有一个“支付TA”来处理指纹比对和签名。人脸解锁,会有一个“人脸识别TA”来处理特征提取和比对。
TA运行在TEE里,受TEE Core管理。它不能直接访问REE的资源,只能通过TEE Core提供的API来操作。
一个典型的TA结构:
// TA入口函数
TEE_Result TA_CreateEntryPoint(void) {
// 初始化TA
return TEE_SUCCESS;
}
// TA被调用时的处理函数
TEE_Result TA_InvokeCommandEntryPoint(void *sessionContext,
uint32_t commandID,
uint32_t paramTypes,
TEE_Param params[4]) {
switch (commandID) {
case CMD_ENCRYPT:
// 执行加密操作
break;
case CMD_DECRYPT:
// 执行解密操作
break;
default:
return TEE_ERROR_NOT_SUPPORTED;
}
return TEE_SUCCESS;
}
// TA退出函数
void TA_DestroyEntryPoint(void) {
// 清理资源
}
说白了,TA就是一个运行在安全环境里的“小程序”。它只做一件事,并且确保这件事是安全的。
4.3 GP TEE标准简介:江湖规矩
好了,现在你知道TEE里有Core、Supplicant、TA。但问题是,不同厂商(ARM、高通、MTK、华为)的TEE实现都不一样。如果每个厂商都搞一套自己的API,那开发者就疯了。
所以,GlobalPlatform(GP)组织站了出来,制定了一套TEE标准。这套标准定义了:
- TA的编程接口: 也就是TA怎么跟TEE Core交互,怎么调用密码学服务。
- REE与TEE的通信接口: 也就是REE侧的CA(Client Application)怎么调用TA。
- 安全存储、密钥管理等规范。
GP TEE标准,说白了就是一套“江湖规矩”。只要大家都按这个规矩来,你写的TA就能在不同厂商的TEE上运行。
我的经验: 虽然GP是标准,但各家实现总有细微差别。比如,有的厂商对共享内存的大小有限制,有的厂商对TA的加载方式有特殊要求。所以,不要完全迷信“一次编写,到处运行”。在移植TA到新平台时,一定要仔细阅读厂商的TEE开发文档。
下面这张图,可以帮你理清TEE的整体架构和组件关系:
这张图很直观地展示了REE和TEE的“楚河汉界”。左边是复杂的REE世界,右边是精简但安全的TEE世界。中间的灰色隔离墙,就是硬件级别的保护。
好了,这一章的内容就到这里。TEE的基础概念,说白了就是“隔离”和“组件”。隔离是硬件保证的,组件各司其职。GP标准则是让这一切有序运转的规则。理解这些,后面讲ATF与TEE的对接,你才能知道每一步是在干什么。
核心要点回顾:
- TEE与REE通过CPU模式、内存、外设进行硬件隔离。
- TEE Core是安全世界的“管家”,管理所有TA。
- TEE Supplicant是运行在REE侧的“代理”,帮TA访问REE资源。
- TA是运行在TEE里的“业务员”,负责具体的安全功能。
- GP TEE标准定义了统一的接口,让TA可以跨平台运行。
可可精品资料库