X5 secure storage介绍
X5 使用开源 optee 的安全存储方案,如下所示

在 optee 中有两种形式的 secure storage
一种是依赖于 normal world 的文件系统,也就是保存在 Linux 侧的文件系统当中,称为 REE FS
一种是依赖于 eMMC 设备的 RPMB 分区,数据保存在 RPMB 当中
对于 REE FS,保存路径为 /data/tee,该路径是一个软链接,当前是链接到了 /userdata/tee 目录中。
客户如果如果想链接到其他目录,对应修改启动文件 etc/init.d/S97link_reefs.sh
function link_reefs() {
mkdir -p /userdata/tee
ln -s /userdata/tee /data/tee
}
KEY 管理
文件加解密的 KEY 管理
在 RPMB 和 REE FS 中,数据都是加密保存的。在加密过程中涉及到以下 KEY
SSK (secure storage key)
SSK 是 per-device 密钥,也就是每颗 X5 芯片的 SSK 都不一样。
除此之外,还与客户的 ROOT KEY 相关。SSK 在 OP-TEE 启动时生成并存储在安全内存中。SSK 用于派生 TA 存储密钥 (TSK)。
SSK 的派生算法如下
SSK = HMACSHA256 (HUK, 芯片 ID || “静态字符串”)
注意: HUK(Hardware Unique Key,硬件唯一密钥),作为设备的唯一标识符,X5 平台通过 socid 和客户的 ROOT KEY 派生得到 HUK
TSK (Trusted Application Storage Key)
TSK 是 per-trusted application key,也就是每个 TA 都不一样。由 SSK 和 TA 的标识符 (UUID) 生成。它用于保护 FEK,换句话说,用于加密/解密 FEK。
TSK 的派生方式为:
TSK = HMACSHA256 (SSK, TA_UUID)
FEK (File encryption key)
当创建新的TEE文件时,将通过PRNG(伪随机数生成器)为 TEE 文件生成新的 FEK,并将加密的 FEK 存储在元文件中。
FEK 用于加密/解密存储在元文件中的 TEE 文件信息或存储在块文件中的数据。
具体细节可参考 https://optee.readthedocs.io/en/latest/architecture/secure_storage.html
注意: ROOT KEY 可在BL2/Uboot/Linux下烧写,具体参考eFuse烧录
RPMB KEY 管理
RPMB 的设计目标是防止数据被非法读取或篡改。为了实现这一目标,RPMB 使用了密钥来进行数据的加密、解密和认证。RPMB 密钥是其安全性和完整性的基础。通常用于以下几个方面
数据加密:存储到 RPMB 中的数据通常会被加密,确保即使数据被非法读取,内容也无法被解密。
数据签名和验证:在每次写入和读取数据时,都会使用 RPMB 密钥来生成和验证签名,确保数据的完整性和真实性,防止重放攻击和篡改。
身份验证:密钥也用于认证操作的合法性,只有具有正确密钥的设备才能进行 RPMB 区域的读写操作。
RPMB KEY 只能烧录一次。会在第一次访问 RPMB 时,由 optee 进行烧录。 与 SSK 类似,RPMB KEY 也是 per-device 密钥,根据客户的 ROOT KEY 和 X5 的 socid 派生而来。
注意
第一次访问指的是通过 optee 形式读写 RPMB ,如果是通过其他形式,如 Linux 或 Uboot 测访问 RPMB 不会触发 RPMB KEY 的烧录,需要手动指定 KEY 进行更新的。当然,由于 KEY 只能烧录一次,如果使用了 Linux 或 Uboot 侧的 RPMB 工具烧录了 KEY,那么本文介绍的 secure storage 将无法使用。
由于 RPMB KEY 只能烧录一次,默认情况下,如果客户 ROOT KEY 未烧录,RPMB KEY 禁止更新。X5 支持在ROOT KEY 未更新的情况下(也就是ROOT KEY为0),强制生成和更新 RPMB KEY ,需要在访问RPMB之前,先调用接口
drobot_force_rpmb_key_ready(不建议这样使用)。SSK 和 RPMB KEY 均与客户 ROOT KEY 相关,务必注意要先更新 ROOT KEY,再使用 RPMB 以及 REE FS
REE FS 是依赖于 RPMB 实现的,即使用 REE FS 同样要注意 RPMB KEY 的问题
secure storage 操作接口
基本信息
头文件:drobot_secure_storage.h
动态库:libsecure_storage.so
Secure stroage 支持 REE FS 和 RPMB,其定义如下
| 名称 | 含义 | 值 |
|---|---|---|
SECURE_STORAGE_PRIVATE_RPMB |
RPMB 作为 Secure storage | 0 |
SECURE_STORAGE_PRIVATE_REE |
REE 作为 Secure storage | 1 |
返回值说明
| 错误码 | 宏定义 | 描述 |
|---|---|---|
| 0 | HBST_OK | 成功 |
| -1 | HBST_ERR_PARAM | 参数错误 |
| -2 | HBST_ERR_KEYNAME | 文件非法路径 |
| -3 | HBST_ERR_MALLOC | 接口申请内存失败 |
| -4 | HBST_ERR_OPEN HBST_ERR_FILEOPEN | 文件打开失败 |
| -5 | HBST_ERR_DELETE | 文件删除失败 |
| -6 | HBST_ERR_WR | 文件写失败 |
| -7 | HBST_ERR_RD HBST_ERR_FILERD | 文件读失败 |
| -8 | HBST_ERR_NULLOBJ | 文件对象为空 |
API 参考
drobot_st_file_load : 读secure storage
drobot_st_file_store : 写secure storage
drobot_force_rpmb_key_ready : 强制设置ROOT KEY为ready状态
drobot_is_st_file_exist : 检查secure storage是否存在某文件
drobot_st_file_delete : 删除secure storage的文件
drobot_st_file_rename : 重命名secure storage中的文件
drobot_get_device_unlock : 取AVB的unlock状态
drobot_set_device_unlock : 设置AVB的unlock状态
接口说明
drobot_st_file_load
【函数声明】
int64_t drobot_st_file_load(const char *file_name, char **output, uint32_t *len, uint32_t store_mode);
【功能描述】
从 secure storage 中读数据
【参数描述】
[IN] char *file_name : 要读取的文件名
[OUT] char **output : 读出的数据存放的位置
[OUT] uint32_t *len : 读出到的数据长度
[IN] uint32_t store_mode : 存储类型,
SECURE_STORAGE_PRIVATE_RPMB表示 RPMB,SECURE_STORAGE_PRIVATE_REE表示 REE FS
【返回值】
成功,返回 0
失败:非 0,参考返回值说明
【注意事项】
该函数将会分配相应的空间用于存放读取的数据,调用者负责将其free
【兼容性】
X5 Linux / U-boot
【示例代码】
int main(int argc, char *argv[])
{
char *key_name = "test";
char *read_buffer = NULL;
uint32_t read_len = 0;
int32_t type_flag = SECURE_STORAGE_PRIVATE_RPMB;
int32_t ret = 0;
ret = drobot_st_file_load(key_name, &read_buffer, &read_len, type_flag);
if (ret) {
printf("read %s failed\n", key_name);
return -1;
}
/* do something */
/* do something done*/
free(read_buffer);
return 0;
}
drobot_st_file_store
【函数声明】
int64_t drobot_st_file_store(const char *file_name, char *input, uint32_t len, uint32_t store_mode);
【功能描述】
写 secure storage
【参数描述】
[IN] char *file_name : 要写入的文件名
[IN] char *input : 写入数据 buffer
[IN] uint32_t len : 写入数据长度
[IN] uint32_t store_mode : 存储类型,
SECURE_STORAGE_PRIVATE_RPMB表示 RPMB,SECURE_STORAGE_PRIVATE_REE表示 REE FS
【返回值】
成功,返回 0
失败:非 0,参考返回值说明
【注意事项】
无
【兼容性】
X5 Linux / U-boot
【示例代码】
int main(int argc, char *argv[])
{
char *key_name = "test";
char *write_buffer = "1234567890";
int32_t type_flag = SECURE_STORAGE_PRIVATE_RPMB;
int32_t ret = 0;
ret = drobot_st_file_store(key_name, write_buffer , strlen(write_buffer) + 1, type_flag);
if (ret) {
printf("write %s failed\n", key_name);
return -1;
}
return 0;
}
drobot_force_rpmb_key_ready
【函数声明】
int32_t drobot_force_rpmb_key_ready(void);
【功能描述】
强制设置 ROOT KEY 为 ready 状态。只在 RPMB KEY 未烧写时有效果
【参数描述】
无
【返回值】
成功,返回 0
失败:非 0,参考返回值说明
【注意事项】
在第一次访问RPMB的时候,会根据客户的 ROOT KEY 和芯片唯一 ID,派生出 RPMB 的 KEY;ROOT KEY 是需要客户烧写的,如果没有烧写 ROOT KEY,则禁止更新 RPMB KEY。如果客户并不想烧写 ROOT KEY,可以强制设置 ROOT KEY 为 ready 状态,就可以在没有烧写 ROOT KEY 的情况下使用 RPMB。
注意:如果在使用了该接口,并访问了 RPMB 之后,RPMB 的 KEY 会更新。如果此后再烧写 ROOT KEY,就将造成 RPMB 无法访问
【兼容性】
X5 Linux / U-boot
【示例代码】
int main(int argc, char *argv[])
{
char *key_name = "test";
char *write_buffer = "1234567890";
int32_t type_flag = SECURE_STORAGE_PRIVATE_RPMB;
ret = drobot_force_rpmb_key_ready();
if (ret) {
printf("force root key ready failed\n");
return -1;
}
ret = drobot_st_file_store(key_name, write_buffer , strlen(write_buffer) + 1, type_flag);
if (ret) {
printf("write %s failed\n", key_name);
return -1;
}
return 0;
}
drobot_is_st_file_exist
【函数声明】
bool drobot_is_st_file_exist(const char *file_name, uint32_t store_mode);
【功能描述】
检查在 secure storage 中是否存在名为 “file_name” 的文件
【参数描述】
[IN] char *file_name : 要查找的文件名
[IN] uint32_t store_mode : 存储类型,
SECURE_STORAGE_PRIVATE_RPMB表示 RPMB,SECURE_STORAGE_PRIVATE_REE表示 REE FS
【返回值】
成功,返回 0
失败:非 0,参考返回值说明
【注意事项】
无
【兼容性】
X5 Linux / U-boot
【示例代码】
int main(int argc, char *argv[])
{
char *key_name = "test";
int32_t type_flag = SECURE_STORAGE_PRIVATE_RPMB;
int32_t ret = 0;
ret = drobot_is_st_file_exist(key_name, type_flag);
if (ret) {
printf("%s is in secure storage\n", key_name);
} else {
printf("%s is not in secure storage\n", key_name);
}
return 0;
}
drobot_st_file_delete
【函数声明】
int64_t drobot_st_file_delete(const char *file_name, uint32_t store_mode);
【功能描述】
删除 secure storage 中名为 “file_name” 的文件
【参数描述】
[IN] char *file_name : 要删除的文件名
[IN] uint32_t store_mode : 存储类型,
SECURE_STORAGE_PRIVATE_RPMB表示 RPMB,SECURE_STORAGE_PRIVATE_REE表示 REE FS
【返回值】
成功,返回 0
失败:非 0,参考返回值说明
【注意事项】
无
【兼容性】
X5 Linux / U-boot
【示例代码】
int main(int argc, char *argv[])
{
char *key_name = "test";
int32_t type_flag = SECURE_STORAGE_PRIVATE_RPMB;
int32_t ret = 0;
ret = drobot_st_file_delete(key_name, type_flag);
if (ret) {
printf("delete [%s] failed ret:0x%x\n", key_name, ret);
} else {
printf("delete [%s] success\n", key_name);
}
return ret;
}
drobot_st_file_rename
【函数声明】
int64_t drobot_st_file_rename(const char *old_name, const char *new_name, uint32_t store_mode);
【功能描述】
重命名 secure storage 中名为 “file_name” 的文件为 “new_name”
【参数描述】
[IN] char *old_name : 要重命名的文件
[IN] char *new_name : 修改后的文件名
[IN] uint32_t store_mode : 存储类型,
SECURE_STORAGE_PRIVATE_RPMB表示 RPMB,SECURE_STORAGE_PRIVATE_REE表示 REE FS
【返回值】
成功,返回 0
失败:非 0,参考返回值说明
【注意事项】
无
【兼容性】
X5 Linux / U-boot
【示例代码】
int main(int argc, char *argv[])
{
char *key_name = "test";
char *new_name = "secure";
int32_t type_flag = SECURE_STORAGE_PRIVATE_RPMB;
int32_t ret = 0;
ret = drobot_st_file_rename(key_name, new_name, type_flag);
if (ret) {
printf("rename [%s] to %s in failed ret:0x%x\n", key_name, new_name, ret);
} else {
printf("rename [%s] to %s in success ret:0x%x\n", key_name, new_name, ret);
}
return ret;
}
drobot_get_device_unlock
【函数声明】
int32_t drobot_get_device_unlock(bool *state);
【功能描述】
获取 AVB 的 unlock 状态
【参数描述】
[OUT] bool *state : 保存获取到的 unlock 状态,0表示 lock,1表示 unlock
【返回值】
成功,返回 0
失败:非 0,参考返回值说明
【注意事项】
无
【兼容性】
X5 Linux / U-boot
【示例代码】
int main(int argc, char *argv[])
{
bool unlock_state = 0;
int32_t ret = 0;
ret = drobot_get_device_unlock(&unlock_state);
if (ret == 0) {
printf("get unlock state:%d\n", unlock_state);
} else {
printf("get lock state failed\n");
}
return ret;
}
drobot_set_device_unlock
【函数声明】
int32_t drobot_set_device_unlock(bool state);
【功能描述】
设置 AVB 的 unlock 状态
【参数描述】
[IN] bool state : unlock 状态,0表示 lock,1表示 unlock
【返回值】
成功,返回 0
失败:非 0,参考返回值说明
【注意事项】
通过 fastboot 命令,也可以设置 AVB 的 lock/unlock 状态
fastboot flashing lock
fastboot flashing unlock
AVB的lock/unlock状态,只有在Uboot开启CONFIG_OPTEE_TA_AVB之后才会有效果,当前未开启,不开启固定是lock状态。lock状态下,AVB和Dm-verity验证失败,会强制重启,并切换分区,unlock状态下,只会报告错误,不会强制切换分区。
AVB lock状态存储在RPMB中,尝试去访问lock状态时,如果它不存在,则会创建它,并更新为lock状态。
【兼容性】
X5 Linux / U-boot
【示例代码】
int main(int argc, char *argv[])
{
bool w_lock_state = true;
int32_t ret = 0;
ret = drobot_set_device_unlock(w_lock_state);
if (ret) {
printf("set lock state failed\n");
return -1;
}
return 0;
}
secure storage 操作命令
基本信息
Uboot下提供了访问了secure storage 的命令,可用于 debug 默认未开启,需要开启
DROBOT_OPTEE_RPMB_DEBUG_CMD=y
注意:Uboot下访问 secure storage 仅支持 PRMB
使用方法
读取RPMB,命令格式 x5_rpmb read [filename] [addr]
参数说明
filename : 表示存储在RPMB的文件名字
addr : 表示将读出的数据保存的地址
写RPMB,命令格式 x5_rpmb write [filename] [addr] [len]
filename : 表示存储在RPMB的文件名字,
addr : 表示将读出的数据保存的地址
len : 表示写入的数据长度,使用十六进制表示