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

![](./_static/_images/secure_storage/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`
```shell
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 文件信息或存储在块文件中的数据。

具体细节可参考 <a href="https://optee.readthedocs.io/en/latest/architecture/secure_storage.html" >https://optee.readthedocs.io/en/latest/architecture/secure_storage.html</a>


**注意: ROOT KEY 可在BL2/Uboot/Linux下烧写，具体参考[eFuse烧录](../efuse/efuse_update.html)**

## 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 |


<span id="secure_storage_rtval"/> </span>
## 返回值说明

| 错误码 | 宏定义    | 描述                             |
| :---------- | :------- | :------ |
| 0      | HBST_OK          | 成功                             |
| -1      | HBST_ERR_PARAM  | 参数错误     |
| -2      | HBST_ERR_KEYNAME  | 文件非法路径 |
| -3      | HBST_ERR_MALLOC          | 接口申请内存失败                 |
| -4      | HBST_ERR_OPEN</br>  HBST_ERR_FILEOPEN | 文件打开失败                 |
| -5      | HBST_ERR_DELETE        | 文件删除失败                 |
| -6      | HBST_ERR_WR        | 文件写失败                 |
| -7      | HBST_ERR_RD</br>  HBST_ERR_FILERD       | 文件读失败                 |
| -8      | HBST_ERR_NULLOBJ        | 文件对象为空                 |


## API 参考

[drobot_st_file_load](#drobot_st_file_load) : 读secure storage  
[drobot_st_file_store](#drobot_st_file_store) : 写secure storage  
[drobot_force_rpmb_key_ready](#drobot_force_rpmb_key_ready) : 强制设置ROOT KEY为ready状态  
[drobot_is_st_file_exist](#drobot_is_st_file_exist) : 检查secure storage是否存在某文件  
[drobot_st_file_delete](#drobot_st_file_delete) : 删除secure storage的文件  
[drobot_st_file_rename](#drobot_st_file_rename) : 重命名secure storage中的文件  
[drobot_get_device_unlock](#drobot_get_device_unlock) : 取AVB的unlock状态  
[drobot_set_device_unlock](#drobot_set_device_unlock) : 设置AVB的unlock状态  

## 接口说明
<span id="drobot_st_file_load"/> </span>

### drobot_st_file_load
#### 【函数声明】
```C
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，参考[返回值说明](#secure_storage_rtval)
#### 【注意事项】
该函数将会分配相应的空间用于存放读取的数据，调用者负责将其free
#### 【兼容性】
X5 Linux / U-boot
#### 【示例代码】
```C
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;
}
```

<span id="drobot_st_file_store"></span>

### drobot_st_file_store
#### 【函数声明】
```C
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，参考[返回值说明](#secure_storage_rtval)
#### 【注意事项】
无
#### 【兼容性】
X5 Linux / U-boot
#### 【示例代码】
```C
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;
}
```

<span id="drobot_force_rpmb_key_ready"/> </span>

### drobot_force_rpmb_key_ready
#### 【函数声明】
```C
int32_t drobot_force_rpmb_key_ready(void);
```
#### 【功能描述】
强制设置 ROOT KEY 为 ready 状态。只在 RPMB KEY 未烧写时有效果
#### 【参数描述】
无
#### 【返回值】
- 成功，返回 0
- 失败：非 0，参考[返回值说明](#secure_storage_rtval)
#### 【注意事项】
在第一次访问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
#### 【示例代码】
```C
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;
}
```


<span id="drobot_is_st_file_exist"/> </span>

### drobot_is_st_file_exist
#### 【函数声明】
```C
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，参考[返回值说明](#secure_storage_rtval)
#### 【注意事项】
无
#### 【兼容性】
X5 Linux / U-boot
#### 【示例代码】
```C
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;
}
```


<span id="drobot_st_file_delete"/> </span>

### drobot_st_file_delete
#### 【函数声明】
```C
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，参考[返回值说明](#secure_storage_rtval)
#### 【注意事项】
无
#### 【兼容性】
X5 Linux / U-boot
#### 【示例代码】
```C
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;
}
```


<span id="drobot_st_file_rename"/> </span>

### drobot_st_file_rename
#### 【函数声明】
```C
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，参考[返回值说明](#secure_storage_rtval)
#### 【注意事项】
无
#### 【兼容性】
X5 Linux / U-boot
#### 【示例代码】
```C
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;
}
```


<span id="drobot_get_device_unlock"/> </span>

### drobot_get_device_unlock
#### 【函数声明】
```C
int32_t drobot_get_device_unlock(bool *state);
```
#### 【功能描述】
获取 AVB 的 unlock 状态
#### 【参数描述】
- [OUT] bool *state : 保存获取到的 unlock 状态，0表示 lock，1表示 unlock
#### 【返回值】
- 成功，返回 0
- 失败：非 0，参考[返回值说明](#secure_storage_rtval)
#### 【注意事项】
无
#### 【兼容性】
X5 Linux / U-boot
#### 【示例代码】
```C
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;
}
```

<span id="drobot_set_device_unlock"/> </span>

### drobot_set_device_unlock
#### 【函数声明】
```C
int32_t drobot_set_device_unlock(bool state);
```
#### 【功能描述】
设置 AVB 的 unlock 状态
#### 【参数描述】
- [IN] bool state : unlock 状态，0表示 lock，1表示 unlock
#### 【返回值】
- 成功，返回 0
- 失败：非 0，参考[返回值说明](#secure_storage_rtval)
#### 【注意事项】
通过 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
#### 【示例代码】
```C
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 : 表示写入的数据长度，使用十六进制表示



