X5 secure storage介绍

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

../../../../_images/secure-storage-overview.png

在 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 派生而来。

注意

  1. 第一次访问指的是通过 optee 形式读写 RPMB ,如果是通过其他形式,如 Linux 或 Uboot 测访问 RPMB 不会触发 RPMB KEY 的烧录,需要手动指定 KEY 进行更新的。当然,由于 KEY 只能烧录一次,如果使用了 Linux 或 Uboot 侧的 RPMB 工具烧录了 KEY,那么本文介绍的 secure storage 将无法使用。

  2. 由于 RPMB KEY 只能烧录一次,默认情况下,如果客户 ROOT KEY 未烧录,RPMB KEY 禁止更新。X5 支持在ROOT KEY 未更新的情况下(也就是ROOT KEY为0),强制生成和更新 RPMB KEY ,需要在访问RPMB之前,先调用接口 drobot_force_rpmb_key_ready(不建议这样使用)。

  3. SSK 和 RPMB KEY 均与客户 ROOT KEY 相关,务必注意要先更新 ROOT KEY,再使用 RPMB 以及 REE FS

  4. 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

【返回值】

【注意事项】

该函数将会分配相应的空间用于存放读取的数据,调用者负责将其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

【返回值】

【注意事项】

【兼容性】

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 未烧写时有效果

【参数描述】

【返回值】

【注意事项】

在第一次访问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

【返回值】

【注意事项】

【兼容性】

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

【返回值】

【注意事项】

【兼容性】

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

【返回值】

【注意事项】

【兼容性】

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

【返回值】

【注意事项】

【兼容性】

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

【返回值】

【注意事项】

通过 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 : 表示写入的数据长度,使用十六进制表示