背景
OP–TEE中的安全存储是根据 GlobalPlatform 的 TEE 内部核心 API(这里称为可信存储)中定义的内容来实现的。本规范规定,应该可以存储通用数据和关键材料,以保证所存储数据的机密性和完整性以及修改存储的操作的原子性(这里的原子性意味着整个操作要么成功完成,要么不写)。
目前,OP-TEE中有两种安全存储实现:
- 第一个依赖于普通世界(REE)文件系统,默认实现。它在编译时通过 CFG_REE_FS=y 启用。
- 第二种方法利用eMMC设备的重放保护内存块(RPMB)分区,通过设置 CFG_RPMB_FS=y 来启用。
REE 文件系统安全存储
OP-TEE 系统安全文件
Source file | Purpose |
---|---|
core/tee/tee_svc_storage.c | TEE trusted storage service calls |
core/tee/tee_ree_fs.c | TEE file system & REE file operation interface |
core/tee/fs_htree.c | Hash tree |
core/tee/tee_fs_key_manager.c | Key manager |
lib/libutee/ | GlobalPlatform Internal API library |
GlobalPlatform 安全存储要求
以下是规范摘录,列出了最重要的要求:
1. 只要应用了适当的加密保护,可信存储就可以由非安全资源支持,而加密保护必须与保护TEE代码和数据本身的方法一样强大。2. 可信存储必须绑定到特定设备,这意味着数据被创建时只有在同一TEE和同一设备上运行的授权ta才能访问或修改它3. TA本身能够隐藏敏感关键数据4. 每个TA都可以访问其自己的存储空间,该存储空间在该TA的所有实例之间共享,但与其他TA分离。5. 受信任存储必须提供最低级别的保护,以防回滚攻击。可以接受的是,实际的物理存储可能位于不安全的区域,因此容易受到来自TEE外部的操作的影响。通常,一个实现可以依赖于REE来达到这个目的(保护级别100),或者依赖于TEE控制的硬件资产(保护级别1000)。(see GP TEE Internal Core API section 2.5 and 5.2)
如果配置CFG_RPMB_FS=y,则回滚保护由TEE控制,并设置为1000。如果CFG_RPMB_FS=n,则没有针对回滚的保护,并且保护级别设置为0。
Linux 文件系统中的 TEE 文件结构
默认情况下,OP-TEE 使用 /data/tee/ 作为 Linux 文件系统中的安全存储空间。每个持久化对象都分配了一个内部标识符。它是一个整数,在 Linux 文件系统中显示为 /data/tee/<file number> 。
目录文件 /data/tee/dirf.db,列出安全存储中的所有对象。所有正常世界的文件都是完整性保护和加密的,如下所述。
密钥管理器
密钥管理器是 TEE 文件系统中的一个组件,负责处理数据的加密和解密以及对敏感密钥的管理。密钥管理器使用三种类型的密钥:安全存储密钥(Secure Storage Key, SSK)、TA存储密钥(Trusted Application Storage Key, TSK)和文件加密密钥(File Encryption Key, FEK)。
硬件唯一密钥(Hardware Unique Key, HUK)
大多数设备都有某种硬件唯一密钥(HUK),主要用于派生其他密钥。例如,当派生密钥用于安全存储等时,可以使用 HUK 派生。HUK 的重要之处在于它需要得到很好的保护,并且在最好的情况下,HUK 永远不应该直接从软件读取,甚至不应该从安全方面读取。有不同的解决方案,加密加速器可能支持它,或者,它可能涉及另一个安全的协处理器。
在 OP-TEE 中,HUK 只是一个存根,您将看到在 core/include/kernel/TEE_common_otp.h 中名为 TEE_otp_get_hw_unique_key(…)的函数中,在真正安全的产品中,必须用其他东西替换它。如果你的设备缺少对HUK的硬件支持,那么你至少必须将其更改为除零之外的其他内容。但是,请记住,在软件中存储密钥不是一个好的安全做法,尤其是不能将密钥作为其他所有内容的根,因此我们建议您不要这样做。
安全存储密钥(Secure Storage Key, SSK)
SSK是每个设备的密钥,在OP-TEE启动时生成并存储在安全内存中。SSK用于派生TA存储密钥(TSK)。
SSK = HMACSHA256 (HUK, Chip ID || “static string”)
获取硬件唯一密钥(HUK)和芯片ID的功能取决于平台实现。目前,OP-TEE 系统中每台设备只有一把 SSK,用于安全存储子系统。但是,为了将来,我们可能需要为每台设备使用生成 SSK 的相同算法为不同的子系统创建不同的密钥。为不同子系统生成不同的密钥的简单方法是使用不同的静态生成密钥的字符串。
TA存储密钥(Trusted Application Storage Key, TSK)
TSK是每个受信任的应用程序密钥,由SSK和TA的标识符(UUID)生成。它被用来保护FEK,换句话说,用来加密/解密FEK。
TSK = HMACSHA256 (SSK, TA_UUID)
文件加密密钥(File Encryption Key, FEK)
当一个新的TEE文件被创建时,密钥管理器将通过 PRNG(pesudo随机数生成器)为TEE文件生成一个新的 FEK,并将加密的 FEK 存储在 meta 文件中。FEK 用于对存储在 meta 文件中的TEE文件信息或块文件中的数据进行加密/解密。
哈希树
哈希树负责处理安全存储文件的数据加密和解密。哈希树被实现为二叉树,树中的每个节点(struct tee_fs_htree_node_image)保护其两个子节点和一个数据块。元数据(meta data)存储在一个头(struct tee_fs_htree_image)中,它也保护顶层节点。
所有字段(头、节点和块)都使用0和1两个版本进行复制,以确保原子更新。有关详细信息,请参见core/tee/fs_htree.c。
元数据解密流程
当需要更新元数据时,PRNG将生成一个新的 Meta 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;
};
块数据(Block Data)解密流程
当需要更新块数据时,PRNG将生成新的 Block IV。Block IV 的大小在core/include/tee/fs_htree.h中定义。
原子操作
根据原子性的GlobalPlatform可信存储要求,以下操作应支持原子更新:
Write, Truncate, Rename, Create and Delete