📺B站:博主个人介绍
📘博主书籍-京东购买链接*:Yocto项目实战教程
📘加博主微信,进技术交流群:jerrydev
KeyBox 深度解析与实战:Rockchip 系统加密的密钥保险箱
目标:把 KeyBox 的“用途、机制、代码细节、在 System-Encryption 中的位置、不同平台差异”一次讲清楚。读完后,你应该能回答三件事:
KeyBox 存的到底是什么 key
为什么它必须放在 OP-TEE 里存,并且要在 Ramdisk 里读
换成别的平台或别的 SoC 时,哪些能力是“概念通用”,哪些是“实现不通用”
1. 先把场景讲透
在“系统加密”这类方案里,真正棘手的不是 dm-crypt 或 dmsetup 本身,而是密钥放哪里、谁能拿到、什么时候拿到。
典型矛盾是:
系统加密要在早期启动阶段解锁(挂载 rootfs 前),否则系统根本起不来。
早期启动通常发生在Ramdisk/initramfs,这部分代码与环境越小越好,但又必须完成解密。
如果把密钥放在普通文件系统(REE)里:
- 密钥可能被直接读取、复制、离线分析。
- 密钥文件被回滚或替换,会影响加密系统的可信性。
因此,行业通用的设计方向是:
- 密钥长期保存放到可信执行环境(TEE)或专用安全硬件里。
- 密钥短暂使用发生在早期启动阶段,并且严格控制暴露范围与生命周期。
在 Rockchip 的 Linux Secure Boot / System-Encryption 方案中,KeyBox 就是为了解决这件事:
KeyBox = “系统加密密钥的安全落盘 + 启动期取用”的示例实现(CA + TA)。
它的定位不是“算法库”,也不是“加密框架”,而是:
- 一个密钥保险箱服务(KeyBox)
- 服务背后使用OP-TEE Secure Storage(RPMB 或 Security 分区或 REE 文件系统后端)
- 服务前端通过CA/TA接口给 Ramdisk 的解密流程提供密钥
2. KeyBox 到底存的是什么
从你提供的源码可以直接确定:
- KeyBox 在 TA 中持久化保存的对象 ID 是:
"enc_key" - CA 默认读写的数据长度是:
KEY_SIZE = 32字节
也就是说,KeyBox 这个示例主要管理一把32 字节对称密钥材料(256-bit)。
这把 key 在“系统加密”里通常有两种用法:
- 直接作为 dm-crypt/LUKS 的解锁 key(主密钥或口令材料)
- 作为 master key 的原材料,再派生出多个子密钥(例如 rootfs、userdata、model 分区各用一把)
注意:KeyBox 示例只展示了“安全存取”,不负责规划“派生体系”。如果你的产品有多分区、多用途密钥需求,通常需要重构:引入 key-id、版本、用途、派生策略。
3. KeyBox 的整体架构
KeyBox 由两部分组成:
- CA(Client Application):运行在 Linux 用户空间(REE)
- TA(Trusted Application):运行在 OP-TEE 安全世界(TEE)
它们通过 GlobalPlatform TEE Client API(TEEC_*)通信。
下面是一张“读者友好”的总览图:
┌─────────────────────────────────────┐ │ Boot 流程 │ └─────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ Ramdisk /init 早期启动环境 │ │ │ │ 1) 设置 SECURITY_STORAGE=RPMB 或 SECURITY │ │ 2) 运行 keybox_app(CA) │ │ 3) keybox_app 通过 /dev/tee* 调用 TA │ │ 4) TA 从安全存储读取 enc_key │ │ 5) keybox_app 将 key 输出为 /tmp/syspw(hex) │ │ 6) 解密脚本用 dmsetup/dm-crypt 等完成解锁 │ │ 7) switch_root 进入真实系统 │ └──────────────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ OP-TEE Secure Storage │ │ - RPMB:eMMC 受保护分区 │ │ - Security 分区:用于无 RPMB 的替代方案 │ │ - REE FS:/metadata/tee 或 /data/vendor/tee 等目录 │ │ enc_key 以持久化对象方式保存 │ └──────────────────────────────────────────────────────────────┘关键点:
- KeyBox 本质是“把 enc_key 变成一个 TA 持久化对象”。
- CA 只是“搬运工”,TA 才是“保险箱”。
4. 关键概念速查
为了读懂 KeyBox,你只需要掌握这些概念(按重要性排序):
| 概念 | 你需要记住的结论 | 和 KeyBox 的关系 |
|---|---|---|
| OP-TEE | TrustZone 上的 TEE 实现,支持 TA 与安全存储 | KeyBox 的 TA 跑在这里 |
| CA/TA | CA 在 REE,TA 在 TEE,通过 TEEC 调用 | KeyBox 由 CA+TA 组成 |
| Secure Storage | TA 可把数据加密后持久化保存 | enc_key 的落盘位置 |
| RPMB | eMMC 的受保护区域,通常安全性更强 | KeyBox 可把 enc_key 存到 RPMB |
| Security 分区 | 无 RPMB 场景下的替代安全分区 | KeyBox 也能使用它 |
| REE FS 后端 | 由 tee-supplicant 在普通文件系统落盘 | KeyBox 代码里映射为 TEE_STORAGE_PRIVATE_REE |
| Ramdisk | 早期启动环境,负责解密/验签 | KeyBox “读 key”主要在这里发生 |
5. 读懂接口:UUID 与四个命令
ta_keybox.h定义了这套 TA 的标识与命令号:
TA UUID:
8c6cf810-685d-4654-ae71-8031beee467e命令:
TA_KEY_READ= 1TA_KEY_WRITE= 2TA_KEY_RNG= 3TA_KEY_VER= 4
你可以把它理解为:
- READ/WRITE:真正的“保险箱读写”
- RNG/VER:一个简化的“握手校验”流程
下面表格把 CA 与 TA 的参数类型也对应起来:
| 命令 | CA 调用方式 | TA 期望参数 | 作用 |
|---|---|---|---|
| TA_KEY_RNG | 申请共享内存输出 | MEMREF_OUTPUT | TA 返回随机数,同时在 TA 内部计算 g_hash |
| TA_KEY_VER | VALUE_INPUT | VALUE_INPUT | CA 回传 hash,TA 校验后置位 g_ver |
| TA_KEY_READ | MEMREF_OUTPUT + storage type | MEMREF_OUTPUT + VALUE_INPUT | 读取 enc_key 输出到共享内存 |
| TA_KEY_WRITE | TEMP_INPUT + storage type | MEMREF_INPUT + VALUE_INPUT | 写入 enc_key 到安全存储 |
这个设计的逻辑是:TA_KEY_READ 默认要求先通过 RNG/VER,否则会走“返回随机数”的分支。
6. TA 侧实现详解
TA 源码keybox.c是理解 KeyBox 的核心,因为真正的“安全存储”都在这里发生。
6.1 持久化对象的名字与句柄
TA 里固定了对象 ID:
uint8_t id[] = "enc_key";
这意味着它在 secure storage 中保存的是一个名为 enc_key 的对象。
写入时通过TA_TouchtHandle():
- 先
TEE_OpenPersistentObject() - 失败则
TEE_CreatePersistentObject()
这是一种典型的“存在则打开,不存在则创建”的逻辑。
6.2 存储后端由 CA 传入
TA 并不硬编码使用 RPMB 还是 Security,而是用params[1].value.a作为 storage type。
CA 侧通过环境变量决定:
SECURITY_STORAGE=RPMB→TEE_STORAGE_PRIVATE_RPMBSECURITY_STORAGE=SECURITY→TEE_STORAGE_PRIVATE_REE
这也是为什么 Rockchip 文档里会强调:init 脚本会根据设备与方案替换 SECURITY_STORAGE。
6.3 RNG 与回退策略
TA 获取随机数使用:
- 优先:
rk_get_trng(buf, size) - 若硬件不支持 TRNG:用
rk_derive_ta_unique_key()做回退
这说明两个事实:
- KeyBox 依赖平台的 TRNG 能力(最好有)。
- 回退策略并不等价于强随机,只是为了“有一个可用的随机源”。
6.4 g_hash 与 g_ver 的意义
TA 中有两个全局变量:
static uint32_t g_hash = 0;static uint32_t g_ver = 0;
流程是:
- CA 调用 TA_KEY_RNG,TA 返回随机数,同时计算
g_hash。 - CA 把随机数做
js_hash()(同一算法)得到值 A。 - CA 调用 TA_KEY_VER 把 A 传回,TA 对比是否等于 g_hash。
- 通过则
g_ver = 1。
然后 TA_KEY_READ 会判断:
- 如果
g_ver == 0:直接返回随机数(不读持久化对象) - 如果
g_ver == 1:才去打开enc_key并读出数据
这是一种“握手后才能读”的最小化示例,但它不是强安全认证。
6.5 TA 的读写逻辑
- 写入:不依赖 g_ver,任何能打开会话的 CA 都可以写 enc_key。
- 读取:依赖 g_ver,且依赖对象存在。
这与 Rockchip 文档中的“建议重构”一致:示例代码为了易用性留了空间,但产品级方案需要加强。
7. CA 侧实现详解
CA 源码main.c看起来很长,核心其实只有两件事:
- 通过 TEEC 调 TA 完成 READ/WRITE
- 把 32 字节 key 与
/tmp/syspw(64 字节 hex 字符串)互相转换
7.1 运行模式:ramfs 模式与 recovery 模式
CA 在 main 里用两个条件分支区分运行场景:
- ramfs 模式:用于启动阶段“读取密钥并输出”
- recovery 模式:用于“从 /tmp/syspw 导入密钥并写入安全存储”
判断逻辑:
- 如果不是 init 的子进程(
getppid() != 1)或者带参数:走 recovery - 否则:尝试确认当前根是否来自 ramdisk,再走 ramfs
可以把它抽象成:
if (早期启动环境) { key = KeyBox_Read_From_TEE(); /tmp/syspw = hex(key); } else { key = unhex(/tmp/syspw); KeyBox_Write_To_TEE(key); }7.2 /tmp/syspw 的作用
为什么不是把二进制 key 直接写到文件?
因为 early boot 脚本与工具链更容易处理“可打印字符串”,所以 CA 把 key 写成:
- 32 字节二进制 → 64 字节 ASCII hex
这样 shell 脚本或解密工具可以更容易接入。
但这同时意味着:密钥会以可见字符串形式在 Ramdisk 中短暂存在,所以生命周期控制很关键。
7.3 storage type 的选择
CA 通过环境变量决定后端:
SECURITY_STORAGE=RPMB:走 RPMBSECURITY_STORAGE=SECURITY:走 REE private storage(在 Rockchip 方案里常映射到 Security 分区或 REE FS)
这也是为什么文档中 init 脚本会用 sed 替换该变量。
8. KeyBox 在 System-Encryption 的位置
Rockchip 的安全方案通常把“验签/解密”放到 Ramdisk 的/init中完成,之后 switch_root 进入真实系统。
KeyBox 的位置就在“系统解密”的关键一步:
[boot.img] = Kernel + Ramdisk Ramdisk /init: 1) 挂载 /proc /sys 等 2) 判断 A/B 槽位 3) 运行 KeyBox(读出 enc_key → /tmp/syspw) 4) 使用 dmsetup/dm-crypt 解密目标分区,生成 vroot 5) switch_root 进入 vroot你可以把 KeyBox 理解为:
- 系统加密链路中的“密钥供给点”
- 它不负责加密算法,也不负责 dmsetup 参数
- 它只负责在正确时机,把正确密钥送到正确地方
9. 安全存储后端怎么选
Rockchip 的 OP-TEE 安全存储在实践中大致分三类(本质上是“TA 的 storageID + 平台能力”组合):
- RPMB:
TEE_STORAGE_PRIVATE_RPMB - Security 分区:通常通过
TEE_STORAGE_PRIVATE_REE+ 特定配置映射到 security 分区 - REE 文件系统后端:同样是
TEE_STORAGE_PRIVATE_REE,但落到 /metadata/tee 或 /data/vendor/tee 等路径
下面用一张表把差异讲清楚:
| 后端 | 典型硬件前提 | 优点 | 代价与风险 | 适用建议 |
|---|---|---|---|---|
| RPMB | eMMC 且启用 RPMB 驱动 | 抗篡改能力更强、断电鲁棒性通常更好 | 容量小,依赖 eMMC 与 RPMB key 绑定机制 | 重要密钥优先选 RPMB |
| Security 分区 | 无 RPMB 或方案要求 | 可在 NAND/非 eMMC 场景实现“类似安全区” | 可能被强制擦除,依赖分区规划 | 作为无 RPMB 的替代 |
| REE FS 后端 | tee-supplicant 可访问目录 | 容量不受限,调试方便 | 易被删除/回滚,强依赖上层完整性策略 | 适合开发调试或低安全级产品 |
关键结论:KeyBox 的“接口”统一,但“安全强度”由后端决定。产品化必须把“后端选择 + 完整性策略 + 反回滚”作为整体设计。
10. 不同 Rockchip 平台是否都有 KeyBox
这里要区分两个层面:
- 概念层面:只要有 OP-TEE + Secure Storage,就可以实现 KeyBox 类服务。
- SDK 提供层面:Rockchip 在某些 Linux Secure Boot / System-Encryption 方案里提供了 KeyBox 示例代码,但它不是所有产品默认必选项。
从 OP-TEE 资源与平台列表看,Rockchip 在多个 SoC 上都有 OP-TEE 支持(不同 SoC 的 TEE 内存配置不同)。
因此可以给出一个更贴近工程的结论:
- 如果该平台的 SDK 安全方案包含 System-Encryption,并启用了 OP-TEE 环境,通常就会带上或可集成 KeyBox。
- 如果只做 System-Verity(验签)而不做加密,KeyBox 不一定需要。
- 如果没有 eMMC RPMB,也并不等于不能用 KeyBox,只是后端会落到 Security 分区或 REE FS。
建议你在具体 SDK 中用三步判断“你的平台是否具备 KeyBox 运行条件”:
- Kernel DTS 是否启用 OP-TEE 节点
- 用户空间是否有 tee-supplicant 与 /dev/tee* 节点
- 分区表是否具备 RPMB 或 security 分区(或 REE FS 后端目录是否可用)
11. 不同厂商平台是否都有 KeyBox
如果把视角扩大到“不同 SoC 厂商”,答案是:
- KeyBox 这个名字不是通用标准。
- “安全保存系统加密密钥”这个需求是通用的。
也就是说:
其他平台通常会提供“相同目的”的组件,但名称与接口完全不同。
下面用对比表让结论更直观:
| 平台生态 | 常见做法 | 与 KeyBox 的关系 |
|---|---|---|
| Rockchip Linux + OP-TEE | 提供 KeyBox 示例 CA/TA,结合 System-Encryption | 名称与代码直接对应 |
| Android 生态 | 使用 Keystore/KeyMint/Keymaster 类服务 | 目标一致,接口体系不同 |
| 其他 Linux SoC | 自研 CA/TA 或使用 TPM/Sealed Storage | 目标一致,组件形式不同 |
| 安全硬件导向平台 | TPM/SE/HSM 保存密钥,OS 通过标准接口取用 | “保险箱”在外设或芯片内安全模块 |
把它抽象到最高层,你会发现 KeyBox 解决的问题可以归纳成一个公式:
系统加密可用性 = 早期启动能拿到 key 系统加密安全性 = key 长期保存不暴露 + 取用过程可控只要一个平台能满足这两点,就“有 KeyBox 这件事”;只是它不一定叫 KeyBox。
12. 必须讲清楚的安全边界
KeyBox 是示例代码,它的默认实现有一些“可用性优先”的取舍。产品化时必须补齐边界。
12.1 默认实现的风险点
| 风险点 | 源码表现 | 影响 |
|---|---|---|
| 写入不受控 | TA_KEY_WRITE 不依赖 g_ver,且会话为 PUBLIC | 可能被非预期程序写入错误 key |
| 读取握手较弱 | js_hash 不是强认证机制 | 不适合对抗强攻击者 |
| /tmp/syspw 暴露 | 早期启动阶段把 key 明文 hex 落到 /tmp | Ramdisk 环境泄露会导致 key 泄露 |
| 失败处理不够“严格” | 文档提到交互出错时可能输出随机 key | 容易让系统进入不可预测状态 |
12.2 产品化改造建议
可以按优先级做增强:
对 TA 文件做签名,阻止第三方加载自定义 TA。
限制 CA 运行环境:只允许在 Ramdisk + PID=1 + 已验证启动链条件下读取。
CA/TA 加强认证:用设备唯一身份、会话上下文、nonce、计数器等替代简化 hash。
把 key 的生命周期缩到最短:
- 使用后立即清理 /tmp/syspw
- 尽量避免把 key 变成可打印字符串
- 能否改为通过管道/内存传递给解密工具
引入版本与反回滚策略:当你使用 REE FS 或 security 分区时,必须考虑“回滚旧对象”的风险。
13. 编译集成与调试要点
这一节的目标是给你一个“工程师视角的检查清单”。
13.1 代码位置与构建关系
KeyBox 的示例目录结构通常类似:
buildroot/package/rockchip/tee-user-app/extra_app/host:CAbuildroot/package/rockchip/tee-user-app/extra_app/ta:TA
构建时要注意两条编译链:
- CA 跟随用户空间架构(常见为 aarch64)
- TA 跟随 BL32/OP-TEE 架构(常见为 aarch32)
13.2 内核与设备树
- Kernel 需要启用 OP-TEE 支持
- DTS 中要有 optee 节点,并且状态为 okay
13.3 运行时检查
在板端按顺序检查:
/dev/tee0或/dev/teepriv0是否存在tee-supplicant是否可运行- 如果使用 RPMB:
/dev/mmcblkXrpmb是否存在 - 如果使用 Security 分区:是否有对应 by-name 节点
- 环境变量
SECURITY_STORAGE是否在 Ramdisk /init 中正确设置
14. 最后用两张流程图收束全篇
14.1 密钥灌装与启动取用流程
┌──────────────┐ ┌──────────────┐ │ Recovery/工厂 │ │ Ramdisk 启动 │ └──────┬───────┘ └──────┬───────┘ │ │ │ 1) 准备 /tmp/syspw │ │ (64字节hex) │ │ │ ▼ │ ┌──────────────┐ │ │ keybox_app │ │ │ 写入模式 │ │ └──────┬───────┘ │ │ 2) TEEC -> TA_KEY_WRITE │ ▼ │ ┌──────────────┐ │ │ OP-TEE TA │ │ │ 保存 enc_key │ │ └──────────────┘ │ │ │ 3) TEEC -> RNG/VER/READ ▼ ┌────────────────┐ │ keybox_app │ │ 读取模式 │ └───────┬────────┘ │ 4) 输出 /tmp/syspw ▼ ┌────────────────┐ │ dmsetup/dm-crypt│ │ 解锁 rootfs │ └────────────────┘14.2 CA 与 TA 的交互时序
CA(keybox_app) TA(keybox TA) | | | TEEC_OpenSession(UUID) | |---------------------------------------->| | | | TA_KEY_RNG (MEMREF_OUTPUT) | |---------------------------------------->| |<----------------------------------------| | rng bytes + TA 内部计算 g_hash | | | | TA_KEY_VER (VALUE_INPUT=hash(rng)) | |---------------------------------------->| | TA 校验通过,置 g_ver=1 | | | | TA_KEY_READ (MEMREF_OUTPUT, storageID) | |---------------------------------------->| | OpenPersistentObject("enc_key") | | ReadObjectData -> key | |<----------------------------------------| | key bytes | | | | hex(key) -> /tmp/syspw |15. 结语与落地建议
如果你要把 KeyBox 真正用到产品里,可以用三句话作为设计准绳:
- KeyBox 管的不是“加密算法”,而是“系统加密密钥的生命周期”。
- 选择 RPMB/Security/REE FS 的本质是在选“安全强度与工程代价”。
- 示例能跑不等于能上量产,必须按威胁模型重构访问控制与失败策略。
如果你希望下一步把这篇文章进一步“落到可复现”,我建议你把你 Ramdisk 的/init解密流程片段(含 dmsetup 参数、A/B 判断、SECURITY_STORAGE 设置)贴出来,我们可以把 KeyBox 与 System-Encryption 的拼装点逐行对齐,形成一套“从刷机灌装到启动解锁”的完整闭环笔记。
📺B站:博主个人介绍
📘博主书籍-京东购买链接*:Yocto项目实战教程
📘加博主微信,进技术交流群:jerrydev