可信执行环境(安全编程篇)
安全应用编程模型
YoC OS的安全应用编程模型如下所示,YoC OS直接调用libcsiteeca.a库函数就可以完成对TEE OS里的可信应用的间接调用。libcsiteeca.a库基于TEE Client API接口实现,在利用TEE SDK开发可信应用的同时产生的,其接口是对可信应用调用的抽象封装,如需要进行个性化的可信应用的开发,具体详见Chapter 7-应用程序开发章节里的安全应用开发文档。
YoC安全应用开发
TEE OS和YoC OS是独立运行的,利用YoC SDK可以生成TEE OS镜像,再生成YoC OS镜像。随着YoC SDK发布的TEE OS里已经预置了一些可信应用,YoC安全应用直接调用libcsiteeca.a库函数就可以完成安全应用的调用。
1. 获取YoC SDK
获取SDK之前先安装yoctools工具。yoctools安装以及使用请看YoCTools
通过yoc命令安装:yoc install sdk_pangu
,安装完毕之后,可以看到如下目录:
ls boards components solutions
boards:
pangu_cpu0
components:
amrnb aui_cloud ble_mesh console drivers drv_snd_pangu flac ipc ...
solutions:
pangu_demo pangu_player pangu_rec_demo pangu_tee pangu_tee_test
2. 系统启动镜像
YoC OS启动的流程有两种情况:包含TEE镜像和不包含TEE镜像。 我们可以在config.yaml脚本里为每一个镜像配置地址空间, 以pangu_cpu0为例, config.yaml位于 ./boards/pangu_cpu0/configs/config.yaml
我们通过修改config.yaml脚本来修改TEE和PRIM的FLASH地址空间,并确保各镜像地址不重叠。
注意TEE和PRIM的RAM地址空间配置请参考后面章节。
启动流程包含TEE
partitions: - { name: bmtb, address: 0x8000000, size: 0x001000 } - { name: boot, address: 0x8001000, size: 0x010000 } - { name: tee, address: 0x8011000, load_addr: 0x18000000, size: 0x010000 } - { name: imtb, address: 0x8021000, size: 0x002000 } - { name: prim, address: 0x8023000, load_addr: 0x18010000, size: 0x300000, verify: true, update: DIFF} - { name: cpu1, address: 0x8323000, size: 0x080000, update: DIFF } - { name: cpu2, address: 0x83A3000, size: 0x200000, update: DIFF } - { name: kv, address: 0x8623000, size: 0x004000 } - { name: lpm, address: 0x8627000, size: 0x020000 } - { name: misc, address: 0x8647000, size: 0x400000 } - { name: kp, address: 0x8FFF000, size: 0x001000 }
启动流程包不含TEE
partitions: - { name: bmtb, address: 0x8000000, size: 0x001000 } - { name: boot, address: 0x8001000, size: 0x010000 } - { name: imtb, address: 0x8021000, size: 0x002000 } - { name: prim, address: 0x8023000, load_addr: 0x18010000, size: 0x300000, verify: true, update: DIFF} - { name: cpu1, address: 0x8323000, size: 0x080000, update: DIFF } - { name: cpu2, address: 0x83A3000, size: 0x200000, update: DIFF } - { name: kv, address: 0x8623000, size: 0x004000 } - { name: lpm, address: 0x8627000, size: 0x020000 } - { name: misc, address: 0x8647000, size: 0x400000 } - { name: kp, address: 0x8FFF000, size: 0x001000 }
在编译生成YoC OS镜像的时候,每个镜像的地址空间从该脚本里读取,最终生成系统镜像
3. 编译生成TEE OS镜像
pangu_tee solution工程位于
./solutions/pangu_tee
TEE OS镜像的地址空间配置文件tee_addr_map.h,该文件位于
/solutions/pangu_tee/csi_arch/ck804t/tw/inc/hobbit4/tee_addr_map.h
更新TEE OS地址空间 打开tee_addr_map.h后, 修改以下四个配置宏来更改TEE OS的地址空间,我们将config.yaml 里的TEE的FLASH的起始地址和大小配置到TW_RO_ADDR和TW_RO_SIZE, TEE OS的RAM地址空间通过修改TW_RW_ADDR和TW_RW_SIZE来实现(一般TEE OS的RAM使用系统RAM最靠前部分), 注意TEE OS使用的RAM地址空间不能和PRIM重叠, PRIM使用的RAM地址空间请在gcc_eflash.ld.S里查看, 防止地址重叠,gcc_eflash.ld.S位于:
./solutions/pangu_tee/gcc_flash.ld
如果无需改动则调过此步骤。
#define TW_RO_ADDR 0x18000000 // TEE OS FLASH start address #define TW_RO_SIZE 0x00008000 // TEE OS FLASH SIZE #define TW_RW_ADDR 0x18008000 // TEE OS RAM start address #define TW_RW_SIZE 0x00004000 // TEE OS RAM size
编译生成TEE OS 在根目录下执行"make"来编译生成TEE OS, 最终生成tee文件和libcsiteeca.a文件
安装TEE OS 将tee文件复制到下面目录,
./boards/pangu_cpu0/bootimgs
将libcsiteeca.a文件复制到以下目录
./components/csi_pangu/tee
4. 开发YoC安全应用
YoC安全应用的开发直接调用libcsiteeca.a库函数即可以, 该库函数的函数申明在tee.h里。 其中libcsiteeca.a和tee.h分别位于
./components/csi_pangu/tee/libcsiteeca.a
和
./components/csi/include/drv/tee.h
具体接口使用说明请参考后续的章节。
利用libcsiteeca.a库函数实现安全应用的参考代码如下:
- AES算法接口使用参考代码
const uint8_t in[16] = "Hello, World!";
uint8_t iv[16] = {0};
const uint8_t key[32] = "Demo-Key";
int32_t ret;
uint8_t out[16] = {};
uint8_t out2[16] = {};
int i;
TEE_LOGI("AES CBC source string:%s\n", in);
TEE_HEX_DUMP("AES CBC IV:", iv, 16);
TEE_HEX_DUMP("AES CBC KEY:", key, 32);
for (i = 0; i < AES_CASE_NUM; i++) {
memset(out, 0, 16);
memset(out2, 0, 16);
memset(iv, 0, 16);
TEE_LOGI("%s TEST\n", aes_case[i].case_name);
TEE_HEX_DUMP("expect out:", aes_case[i].expect_out, 16);
ret = csi_tee_aes_encrypt(in, 16, key, aes_case[i].key_len, iv, out, aes_case[i].mode);
if (0 == ret) {
TEE_HEX_DUMP("encrypt:", out, 16);
TEE_LOGE("%s encrypt test: %s\n", aes_case[i].case_name, memcmp(out, aes_case[i].expect_out, 16) ? "Fail" : "Pass");
} else {
TEE_LOGE("%s test: Fail, ret 0x%x\n", aes_case[i].case_name, ret);
}
memset(iv, 0, 16);
ret = csi_tee_aes_decrypt(out, 16, key, aes_case[i].key_len, iv, out2, aes_case[i].mode);
if (0 == ret) {
TEE_HEX_DUMP("decrypt:", out2, 16);
TEE_LOGE("%s decrypt test: %s\n", aes_case[i].case_name, memcmp(out2, in, 16) ? "Fail" : "Pass");
} else {
TEE_LOGE("%s test: Fail, ret 0x%x\n", aes_case[i].case_name, ret);
}
}
SHA运算接口使用参考代码
uint8_t digest[64] = {0}; int i, ret; TEE_HEX_DUMP("SHA source", (uint8_t *)data_in, sizeof(data_in)); for (i = 0; i < SHA_CASE_NUM; i++) { memset(digest, 0, sizeof(digest)); TEE_LOGI("%s TEST\n", sha_case[i].case_name); TEE_HEX_DUMP("expect out:", sha_case[i].expect_out, sha_case[i].hash_len); ret = csi_tee_sha_digest((uint8_t *)data_in, sizeof(data_in), digest, sha_case[i].type); if (ret == 0) { TEE_HEX_DUMP("digest:", digest, sha_case[i].hash_len); TEE_LOGI("%s digest test: %s\n", sha_case[i].case_name, memcmp(digest, sha_case[i].expect_out, sha_case[i].hash_len) ? "Fail" : "Pass"); } else { TEE_LOGI("%s digest test: Fail, ret 0x%x\n", sha_case[i].case_name, ret); } }
RSA算法接口使用参考代码
ret = csi_tee_rsa_sign(Digest, 32, rsa2048_key, sizeof(rsa2048_key), dst_2048, &dst_size, TEE_RSA_SHA256); TEE_HEX_DUMP("RSA2048 SHA256 sign:", dst_2048, dst_size); if (ret) { TEE_LOGI("RSA2048 sign Fail,ret %x", ret); } ret = csi_tee_rsa_verify(Digest, 32, rsa2048_pub_key, sizeof(rsa2048_pub_key), dst_2048, dst_size, TEE_RSA_SHA256); TEE_LOGI("RSA2048 SHA256 sign and verify: %s\n", ret ? "Fail" : "Pass"); ret = csi_tee_rsa_encrypt(Digest, 32, rsa2048_pub_key, sizeof(rsa2048_pub_key), dst_2048, &dst_size, TEE_RSA_PKCS1_PADDING); TEE_HEX_DUMP("RSA2048 encrypt:", dst_2048, dst_size); if (ret) { TEE_LOGI("RSA2048 encrypt Fail,ret %x", ret); } ret = csi_tee_rsa_decrypt(dst_2048, dst_size, rsa2048_key, sizeof(rsa2048_key), dst_2048, &dst_size, TEE_RSA_PKCS1_PADDING); TEE_HEX_DUMP("RSA2048 decrypt:", dst_2048, dst_size); if (ret == 0) { TEE_LOGI("RSA2048 encrypt and decrypt: %s\n", memcmp(dst_2048, Digest, 20) ? "Fail" : "Pass"); } else { TEE_LOGI("RSA2048 decrypt Fail,ret %x", ret); }
系统频率接口操作实例代码
uint32_t sysclk; csi_tee_get_sys_freq(&sysclk); printf("current system clock is %d\n", sysclk); csi_tee_set_sys_freq(EHS_CLK, OSR_8M_CLK_24M); console_init_ree(24000000); printf("set system clock to %d\n", 24000000); csi_tee_get_sys_freq(&sysclk); printf("current system clock is %d\n", sysclk);
5. 编译生成YoC OS镜像
所有的YoC安全应用开发完了以后,就可以通过在solutions下的应用目录进行编译,这里以pangu_demo为例,
在solutions/pangu_demo/目录下执行"make"指令来完成YoC OS镜像的生成。具体编译结果如下:
YoC SDK Done
[INFO] Create bin files
----------------------------------------------------------------
bmtb, 0, 0, 0x08000000, 0x00001000, 0x08001000, bmtb
boot, 0, 0, 0x08001000, 0x00010000, 0x08011000, boot
tee, 0, 0, 0x08011000, 0x00010000, 0x08021000, tee
imtb, 0, 0, 0x08021000, 0x00002000, 0x08023000, imtb
prim, 1, 1, 0x08023000, 0x00300000, 0x08323000, prim
cpu1, 0, 1, 0x08323000, 0x00080000, 0x083a3000, cpu1
cpu2, 0, 1, 0x083a3000, 0x00200000, 0x085a3000, cpu2
kv, 0, 0, 0x08623000, 0x00004000, 0x08627000
lpm, 0, 0, 0x08627000, 0x00020000, 0x08647000, lpm
misc, 0, 0, 0x08647000, 0x00400000, 0x08a47000
kp, 0, 0, 0x08fff000, 0x00001000, 0x09000000, kp
boot, 41516 bytes
tee, 21604 bytes
prim, 1312364 bytes
cpu1, 427844 bytes
cpu2, 347444 bytes
lpm, 24484 bytes
kp, 576 bytes
bmtb, 192 bytes
imtb, 8192 bytes
----------------------------------------------------------------
其中prim镜像就是YoC OS镜像, tee镜像就是之前生成的TEE OS镜像,其他的镜像在默认情况下不用改动,如需要改动,请参考相应模块的章节。最终,所有的镜像都会打包成一个total_image.hex镜像, 直接利用烧录工具进行烧写即可,具体烧录步骤请参考相应章节,total_image.hex镜像位于
/solutions/pangu_demo/generated/total_image.hex
这里,YoC安全应用开发完毕。
6. 烧写YoC镜像
请参考镜像烧写章节。
7. 系统运行
按下启动按钮,系统开始运行,在串口控制台可以看到如下打印信息说明系统运行成功。
TEE v1.1 Initialize done.
由于TEE在系统运行过程中调试信息关闭,所以如果要确认可信安全应用是否正确执行,可以根据libcsiteeca.a库函数的返回值和参数来判断。
CSI TEE CA接口定义
返回值
#define TEEC_SUCCESS 0x00000000
#define TEEC_ERROR_GENERIC 0xFFFF0000
#define TEEC_ERROR_ACCESS_DENIED 0xFFFF0001
#define TEEC_ERROR_CANCEL 0xFFFF0002
#define TEEC_ERROR_ACCESS_CONFLICT 0xFFFF0003
#define TEEC_ERROR_EXCESS_DATA 0xFFFF0004
#define TEEC_ERROR_BAD_FORMAT 0xFFFF0005
#define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006
#define TEEC_ERROR_BAD_STATE 0xFFFF0007
#define TEEC_ERROR_ITEM_NOT_FOUND 0xFFFF0008
#define TEEC_ERROR_NOT_IMPLEMENTED 0xFFFF0009
#define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A
#define TEEC_ERROR_NO_DATA 0xFFFF000B
#define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C
#define TEEC_ERROR_BUSY 0xFFFF000D
#define TEEC_ERROR_COMMUNICATION 0xFFFF000E
#define TEEC_ERROR_SECURITY 0xFFFF000F
#define TEEC_ERROR_SHORT_BUFFER 0xFFFF0010
数据结构
AES模式类型
typedef enum {
TEE_AES_MODE_ECB = 0, ///< TEE AES ECB mode
TEE_AES_MODE_CBC = 1, ///< TEE AES CBC mode
TEE_AES_MODE_MAX, ///< invaild mode
} tee_aes_mode_e;
低功耗操作类型
typedef enum {
TEE_LPM_MODE_WAIT = 0, ///< lpm wait
TEE_LPM_MODE_DOZE = 1, ///< lpm doze
TEE_LPM_MODE_STOP = 2, ///< lpm stop
TEE_LPM_MODE_STANDBY = 3, ///< lpm standby
TEE_LPM_MODE_CLOCK = 4, ///< lpm clock gate
TEE_LPM_MODE_MAX,
} tee_lpm_mode_e;
RSA签名类型
typedef enum {
TEE_RSA_MD5 = 0, ///< MD5
TEE_RSA_SHA1 = 1, ///< SHA1
TEE_RSA_SHA256 = 3, ///< SHA256
TEE_RSA_SIGN_TYPE_MAX, ///< invailed type
} tee_rsa_sign_type_e;
RSA 填充类型
typedef enum {
TEE_RSA_PKCS1_PADDING = 0x01, ///< RSA PKCS padding mode
TEE_RSA_NO_PADDING = 0x02, ///< RSA no padding mode
} tee_rsa_padding_mode_e;
SHA 运算操作类型
typedef enum {
TEE_HASH_OP_NONE = 0, ///< No operation
TEE_HASH_OP_START = 1, ///< HASH init
TEE_HASH_OP_UPDATA = 2, ///< HASH update
TEE_HASH_OP_FINISH = 3, ///< HASH finish
TEE_HASH_OP_MAX, ///< invailed operation
} tee_hash_op_e;
SHA 运算模式类型
typedef enum {
TEE_SHA1 = 0, ///< SHA1
TEE_SHA256 = 1, ///< SHA256
TEE_SHA224 = 2, ///< SHA224
TEE_SHA384 = 3, ///< SHA384
TEE_SHA512 = 4, ///< SHA512
TEE_SHA_MAX, ///< invaild sha type
} tee_sha_type_t;
系统时钟类型
typedef enum {
IHS_CLK = 0, /* internal high speed clock */
EHS_CLK = 1, /* external high speed clock */
ILS_CLK = 2, /* internal low speed clock */
ELS_CLK = 3 /* enternal low speed clock */
} clk_src_e;
系统时钟频率
typedef enum {
OSR_8M_CLK_16M = 0x80204, ///< register value for clock 16M
OSR_8M_CLK_24M = 0x80206, ///< register value for clock 24M
OSR_8M_CLK_32M = 0x80208, ///< register value for clock 32M
OSR_8M_CLK_40M = 0x8020a, ///< register value for clock 40M
OSR_8M_CLK_48M = 0x8020c ///< register value for clock 48M
} clk_val_e;
AES ECB模式加密
int32_t csi_tee_aes_encrypt_ecb(const uint8_t *in,
uint32_t in_len,
uint8_t *key,
uint32_t key_len,
uint8_t *out);
- 参数
- in: 输入数据缓冲区指针
- in_len: 输入数据长度
- key: 输入加密密钥缓冲区指针
- key_len: 输入加密密钥长度
- out: 输出数据缓冲区指针
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
AES ECB模式解密
int32_t csi_tee_aes_decrypt_ecb(const uint8_t *in,
uint32_t in_len,
uint8_t *key,
uint32_t key_len,
uint8_t *out);
- 参数
- in: 输入数据缓冲区指针
- in_len: 输入数据长度
- key: 输入解密密钥缓冲区指针
- key_len: 输入解密密钥长度
- out: 输出数据缓冲区指针
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
AES CBC模式加密
int32_t csi_tee_aes_encrypt_cbc(const uint8_t *in,
uint32_t in_len,
uint8_t *key,
uint32_t key_len,
uint8_t *iv,
uint8_t *out);
- 参数
- in: 输入数据缓冲区指针
- in_len: 输入数据长度
- key: 输入加密密钥缓冲区指针
- key_len: 输入加密密钥长度
- iv: 输入的iv向量
- out: 输出数据缓冲区指针
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
AES CBC模式解密
int32_t csi_tee_aes_decrypt_cbc(const uint8_t *in,
uint32_t in_len,
uint8_t *key,
uint32_t key_len,
uint8_t *iv,
uint8_t *out);
- 参数
- in: 输入数据缓冲区指针
- in_len: 输入数据长度
- key: 输入解密密钥缓冲区指针
- key_len: 输入解密密钥长度
- iv: 输入的iv向量
- out: 输出数据缓冲区指针
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
RSA 签名
int32_t csi_tee_rsa_sign(const uint8_t *in, uint32_t in_len,
const uint8_t *key, uint32_t key_len,
uint8_t *sign, uint32_t *sign_len,
tee_rsa_sign_type_e type);
- 参数
- in: 输入数据缓冲区指针
- in_len: 输入数据长度
- key: 输入密钥缓冲区指针
- key_len: 输入密钥长度
- sign: 签名数据缓冲区指针
- sign_len: 签名数据长度变量指针
- type: 签名类型
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
RSA 验签
int32_t csi_tee_rsa_verify(const uint8_t *in, uint32_t in_len,
const uint8_t *key, uint32_t key_len,
uint8_t *sign, uint32_t sign_len,
tee_rsa_sign_type_e type);
- 参数
- in: 输入数据缓冲区指针
- in_len: 输入数据长度
- key: 输入密钥缓冲区指针
- key_len: 输入密钥长度
- sign: 签名数据缓冲区指针
- sign_len: 签名数据长度变量指针
- type: 签名类型
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
RSA 加密
int32_t csi_tee_rsa_encrypt(const uint8_t *in, uint32_t in_len,
const uint8_t *key, uint32_t key_len,
uint8_t *out, uint32_t *out_len,
tee_rsa_padding_mode_e padding);
- 参数
- in: 输入数据缓冲区指针
- in_len: 输入数据长度
- key: 输入密钥缓冲区指针
- key_len: 输入密钥长度
- out: 密文数据缓冲区指针
- out_len: 密文数据长度变量指针
- padding: 填充算法类型
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
RSA 解密
int32_t csi_tee_rsa_decrypt(const uint8_t *in, uint32_t in_len,
const uint8_t *key, uint32_t key_len,
uint8_t *out, uint32_t *out_len,
tee_rsa_padding_mode_e padding);
- 参数
- in: 输入数据缓冲区指针
- in_len: 输入数据长度
- key: 输入密钥缓冲区指针
- key_len: 输入密钥长度
- out: 明文数据缓冲区指针
- out_len: 明文数据长度变量指针
- padding: 填充算法类型
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
SHA 运算操作启动
int32_t csi_tee_sha_start(tee_sha_type_t type, void *ctx);
- 参数
- type: 哈希运算类型
- ctx: 哈希运算上下文指针
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
SHA 运算操作更新
int32_t csi_tee_sha_update(const uint8_t *in, uint32_t in_len, void *ctx);
- 参数
- in: 输入数据缓冲区指针
- in_len: 输入数据长度
- ctx: 哈希运算上下文指针
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
SHA 运算操作结束
int32_t csi_tee_sha_finish(uint8_t *out, vid *ctx);
- 参数
- out: 输出数据缓冲区指针
- ctx: 哈希运算上下文指针
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
SHA 摘要运算
int32_t csi_tee_sha_digest(const uint8_t *in, uint32_t in_len, uint8_t *out, tee-sha_type_t type);
- 参数
- in: 输入数据缓冲区指针
- in_len: 输入数据长度
- out: 输出数据缓冲区指针
- type: 哈希运算类型
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
低功耗操作
int32_t csi_tee_enter_lpm(uint32_t gate, uint32_t irqid, tee_lpm_mode_e mode);
- 参数
- gate: IP clk gate配置字,具体参考PMU 寄存器说明
- irqid: 保留
- mode: 索要进入低功耗的模式
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
设置系统频率
int32_t csi_tee_set_sys_freq(uint32_t clk_src, uint32_t clk_val);
- 参数
- clk_src: 系统内部或者外部时钟源选择
- clk_val: 需要设置的系统频率
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败
获取系统频率
int32_t csi_tee_get_sys_freq(uint32_t *clk_val);
- 参数
- clk_val: 存放系统频率的数据变量指针
- 返回值
- ==TEEC_SUCCESS: 成功
- !=TEEC_SUCCESS: 失败