本文主要介绍OPTEE的安全存储技术,翻译自官方文档:
Secure storage — OP-TEE documentation documentation (optee.readthedocs.io)
一、背景
OP-TEE中的安全存储是根据GlobalPlatform的TEE Internal Core API(这里称为可信存储)中定义的内容实现的。此规范要求应该能够存储通用数据和密钥材料,以保证所存储数据的机密性和完整性以及修改存储的操作的原子性(这里的原子性意味着整个操作成功完成或不完成写入)。
OP-TEE 中目前有两种安全存储实现:
第一个依赖于正常世界 (REE) 文件系统。它在本文档中进行了描述,并且是默认实现。它在编译时由 CFG_REE_FS=y 启用。
第二个使用eMMC设备的重放保护内存块(RPMB)分区,并通过设置CFG_RPMB_FS=y启用。RPMB 安全存储中对此进行了描述。
可以同时使用正常文件系统和 RPMB 实现。为此,定义了两个特定于 OP-TEE 的存储标识符:TEE_STORAGE_PRIVATE_REE 和 TEE_STORAGE_PRIVATE_RPMB。根据编译时配置,可以使用一个或多个值。TEE_STORAGE_PRIVATE值在可用时选择 REE FS,否则选择 RPMB FS(按此顺序)。
二、REE FS安全存储
安全存储系统体系结构
OP-TEE 操作系统中的源文件:
源文件 | 目的 |
---|---|
core/tee/tee_svc_storage.c | TEE 可信存储服务调用 |
core/tee/tee_ree_fs.c | TEE 文件系统和 REE 文件操作界面 |
core/tee/fs_htree.c | 哈希树 |
core/tee/tee_fs_key_manager.c | 密钥管理器 |
lib/libutee/ | 全球平台内部 API 库 |
1、基本文件操作流程
当 TA 调用 GP 可信存储 API 提供的写入函数将数据写入持久对象时,会调用 TEE 可信存储服务中实现的相应系统调用,进而调用一系列 TEE 文件操作来存储数据。然后,TEE 文件系统将对数据进行加密,并通过一系列 RPC 消息将 REE 文件操作命令和加密数据发送给 TEE 请求方。TEE 请求方将接收消息并将加密数据存储到 Linux 文件系统中。读取文件的处理方式类似。
2、全球平台可信存储要求
以下是规范的摘录,列出了最重要的要求:
1. 受信任的存储可能由非安全资源支持,只要应用了适当的加密保护,其强度必须与用于保护 TEE 代码和数据本身的方法。
2. 可信存储必须绑定到特定设备,这意味着它必须只能由授权的助教访问或修改在与数据相同的 TEE 和同一设备上运行创建。
3. 能够对 TA 本身隐藏敏感密钥材料。
4. 每个 TA 都可以访问自己的存储空间,该存储空间由所有该 TA 的实例,但与其他 TA 分开。
5. 受信任的存储必须提供最低级别的保护回滚攻击。可以接受的是,实际的物理存储可能位于不安全区域,因此容易受到来自TEE之外。通常,实现可能依赖于 REE用于此目的(保护级别 100)或硬件资产由 TEE 控制(防护等级 1000)。
(请参阅 GP TEE 内部核心 API 第 2.5 和 5.2 节)
如果配置为 CFG_RPMB_FS=y,则防回滚保护由 TEE 控制,设置为 1000。如果 CFG_RPMB_FS=n,则没有针对回滚的保护,并且保护级别设置为 0。
3、Linux 文件系统中的 TEE 文件结构
OP-TEE 默认使用 /data/tee/ 作为 Linux 文件系统中的安全存储空间。每个持久性对象都分配有一个内部标识符。它是一个整数,在 Linux 文件系统中显示为 /data/tee/<file number>。
目录文件 /data/tee/dirf.db 列出了安全存储中的所有对象。所有正常世界文件都受到完整性保护和加密,如下所述。
三、密钥管理器
密钥管理器是 TEE 文件系统中的一个组件,负责处理数据加密和解密以及敏感密钥材料的管理。密钥管理器使用三种类型的密钥:安全存储密钥 (SSK)、TA 存储密钥 (TSK) 和文件加密密钥 (FEK)。
1、安全存储密钥 (SSK)
SSK 是每个设备的密钥,在 OP-TEE 启动时生成并存储在安全内存中。SSK 用于派生 TA 存储密钥 (TSK)。
SSK派生自
获取硬件唯一密钥 (HUK) 和芯片 ID 的函数取决于平台实现。目前,在 OP-TEE OS 中,我们只有一个用于安全存储子系统的每设备密钥 SSK,但是,将来我们可能需要使用生成 SSK 时相同的算法为不同的子系统创建不同的每设备密钥;为不同子系统生成不同的每设备密钥的一种简单方法是使用不同的静态字符串来生成密钥。
2、受信任的应用程序存储密钥 (TSK)
TSK 是每个受信任的应用程序密钥,由 SSK 和 TA 的标识符 (UUID) 生成。它用于保护FEK,换句话说,加密/解密FEK。
TSK派生自:
3、文件加密密钥 (FEK)
创建新的 TEE 文件时,密钥管理器将通过 PRNG(pesudo 随机数生成器)为 TEE 文件生成新的 FEK,并将加密的 FEK 存储在元文件中。FEK用于加密/解密存储在元文件中的TEE文件信息或存储在块文件中的数据。
四、哈系树
哈希树负责处理安全存储文件的数据加密和解密。哈希树被实现为二叉树,其中树中的每个节点(下面的结构tee_fs_htree_node_image)保护其两个子节点和一个数据块。元数据存储在标头(下面的结构tee_fs_htree_image)中,该标头也保护顶部节点。
所有字段(标头、节点和块)都与两个版本(0 和 1)重复,以确保原子更新。有关详细信息,请参阅core/tee/fs_htree.c。
1、元数据加密流程
元数据加密
当元数据需要更新时,PRNG 将生成一个新的元 IV。meta IV 的大小在 core/include/tee/fs_htree.h 中定义,元数据和节点数据的数据结构在 fs_htree.h 中定义如下:
struct tee_fs_htree_node_image {
uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
uint8_t iv[TEE_FS_HTREE_IV_SIZE];
uint8_t tag[TEE_FS_HTREE_TAG_SIZE];
uint16_t flags;
};
struct tee_fs_htree_meta {
uint64_t length;
};
struct tee_fs_htree_imeta {
struct tee_fs_htree_meta meta;
uint32_t max_node_id;
};
struct tee_fs_htree_image {
uint8_t iv[TEE_FS_HTREE_IV_SIZE];
uint8_t tag[TEE_FS_HTREE_TAG_SIZE];
uint8_t enc_fek[TEE_FS_HTREE_FEK_SIZE];
uint8_t imeta[sizeof(struct tee_fs_htree_imeta)];
uint32_t counter;
};
2、块数据加密流程
块数据加密
当需要更新区块数据时,PRNG 将生成一个新的区块 IV。块IV的大小在core/include/tee/fs_htree.h中定义。
五、原子操作
根据GP可信存储规范的原子性要求,以下操作应支持原子更新:
写入、截断、重命名、创建和删除
OP-TEE安全存储中用于保证原子性的策略是异地更新。
六、RPMB 安全存储
本文介绍 OP-TEE 中的 RPMB 安全存储实现,通过设置 CFG_RPMB_FS=y 启用。 受信任的应用程序可以通过传递等于 TEE_STORAGE_PRIVATE_RPMB 的存储 ID 来使用此实现,如果禁用CFG_REE_FS则传递TEE_STORAGE_PRIVATE。有关RPMB的详细信息,请参阅JEDEC eMMC规范(JESD84-B51)。
架构如下图所示:
| NORMAL WORLD : SECURE WORLD |
:
U tee-supplicant : Trusted application
S (rpmb.c) : (secure storage API)
E ^ ^ : ^
R | | : |
~~~~~~~ ioctl ~~~~~~~|~~~~~~~~~~~~:~~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~
K | | : OP-TEE
E v v : (tee_svc_storage.c)
R MMC/SD subsys. OP-TEE driver : (tee_rpmb_fs.c, tee_fs_key_manager.c)
N ^ ^ : ^
E | | : |
L v | : |
Controller driver | : |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~
v v
Secure monitor / EL3 firmware
有关 Linux 内核中 MMC/SD 子系统的 ioctl() 接口的信息,请参阅 Linux core MMC 头文件 linux/mmc/core.h 和 mmc-utils 存储库。
1、安全存储 API
这部分在基于 REE 的文件系统中是通用的。core/tee/tee_svc_storage.c 中的系统调用与 RPMB 文件系统之间的接口是tee_file_operations,即结构tee_file_ops。
2、RPMB 文件系统
FS 实现完全在 core/tee/tee_rpmb_fs.c 中,RPMB 分区分为三个部分:
前 128 个字节保留用于分区数据(结构 rpmb_fs_partition)。
偏移量 512 处是文件分配表 (FAT)。它是一个结构rpmb_fat_entry元素数组,每个文件一个。FAT 随着文件添加到文件系统而动态增长。除此之外,每个条目都有文件数据的开始地址、大小和文件名。
从 RPMB 分区的末尾开始向下延伸是文件数据区域。
分区中的空间由通用分配器函数 tee_mm_alloc(...) 和 tee_mm_alloc2(...) 分配。
所有文件操作都是原子操作。这要归功于以下属性:
根据 eMMC 规范,将单个数据块写入 RPMB 分区是原子的。
在成功写入数据后,修改文件的 FAT 块始终最后更新。
仅当数据跨度不超过“可靠写入块计数”块时,才会就地更新文件内容。否则,或者如果需要扩展文件,则会创建一个新文件。
3、设备访问
OP-TEE 中没有 eMMC 控制器驱动程序。设备操作都必须经过正常世界。它们由 tee 请求进程处理,该进程进一步依赖于内核的 ioctl() 接口来访问设备。三通请求方还具有仿真模式,该模式可实现用于测试目的的虚拟 RPMB 设备。
RPMB 操作如下:
读取设备信息(分区大小、可靠的写入块计数)。
对安全密钥进行编程。此密钥用于身份验证目的。请注意,它与下面定义的用于加密的安全存储密钥 (SSK) 不同。但是,与 SSK 一样,安全密钥也派生自硬件唯一密钥或标识符。
目前,函数 tee_otp_get_hw_unique_key() 用于生成 RPMB 安全密钥。
读取写入计数器值。写入计数器在读取和写入请求期间用于 HMAC 计算。该值在初始化时读取,并存储在结构tee_rpmb_ctx中,即rpmb_ctx->wr_cnt。
读取或写入数据块。
RPMB 操作是根据 FS 层的请求启动的。请求和响应的内存缓冲区使用 thread_rpc_alloc_payload(...) 在共享内存中分配。缓冲区在TEE_RPC_RPMB_CMD消息中传递到正常世界,这要归功于 thread_rpc_cmd() 函数。大多数 RPMB 请求和响应使用 JEDEC eMMC 规范定义的数据帧格式。HMAC 身份验证也在此处实现。
4、安全注意事项
在设备上对密钥进行编程之前,无法访问 eMMC 中的 RPMB 分区:这是设备生命周期内的一次性操作。在 eMMC 控制器上写入密钥后,控制器将使用它对请求进行身份验证。
可以通过多种方式对此密钥进行编程。如果您希望OP-TEE执行此操作,则必须将OP-TEE配置为CFG_RPMB_WRITE_KEY=y。
警告:注意,此配置会将 RPMB 密钥明文发送到将 RPMB 密钥编程请求中继到 eMMC 硬件设备的非安全端。
OP-TEE 可以嵌入内置的 RPMB 密钥 (CFG_RPMB_TESTKEY=y),也可以从特定于平台的机密 (CFG_RPMB_TESTKEY=n) 派生它。前一种情况在开发过程中可能很有用,而后一种情况则建议用于生产设备。
从机密派生密钥可避免 OP-TEE 将其存储在内存中,从而减少攻击面;OP-TEE 从包含 eMMC 序列号的内部集派生 RPMB 密钥,更重要的是硬件唯一密钥 (HUK)。
要使此配置生效,硬件唯一密钥(SoC 特定实例化的唯一标识符)不得公开访问(请注意,并非所有平台都可能强制实施此要求)。
保持 HUK 机密的需要是为什么在安全感知系统上,硬件将根据平台的安全状态生成不同的 HUK 值的原因:换句话说,SoC 将生成不同的 HUK,具体取决于是否将 BOOT ROM 配置为启动签名映像。
但请注意,由于 RPMB 密钥只能在控制器上写入一次,因此在保护电路板之前访问 RPMB 将导致在保护板后拒绝未来的 RPMB 访问。为了防止这种情况发生,OP-TEE提供了一个软件钩子,平台应使用该钩子来实现其安全逻辑plat_rpmb_key_is_ready()。
警告:为了使 OP-TEE 能够写入 RPMB 密钥,必须配置 CFG_RPMB_WRITE_KEY=y,并且 plat_rpmb_key_is_ready() 必须在运行时允许它。
5、加密
FS 加密例程位于 core/tee/tee_fs_key_manager.c 中。块加密可保护文件数据。该算法是密码块链 (CBC) 模式下的 128 位 AES,具有加密盐扇区初始化向量 (ESSIV),有关详细信息,请参阅 CBC-ESSIV。
在 OP-TEE 初始化期间,128 位 AES 安全存储密钥 (SSK) 派生自硬件唯一密钥 (HUK)。它保存在安全内存中,永远不会写入磁盘。受信任的应用程序存储密钥派生自 SSK 和 TA UUID。
对于每个文件,在创建文件时随机生成一个 128 位加密文件加密密钥 (FEK),使用 TSK 加密并存储在文件的 FAT 条目中。
然后,每个 256 字节的数据块在 CBC 模式下加密。初始化向量是通过ESSIV算法获得的,即通过FEK的哈希对块号进行加密。这允许直接访问文件中的任何块,如下所示:
FEK = AES-Decrypt(TSK, encrypted FEK);
k = SHA256(FEK);
IV = AES-Encrypt(128 bits of k, block index padded to 16 bytes)
Encrypted block = AES-CBC-Encrypt(FEK, IV, block data);
Decrypted block = AES-CBC-Decrypt(FEK, IV, encrypted block data);
SSK、TSK 和 FEK 处理在基于 REE 的安全存储中很常见,而 AES CBC 块加密仅用于 RPMB(REE 实现使用 GCM)。FAT 未加密。
6、REE FS 哈希状态
如果同时配置了CFG_REE_FS=y和CFG_RPMB_FS=y,REE FS将在RPMB中创建一个特殊文件dirfile.db.hash,其中包含一个表示REE FS状态的哈希。
七、重要注意事项
警告
目前,某些OP-TEE平台不支持检索安全操作所需的硬件唯一密钥或芯片ID。对于这些平台,使用常量密钥,导致无法防止解密或安全存储复制到其他设备。这是因为有关如何从 SoC 检索密钥数据的信息被某些供应商视为敏感信息,并且不公开。
要允许安全存储在您的平台上安全运行,您必须在平台代码中定义以下实现:
void tee_otp_get_hw_unique_key(struct tee_hw_unique_key *hwkey);
int tee_otp_get_die_id(uint8_t *buffer, size_t len);
这些实现应根据 SoC 供应商定义的方法从特定于 SoC 的电子保险丝或加密单元中提取密钥数据。
八、参考资料
有关安全存储的更多信息,请参阅演示文稿(Presentations — OP-TEE documentation documentation (optee.readthedocs.io))中的 SFO15-503、LAS16-504、SFO17-309 和 TEE Internal Core API 规范GlobalPlatform API — OP-TEE documentation documentation (optee.readthedocs.io))。