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 (安全世界) 硬件隔离墙 (Monitor Mode) CA (Client Application) 例如:支付App、人脸解锁App TEE Supplicant REE侧的“内鬼”,代理TA请求 REE Kernel (Linux/Android) 非安全世界的操作系统 TA (Trusted Application) 例如:支付TA、人脸识别TA TEE Core (安全内核) 安全世界的“管家” 安全硬件 (密码引擎/安全存储) 硬件级安全能力 GP TEE Client API 共享内存通信 REE组件 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可以跨平台运行。

可可精品资料库