ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

关于TA的签名、验签、加载以及调用的学习笔记

2021-12-15 18:30:33  阅读:521  来源: 互联网

标签:session 调用 TA shdr 验签 加载 open ta


TA验签加载与调用

TA的签名

以optee-os 3.11版本为例。在optee_os目录下,存放着签名的私钥和签名脚本。
工程目录/optee_os/keys/default_ta.pem
工程目录/optee_os/scripts/sign_encrypt.py
编译optee-os会将TA编译为elf文件。此时执行签名脚本,对elf文件签名并生成.ta文件。此脚本还会放置头部数据,shdr,放在TA镜像的头部。头部放置了magic number,需要和optee-os代码里的magic number匹配。其他的就是签名相关的数据。

/* struct shdr - signed header*/
struct shdr {
	uint32_t magic;
	uint32_t img_type;
	uint32_t image_size;
	uint32_t algo;
	uint16_t hash_size;
	uint16_t sig_size;
};

下图是签名后的TA镜像文件的内容。
在这里插入图片描述

TA的验签

在编译optee_os时,pem_to_pub_c.py脚本中将default_ta.pem中解析出der格式rsa_pub公钥,放到了ta_pub_key.c文件的ta_pub_key_modulus数组中,此时等于说公钥是放在.rodata段。此数组在optee-os加载TA时shdr_verify_signature函数会从此数组里获取公钥用于验签TA。验签时只验头部shdr,通过rpc调用到ree侧的tee_supplicant,此时会先加载TA的头shdr,然后调用shdr_verify_signature读取公钥和shdr的签名信息验签。

/* 存放公钥的数组 */
const unit8_t ta_pub_key_modulus[];
/* Validate header signature */
res = shdr_verify_signature(shdr);

TA的加载

动态TA的加载

在这里插入图片描述

通过GP标准调用的与TA通信的命令(opensession\invoke\closession)其实都是std smc call,该smc调用后,会进入到TEE中的tee_entry_std中。
tee_entry_std函数中调用就是opensession\invoke\closession的接口。调用到entry_open_session接口。动态TA最终会在tee_ta_init_user_ta_session函数里加载。

ldelf

tee_ta_init_user_ta_session里调用load_ldelf函数。其实现大致为,在代码编译的时候,optee-os下的ldelf目录下编译出ldelf.elf二进制文件,通过gen_ldelf_hex.py脚本将二进制文件生成ldelf_hex.c文件。其二进制数据会被写入数组ldelf_data,还有代码段,数据段的大小。在load_ldelf函数中将其加载到内存中。再通过init_with_ldelf函数调用thread_enter_user_mode函数切换到用户模式el0,运行此ldelf的代码,ldelf其作用为load需要加载的TA。调用ldelf → ta_elf_load_main → load_main加载TA的二进制文件,通过系统调用到optee内核再由内核切到system.PTA,此TA加载动态TA的镜像。invoke到此TA,调用system_open_ta_binary。此函数会遍历注册的TA_STORE,调用open等操作函数。
3.11的optee-os的版本里注册的是下面两个。

TEE_TA_REGISTER_TA_STORE(9) = {
	.description = "REE",
	.open = ree_fs_ta_open,
	.get_size = ree_fs_ta_get_size,
	.get_tag = ree_fs_ta_get_tag,
	.read = ree_fs_ta_read,
	.close = ree_fs_ta_close,
};
TEE_TA_REGISTER_TA_STORE(4) = {
	.description = "Secure Storage TA",
	.open = secstor_ta_open,
	.get_size = secstor_ta_get_size,
	.get_tag = secstor_ta_get_tag,
	.read = secstor_ta_read,
	.close = secstor_ta_close,
};

ree_fs_ta_open函数解析:
先调用rpc_load:通过rpc调用ree侧的tee_supplicant ,传入uuid,ree侧将uuid对应的TA二进制文件加载到共享内存中,在rpc_load中包含两次TA加载的rpc请求,第一次参数里只有UUID,因为此时optee-os不知道加载的TA的image大小,tee supplicant接收到TA加载请求后由于解析出参数的image size为0,所以其通过UUID读取TA文件后计算出ta_size,发现传入大小不足,所以此时没有加载TA 并返回需要传入的size给到optee-os的rpc_load(),其得到size后通过rpc请求size大小的共享内存并得到地址,此时将uuid、共享内存地址、size传入作为第二次rpc加载TA请求,tee-suplicant收到后加载TA到对应的共享内存中。

/* Request TA from tee-supplicant */
res = rpc_load(uuid, &ta, &ta_size, &mobj);

接着调用shdr_alloc_and_copy:通过rpc_load()之后得到的TA加载的共享内存地址,因为TA镜像在签名时增加签名相关的头部文件shdr,在此函数中计算shdr的大小,struct shdr + hash_size + sig_size。然后申请此大小的内存并将TA镜像的shdr拷贝到申请的内存里。用于后面的验签流程。

/* Make secure copy of signed header */
shdr = shdr_alloc_and_copy(ta, ta_size);

接着调用shdr_verify_signature执行验签工作。传入镜像头部文件shdr指针。shdr包括了magic、img_type、img_size、algo等参数。先检验代码中的magic与shdr中的是否一致,从前面提到的ta_pub_key.c中读取ta_pub_key_modulus,提取出pub_key,从shdr中提取hash和sig,使用rsa算法验签此shdr。

/* Validate header signature */
res = shdr_verify_signature(shdr);

然后在ree_fs_ta_open()里还要取出TA镜像里的uuid与传入的uuid比较,并初始化hash_ctx。
ldelf完成后返回到optee-os内核中。

TA的调用

在这里插入图片描述

动态TA的调用

从前面的ldelf回到tee_ta_open_session函数。在此函数中调用了user_ta注册的enter_open_session。

res = ctx->ops->enter_open_session(s, param, err);
static const struct tee_ta_ops user_ta_ops __rodata_unpaged = {
	.enter_open_session = user_ta_enter_open_session,
	.enter_invoke_cmd = user_ta_enter_invoke_cmd,
	.enter_close_session = user_ta_enter_close_session,
	.dump_state = user_ta_dump_state,
#ifdef CFG_FTRACE_SUPPORT
	.dump_ftrace = user_ta_dump_ftrace,
#endif
	.destroy = user_ta_ctx_destroy,
	.get_instance_id = user_ta_get_instance_id,
	.handle_svc = user_ta_handle_svc,
};

而不论是open_session、close_session、invoke_cmd等CA调用,到了optee-os内核中都是通过user_ta_enter函数处理的。在此函数中调用thread_enter_user_mode切换到el0,执行TA。TA入口为__ta_entry,根据不同的调用命令,选择不同的执行函数(open、close、invoke),此处为open_session,进入entry_open_session,最后执行到TA_OpenSessionEntryPoint的GP接口。

静态TA的调用

静态TA,也就是PTA,其作为optee内核的一部分被编译进内核。其不需要加载,CA调用的open/invoke/close和动态TA一样,与动态TA不同的是,调用动态TA实际对应的是user_ta_ops 注册的操作函数,也就是上面提到的。而调用静态TA对应的接口变为了下面的pseudo_ta_ops 接口,对应注册PTA的这些接口,open_session_entry_point、invoke_command_entry_point 等。所以CA的调用流程是一样的。

static const struct tee_ta_ops pseudo_ta_ops = {
	.enter_open_session = pseudo_ta_enter_open_session,
	.entry_invoke_cmd = pseudo_ta_enter_invoke_cmd,
	.enter_close_session = pseudo_ta_enter_close_session,
	.destroy = pseudo_ta_destroy,
};
pseudo_ta_register(.uuid = PTA_INVOKE_TEST_UUID, .name = TA_NAME,
		   .flag = PTA_DEFAULT_FLAGS | TA_FLAG_SECURE_DATA_PATH |
				   TA_FLAG_CONCURRENT | TA_FLAG_DEVICE_ENUM,
		   .create_entry_point = create_ta,
		   .destroy_entry_point = destroy_ta,
		   .open_session_entry_point = open_session,
		   .close_session_entry_point = close_session,
		   .invoke_command_entry_point = invoke_command);

标签:session,调用,TA,shdr,验签,加载,open,ta
来源: https://blog.csdn.net/qq_42878531/article/details/121783732

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有