📺 B站视频讲解(Bilibili):https://www.bilibili.com/video/BV1k1C9BYEAB/
📘 《Yocto项目实战教程》京东购买链接:Yocto项目实战教程
OP-TEE HelloWorld 文件保护实战:把一份 helloworld.txt 交给 Secure World 保管(EKB → PTA → CA)
目标:你亲手跑通一个最小闭环:
Linux 侧只看到“密文文件”,无法直接拿到密钥;只有通过你写的 CA 调用 Secure World 的 PTA,才能把文件解密回来。
平台:Jetson Orin(t234),Yocto 镜像(含 OP-TEE、tee-supplicant、xtest),EKB 存在 EKS 分区(例如
A_eks/eks.img)。
0. 你要跑通的“链路”到底是什么
0.1 两个世界(你只要记住这张图)
Normal World (Linux)
CA: jcl-tee-crypt -> libteec.so (optee-client)
| TEEC_OpenSession / TEEC_InvokeCommand
v
--------------------------------------------------- SMC 切换
Secure World (OP-TEE OS)
PTA: pta_hello_crypt (内置在 OP-TEE OS)
| 从 EKB 取 seed -> KDF 派生 work_key
| AES-256-CBC/GCM 加/解密 buffer
v
返回密文/明文(只返回数据,不返回 key)
0.2 本工程的“验收标准”
你完成后必须做到:
- 能生成并刷入一个最小 EKB(EKS 镜像)。
- 能在板子上确认 OP-TEE 正在跑(
tee-supplicant、xtest)。 - 你写的 CA 能成功调用你写的 PTA。
- Linux 侧文件是密文:
cat helloworld.enc看不到明文。 - 只有通过 CA 才能解密回
hello world。 - CA 代码里不出现任何明文 key/seed。
1. 先把“最容易混的概念”一次讲清
1.1 这次工程只用对称加密
| 你关心的问题 | 本工程用什么 | 说明 |
|---|---|---|
| 文件内容保密(别人拷走磁盘也看不懂) | 对称加密(AES) | 加/解密用同一把 key(在 Secure World 内) |
| 防篡改 / 验证可信(像 OTA、Secure Boot) | 非对称签名(RSA/ECDSA) | 这次工程不做(避免分心) |
结论:HelloWorld 文件保护 = 对称加密。你不需要公钥/私钥。
1.2 EKB 是什么(够用版)
EKB(Encrypted Key Blob)= 一个“装着多个业务 key/seed 的加密文件”。
- 它存在 EKS 分区里(例如
A_eks,xml 里常见filename>eks.img</filename>)。 - 启动时由 OP-TEE(Secure World)解密/验签,然后把里面的 key/seed 放到 Secure World 内存中。
- Linux 不能直接拿到这些 key。

1.3 PTA 是什么(够用版)
PTA(Pseudo TA)= OP-TEE OS 内置的可信服务。
- 它运行在 Secure World,不在 Linux。
- 你可以写自己的 PTA,提供“加密/解密”这种能力。
- CA(Linux 应用)通过
libteec调用 PTA。
你这次要做的,就是:写一个只服务于 HelloWorld 的最小 PTA + 最小 CA。
2. 阶段 1:确认 OP-TEE 真的在跑(你已经基本做完,但这里给“标准动作”)
这一阶段的目的只有一个:证明 Secure World 的 OP-TEE 运行正常。
2.1 检查 tee-supplicant
systemctl status tee-supplicant
你要看到:Active: active (running)。
2.2 检查 OP-TEE TA 存放目录
常见路径(Jetson L4T/Yocto 配置不同,但你这台已经有):
ls /lib/optee_armtz/
2.3 跑 xtest(最直接证据)
xtest
- 有些测试可能 skip(比如 pseudo TA 不在)是正常的。
- 关键是:能跑起来并执行一批测试,而不是
Failed to open TEE context。
你已经从 “0xffff0008 无法打开 context” 走到了 “xtest 能跑一堆用例”,这说明 OP-TEE 基础链路 OK。
3. 阶段 2:跑通“最小 EKB 闭环”(生成 + 刷入 + 启动生效)
这一阶段目标:你能生成一个 EKS 镜像(eks_t234.img / eks.img),并确保刷到 EKS 分区。
3.1 找到 gen_ekb.py(你已找到)
你已经定位到了:
.../nvidia-jetson-optee-source/optee/samples/hwkey-agent/host/tool/gen_ekb/gen_ekb.py
结论:
- 这个脚本在 samples 目录里很正常,它是“生成 EKB 的工具”,不是运行在板子上的东西。
- 你可以把它单独拷出来使用(更清晰)。
3.2 准备 Python 依赖(你踩过坑,这里给最短可靠方案)
在 Ubuntu 24.04+ 上经常遇到 PEP668(externally-managed-environment),你最终用的是可行的:
python3 -m pip install pycryptodome --break-system-packages
python3 - << 'EOF'
from Crypto.Cipher import AES
print('PyCryptodome OK')
EOF
看到 PyCryptodome OK 才往下走。
3.3 准备最小 key 文件(测试 key)
这一步只为了让闭环跑通。生产阶段你会换成真正的 OEM_K1 fuse key 等。
在一个干净目录(例如 ekb_min/)里准备:
oem_k1.key(32 bytes for t234)sym_t234.key(示例中的对称 key)auth_t234.key(示例中的认证 key)
如果你只是要跑通,可用随机生成:
openssl rand -out oem_k1.key 32
openssl rand -out sym_t234.key 32
openssl rand -out auth_t234.key 32
3.4 生成 EKS 镜像
python3 ../gen_ekb.py -chip t234 \
-oem_k1_key oem_k1.key \
-in_sym_key sym_t234.key \
-in_auth_key auth_t234.key \
-out eks_t234.img
ls -l eks_t234.img
你已经成功生成了 eks_t234.img。
3.5 确认刷写到 EKS 分区(Yocto/flash 关联)
你给出的 xml 片段:
<partition name="A_eks" type="eks" oem_sign="true">
...
<filename> eks.img </filename>
...
</partition>
你要理解两件事:
-
刷机工具最终会拿
eks.img去刷A_eks。 -
你生成的是
eks_t234.img,所以你要么:- 把它改名/拷贝成刷机工具期望的名字(例如
eks.img), - 要么在打包/配方里把期望的名字改为你的输出。
- 把它改名/拷贝成刷机工具期望的名字(例如
最简单的落地办法(便于验证):
- 把
eks_t234.img拷贝为刷机目录里的eks.img(或你 BSP/Yocto 生成的 eks.img 的覆盖路径)。
关键点:你的镜像生成系统(Yocto)要“最终产物可控”。
4. 阶段 3:HelloWorld 文件保护工程(你要做的核心)
这一阶段我们严格只做“HelloWorld 文件”。
不扩展到 OTA / Secure Boot / 大体系,避免分心。
4.1 你最终要得到的命令体验
你希望的是这种体验:
# 生成明文
echo "hello world" > helloworld.txt
# 加密:明文 -> 密文
jcl-tee-crypt enc helloworld.txt helloworld.enc
# Linux 直接看密文:看不到 hello world
cat helloworld.enc
# 解密:密文 -> 明文
jcl-tee-crypt dec helloworld.enc helloworld.dec.txt
cat helloworld.dec.txt
# hello world
这就是最小闭环。
4.2 选择加密方式:为什么推荐 AES-256-GCM 或 AES-256-CBC
你问过“用什么加密方式更常用”。给你一个非常工程化的选择:
| 方案 | 推荐度 | 优点 | 备注 |
|---|---|---|---|
| AES-256-GCM | ★★★★★ | 自带认证(保密 + 完整性) | 更现代,推荐 |
| AES-256-CBC + HMAC/CMAC | ★★★★☆ | 兼容广 | 需要你额外做认证 |
为了“最少踩坑 + 最小闭环”,我建议你:
- 第一版先做 AES-256-CBC(实现简单,容易对照)。
- 第二版再升级到 AES-256-GCM(更像真实产品)。
本篇先按 CBC 讲解(你更容易一把跑通)。
4.3 设计 EKB 内容:只放一个 HelloWorld seed(最小可控)
你问过:EKB 里多个 key 怎么选?答案是 Key_tag。
但为了最小闭环:
- 这次只放一个条目:
HELLO_SEED。 - PTA 写死使用
HELLO_SEED(避免你在 CA/PTA 都传 tag 导致复杂化)。
4.3.1 约定一个 Key_tag(示例)
建议你用一个“可读”的 tag(4 bytes):
HELO= 0x4F4C4548(小端存储时注意字节序)
你只要做到:CA/PTA 内部用同一个 tag 常量即可。
4.3.2 生成 hello seed(在 PC 上)
openssl rand -out hello_seed.key 32
ls -l hello_seed.key
# 32 bytes
4.3.3 把 hello seed 打进 EKB
做法有两种:
- 做法 A(推荐你现在用):修改/扩展
gen_ekb.py,让它支持-in_hello_seed hello_seed.key并写入一个新的 Key_tag。 - 做法 B:你把 seed 放到
-in_sym_key2这类“已有字段”里(但语义不清晰,后面你会绕)。
建议你走做法 A:语义清晰,你之后扩展多 key 不会乱。
你已经生成过 EKS 镜像。下一次生成时,把 hello seed 一起写进去。
4.4 PTA:只做两件事(加密 / 解密),且永不泄露 key
4.4.1 PTA 的接口设计(最小命令集)
| 命令 | 输入 | 输出 |
|---|---|---|
CMD_HELLO_ENCRYPT | 明文 buffer + IV(或由 PTA 生成) | 密文 buffer |
CMD_HELLO_DECRYPT | 密文 buffer + IV | 明文 buffer |
注意:PTA 里永远不返回 key。
4.4.2 PTA 内部做什么(用一张流程图记住)
[CMD]
|
v
从 EKB 取 HELLO_SEED (只在 Secure World)
|
v
KDF(HELLO_SEED) -> WORK_KEY (AES-256)
|
v
AES-256-CBC(WORK_KEY, IV) 加密/解密
|
v
返回数据(密文/明文)
4.4.3 KDF(够用版)
你不用一开始就实现 NIST-SP-800-108 那套(容易分心)。
最小可控方案:
- WORK_KEY = SHA256(HELLO_SEED || “hello-work-key”)
- 取 32 bytes 作为 AES-256 key
这样你:
- 有稳定派生
- 逻辑清晰
- 后面再替换为官方 KDF 也容易
注意:生产方案你可以改成与 NVIDIA PTA 同样的 KDF,但不影响你现在把链路跑通。
4.5 CA:只做“搬运”,不做任何 crypto
CA(Linux App)必须满足这条硬规则:
- CA 代码里不出现任何明文 key/seed。
- CA 不包含 AES/SHA 等算法实现(除非你只是做格式处理)。
4.5.1 CA 伪代码(你写的时候就照这个结构)
int main(int argc, char** argv) {
// 1) 解析 enc/dec
// 2) 读输入文件 -> buffer
// 3) 初始化 TEEC
// 4) OpenSession(PTA)
// 5) InvokeCommand(CMD, buffer)
// 6) 写输出文件
}
4.6 Yocto 集成:把 PTA 和 CA 真的装进系统
你现在走的是 Yocto,因此最推荐的落地方式是:
- CA:做成一个 recipe,安装到
/usr/bin/jcl-tee-crypt - PTA:编译进 OP-TEE OS(或以 TA 的方式安装到 TA 路径,但本工程建议 PTA 以减少变量)
- EKS:用你生成的
eks.img覆盖刷机所用镜像
你之前遇到
hwkey-agent直接 make 失败、tee_client_api.h缺失,这正说明:手工在源码树 make 往往不如直接复用 Yocto 的 optee-client/optee-os 交叉编译体系。
4.6.1 你已经找到 optee-nvsamples 的 Yocto 产物
例如:
.../tmp/work/.../optee-nvsamples/36.4.4/build/ca/hwkey-agent/nvhwkey-app
这说明:
- Yocto 环境已经把 include/sysroot/link 都配好了
- 你自己的 CA 最好也用同样方式构建(recipe)
5. 实操步骤清单(你照着做,一步一步验收)
这一节是“你真正要敲的步骤”。
Step 1:准备明文
echo "hello world" > helloworld.txt
验收:
cat helloworld.txt
# hello world
Step 2:准备 hello seed
在 PC 上:
openssl rand -out hello_seed.key 32
验收:
ls -l hello_seed.key
# 32 bytes
Step 3:生成带 HELLO_SEED 的 EKS 镜像
- 修改
gen_ekb.py支持写入HELLO_SEED(Key_tag + Key_len + Key_data) - 生成输出
eks.img(或生成后改名/拷贝)
验收:
ls -l eks.img
Step 4:刷写 EKS 分区(与你的 Yocto 刷机流程结合)
你要确保最终刷的是你这份 eks.img。
验收(刷机日志):
- 看到写入
A_eks(或 EKS)分区 - 设备能正常启动
Step 5:确认 OP-TEE 正常(运行时)
systemctl status tee-supplicant
xtest
验收:
tee-supplicantrunningxtest能跑
Step 6:集成并安装你的 PTA
- PTA 编译进 OP-TEE OS
- 设备启动后 PTA 可被
TEEC_OpenSession()打开
验收:
- CA 能打开 session,不报
0xffff0008
你之前
nvhwkey-app -e报TEEC_InvokeCommand failed 0xffff0008 origin 0x4,通常意味着:
- 目标 TA/PTA 不存在/没安装
- UUID/路径不对
- 依赖的服务(例如 jetson-user-key PTA)没启用或 EKB 没准备好
你的 HelloWorld 工程要规避这类不确定性:用你自己的 PTA,确保 UUID、命令号、部署路径都在你掌控范围内。
Step 7:安装你的 CA(jcl-tee-crypt)
把 CA 放到板子上:
/usr/bin/jcl-tee-crypt --help
验收:
- 有帮助信息
- 能正常运行
Step 8:加密 helloworld.txt
jcl-tee-crypt enc helloworld.txt helloworld.enc
验收:
cat helloworld.enc
# 乱码(看不到 hello world)
Step 9:解密回明文
jcl-tee-crypt dec helloworld.enc helloworld.dec.txt
cat helloworld.dec.txt
# hello world
验收:
- 输出一致
Step 10:安全验收(你必须做的最后一步)
你要证明“Linux 想拿 key 拿不到”:
- 在 Linux 上全盘搜索 key 字符串(例如
hello_seed)应该找不到 - CA 源码里没有 seed
- CA 只传 buffer
6. 最小代码骨架(让你能马上开工)
下面给的是“工程骨架”,不是完整可编译成品。你照着填就能做出来。
6.1 CA:jcl-tee-crypt(Linux)
文件:jcl_tee_crypt.c(核心片段)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tee_client_api.h>
// TODO: 换成你自己的 PTA UUID
static const TEEC_UUID PTA_HELLO_UUID = {
0x12345678, 0x1234, 0x1234,
{ 0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0 }
};
#define CMD_HELLO_ENCRYPT 0x1
#define CMD_HELLO_DECRYPT 0x2
static int invoke(int cmd, void *buf, size_t len) {
TEEC_Context ctx;
TEEC_Session sess;
TEEC_Operation op;
TEEC_Result res;
uint32_t origin;
res = TEEC_InitializeContext(NULL, &ctx);
if (res != TEEC_SUCCESS) return -1;
res = TEEC_OpenSession(&ctx, &sess, &PTA_HELLO_UUID,
TEEC_LOGIN_PUBLIC, NULL, NULL, &origin);
if (res != TEEC_SUCCESS) {
TEEC_FinalizeContext(&ctx);
return -2;
}
memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INOUT,
TEEC_NONE, TEEC_NONE, TEEC_NONE);
op.params[0].tmpref.buffer = buf;
op.params[0].tmpref.size = len;
res = TEEC_InvokeCommand(&sess, cmd, &op, &origin);
TEEC_CloseSession(&sess);
TEEC_FinalizeContext(&ctx);
return (res == TEEC_SUCCESS) ? 0 : -3;
}
int main(int argc, char **argv) {
// enc/dec 解析 + 文件读写略
return 0;
}
你只要把:
- UUID
- 文件读写
- enc/dec 命令行
填完,就能跑。
6.2 PTA:pta_hello_crypt(Secure World)
PTA 的代码位置取决于你的 OP-TEE OS 源码结构,核心是:
- 注册 UUID
- 实现
invoke_command - 读取 EKB 中的 HELLO_SEED
- 派生 WORK_KEY
- 用 TEE 内部 crypto API 做 AES
你第一版可以先把“从 EKB 取 seed”替换成一个“已知 seed”(仅用于验证 PTA/CA 通路)。
一旦通路跑通,再把 seed 来源切换成 EKB。
这样你不会一次卡死在两件事上。
7. 常见报错与定位(专治卡住)
7.1 TEEC_InvokeCommand failed 0xffff0008
这类错误最常见原因:
- 目标 PTA/TA 没部署成功(UUID 找不到)
- TA 路径不对(用户 TA 时更常见)
- PTA 没编进 OP-TEE OS
- 依赖的服务没启用(例如你跑 NVIDIA sample 但没满足它的前置)
你这次工程的策略:
- 用你自己的 PTA,避免 sample 的复杂依赖。
- 用最小命令集验证通路。
7.2 xtest 有 skip
只要:
- 能启动
- 能跑核心用例
一般不影响你做 HelloWorld 工程。
8. 你完成后“真正掌握”的核心点(不深奥,但很关键)
你完成 HelloWorld 工程后,你就真正掌握了:
- OP-TEE 的两世界架构(Linux 只是调用者,真正保护发生在 Secure World)。
- EKB 的工程意义:把业务秘密(seed/key)放到 Secure World 可用、Linux 不可见的存储链路里。
- PTA 的价值:让 Linux 获得“能力”,而不是“密钥”。
- 安全边界怎么验收:Linux 只能拿到密文;解密必须走 Secure World。
这四点就是你要的“链路理解”。
9. 你接下来可以自然扩展到“多 key、多业务”(但不是现在)
当 HelloWorld 跑通后,你再做扩展会非常顺:
-
EKB 放多个 key(多个 Key_tag)
-
PTA 支持:
- CA 传 tag
- 或多命令对应不同业务
-
业务场景:
- 模型文件解密
- 配置文件加密
- license 解锁
但你现在先把 HelloWorld 做到 100% 可复现。
10. 最终检查清单(你做完后对照打钩)
-
tee-supplicant运行正常 -
xtest能跑 - 你能生成并刷入
eks.img - 你自己的 PTA 能被打开 session
-
jcl-tee-crypt enc输出密文 -
cat helloworld.enc看不到明文 -
jcl-tee-crypt dec恢复hello world - CA 源码里没有任何 seed/key
做到这里,你就不是“看懂了”,而是“真的会了”。
📺 B站视频讲解(Bilibili):https://www.bilibili.com/video/BV1k1C9BYEAB/
📘 《Yocto项目实战教程》京东购买链接:Yocto项目实战教程
2256

被折叠的 条评论
为什么被折叠?



