第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > OPTEE的进程间通信(Inter-Process Communication IPC)

OPTEE的进程间通信(Inter-Process Communication IPC)

时间:2023-06-09 11:35:42

相关推荐

OPTEE的进程间通信(Inter-Process Communication  IPC)

好久没有翻看这本书了,今天来看看。

《手机安全和可信应用开发指南》

1、什么是IPC

进程间通信(Inter-Process Communication, IPC)机制是指系统中进程或线程之间的通信机制,用于实现线程与线程之间进行通信、数据交互等功能。Linux具有多种方式能够实现进程或线程之间的通信和数据共享,例如:消息队列、信号量、共享内存等。而在OP-TEE中并未提供如此丰富的IPC方法,本章将介绍OP-TEE中的IPC机制。

2、IPC的作用

动态TA是以线程的方式运行于OP-TEE的用户空间,OP-TEE的IPC机制用于实现各线程之间的相互调用、线程调用安全驱动、线程调用OP-TEE内核空间的服务

OP-TEE中并未有类似消息队列、信号量等专门用于线程间通信的机制,但OP-TEE提供动态TA调用其他TA或安全驱动的方法和接口,从而实现OP-TEE中各线程间的通信。

是什么方法和接口呢?

3、IPC机制的原理

OP-TEE中的IPC机制主要是为满足OP-TEE用户空间运行的线程调用其他线程、静态TA、安全驱动的需求。其原理的核心是利用系统调用来访问其他线程或者安全驱动。

当线程需要调用其他线程或者安全驱动时,首先会通过系统调用陷入到OP-TEE的内核态,然后执行类似CA调用TA的操作,建立会话并通过调用命令的方式让其他TA来完成相应的操作。

线程调用安全驱动时,同样是通过调用系统调用陷入到OP-TEE的内核态,然后调用服务或安全驱动提供给OP-TEE内核空间的接口来完成TA对安全驱动和服务的调用。关于OP-TEE中系统调用的实现和定义方式可参考本书第16章。

4、怎么做一个IPC

OP-TEE的IPC机制是通过系统调用陷入到内核中来实现的。

调用其他TA的操作有专门的接口,而访问安全驱动和OP-TEE的服务则是通过在内核态中调用服务提供的内核级接口来实现的。

4.1 TA调用其他TA的实现

一个TA调用其他TA时,OP-TEE通过建立两者间的会话,并调用命令来实现。GP规范定义了如表17-1中的三个接口,这些接口可在OP-TEE的用户空间被调用。

当一个TA需要调用其他的TA时,首先需要使用TEE_OpenTASession创建两个TA之间的会话,再使用TEE_InvokeTACommand调用到已经建立的会话的TA中的具体操作,待不再需要调用其他TA时,则调用TEE_CloseTASession函数关闭会话来断开两个TA间的联系。(书里这里有点小错误写成了TEE_InvokeTACommand)

4.1.1 TEE_OpenTASession的实现

TEE_OpenTASession的实现与CA中创建与TA的会话的过程大致相同,但TEE_OpenTASession是通过系统调用的方式来触发OP-TEE分配线程并创建会话,而CA则是通过触发安全监控模式调用(smc)来让OP-TEE分配线程并创建会话。TEE_OpenTASession操作的整体流程如图17-1所示。

函数执行到tee_ta_open_session后,其操作与在CA创建会话的操作完全一致,syscall_open_ta_session函数的说明如下:

TEE_Result syscall_open_ta_session(const TEE_UUID *dest,unsigned long cancel_req_to,struct utee_params *usr_param, uint32_t *ta_sess,uint32_t *ret_orig){TEE_Result res;uint32_t ret_o = TEE_ORIGIN_TEE;struct tee_ta_session *s = NULL;struct tee_ta_session *sess;struct mobj *mobj_param = NULL;TEE_UUID *uuid = malloc(sizeof(TEE_UUID));struct tee_ta_param *param = malloc(sizeof(struct tee_ta_param));TEE_Identity *clnt_id = malloc(sizeof(TEE_Identity));void *tmp_buf_va[TEE_NUM_PARAMS];struct user_ta_ctx *utc;/* 参数合法性检查 */if (uuid == NULL || param == NULL || clnt_id == NULL) {res = TEE_ERROR_OUT_OF_MEMORY;goto out_free_only;}/* 清空分配的param变量中的数据 */memset(param, 0, sizeof(struct tee_ta_param));/* 获取当前TA的会话信息 */res = tee_ta_get_current_session(&sess);if (res ! = TEE_SUCCESS)goto out_free_only;utc = to_user_ta_ctx(sess->ctx);/* 将用户空间传递的UUID值复制到内核空间中 */res = tee_svc_copy_from_user(uuid, dest, sizeof(TEE_UUID));if (res ! = TEE_SUCCESS)goto function_exit;/* 设定login方式并设定clnt_id->uuid的值,即让当前TA认为是client端*/clnt_id->login = TEE_LOGIN_TRUSTED_APP;memcpy(&clnt_id->uuid, &sess->ctx->uuid, sizeof(TEE_UUID));/* 复制用户空间传递的参数数据到内核空间 */res = tee_svc_copy_param(sess, NULL, usr_param, param, tmp_buf_va,&mobj_param);if (res ! = TEE_SUCCESS)goto function_exit;/* 执行创建会话操作*/res = tee_ta_open_session(&ret_o, &s, &utc->open_sessions, uuid,clnt_id, cancel_req_to, param);if (res ! = TEE_SUCCESS)goto function_exit;/* 更新param的内容 */res = tee_svc_update_out_param(sess, s, param, tmp_buf_va, usr_param);function_exit:if (mobj_param) {mutex_lock(&tee_ta_mutex);mobj_free(mobj_param);mutex_unlock(&tee_ta_mutex);}if (res == TEE_SUCCESS)tee_svc_copy_kaddr_to_uref(ta_sess, s); //将获得的会话的ID值复制到用户空间//复制执行函数的返回值到用户空间tee_svc_copy_to_user(ret_orig, &ret_o, sizeof(ret_o));out_free_only:free(param);free(uuid);free(clnt_id);return res;}

关于tee_ta_open_session函数的执行过程可参阅本书第13章。

4.1.2 TEE_InvokeTACommand的实现

调用TEE_InvokeTACommands时带入命令ID的值就能调用TA中具体的命令,其过程与CA的命令调用操作几乎一致,该接口的执行流程如图17-2所示。

Syscall_invoke_ta_command的内容和说明如下:

TEE_Result syscall_invoke_ta_command(unsigned long ta_sess,unsigned long cancel_req_to, unsigned long cmd_id,struct utee_params *usr_param, uint32_t *ret_orig){TEE_Result res;TEE_Result res2;uint32_t ret_o = TEE_ORIGIN_TEE;struct tee_ta_param param = { 0 };TEE_Identity clnt_id;struct tee_ta_session *sess;struct tee_ta_session *called_sess;struct mobj *mobj_param = NULL;void *tmp_buf_va[TEE_NUM_PARAMS];struct user_ta_ctx *utc;/* 获取当前的TA的session信息 */res = tee_ta_get_current_session(&sess);if (res ! = TEE_SUCCESS)return res;utc = to_user_ta_ctx(sess->ctx);/* 根据session ID从保存已经open的session链表中找到对应的session */called_sess = tee_ta_get_session((vaddr_t)tee_svc_uref_to_kaddr(ta_sess), true,&utc->open_sessions);if (! called_sess)return TEE_ERROR_BAD_PARAMETERS;/* 设定clnt_id的内容,将调用者作为client端处理 */clnt_id.login = TEE_LOGIN_TRUSTED_APP;memcpy(&clnt_id.uuid, &sess->ctx->uuid, sizeof(TEE_UUID));/* 复制从用户空间传入的参数 */res = tee_svc_copy_param(sess, called_sess, usr_param, &param,tmp_buf_va, &mobj_param);if (res ! = TEE_SUCCESS)goto function_exit;/* 开始调用找到的session中的invoke command,根据command ID执行指定的操作 */res = tee_ta_invoke_command(&ret_o, called_sess, &clnt_id,cancel_req_to, cmd_id, &param);/* 更新执行结果到输出参数 */res2 = tee_svc_update_out_param(sess, called_sess, &param, tmp_buf_va,usr_param);if (res2 ! = TEE_SUCCESS) {ret_o = TEE_ORIGIN_TEE;res = res2;}function_exit:tee_ta_put_session(called_sess);if (mobj_param) {mutex_lock(&tee_ta_mutex);mobj_free(mobj_param);mutex_unlock(&tee_ta_mutex);}if (ret_orig)tee_svc_copy_to_user(ret_orig, &ret_o, sizeof(ret_o));return res;}

关于tee_ta_invoke_command函数的执行过程可参阅第13章。

4.1.3 TEE_CloseTASession的实现

TEE_CloseTASession接口用于断开TA与其他TA之间的连接,其过程与CA的关闭会话操作几乎一致,该接口的执行流程如图17-3所示。

调用TEE_CloseTASession接口时会产生系统调用,系统会执行关闭会话的操作,syscall_close_ta_session函数的内容和说明如下:

TEE_Result syscall_close_ta_session(unsigned long ta_sess){TEE_Result res;struct tee_ta_session *sess;TEE_Identity clnt_id;struct tee_ta_session *s = tee_svc_uref_to_kaddr(ta_sess);struct user_ta_ctx *utc;/* 获取当前TA的session信息 */res = tee_ta_get_current_session(&sess);if (res ! = TEE_SUCCESS)return res;utc = to_user_ta_ctx(sess->ctx);/* 设定clnt_id信息 */clnt_id.login = TEE_LOGIN_TRUSTED_APP;memcpy(&clnt_id.uuid, &sess->ctx->uuid, sizeof(TEE_UUID));/* 将需要被关闭的session从保存已经Open的session链表中移除 */return tee_ta_close_session(s, &utc->open_sessions, &clnt_id);}

4.2 TA调用系统服务和安全驱动的实现

动态TA实现具体功能时需要调用到安全驱动或系统底层的资源。例如密码学操作、加载TA镜像文件操作、对SE模块的操作等。

这些资源提供的接口都处于OP-TEE的内核空间,当用户空间的TA需要使用这些资源来实现具体功能时,则需要让TA的调用操作通过系统调用的方式进入到内核空间,然后再调用特定的接口。

4.2.1 OP-TEE中服务和安全驱动的构成框架

OP-TEE使用系统服务的方式统一管理各功能模块,安全驱动的操作接口会接入到系统服务中,系统服务是在OP-TEE启动过程中执行initcall段中的内容时被启动service_init的启动等级设置为1而driver_init的启动等级设置成3

故在OP-TEE的启动过程中,首先会启动使用service_init宏定义的功能函数,再初始化安全驱动。各系统服务、安全驱动和上层的TA之间的关系如图17-4所示。

(这个部分在那个怎么写一个安全驱动的学习笔记中,有详细—》安全驱动怎么设计(一))

OP-TEE中的系统服务提供了类似框架层的功能安全驱动初始化时会将驱动的操作接口注册到对应的系统服务。TA可使用的只是各系统服务提供的接口,如果系统服务并不需要给上层TA使用,则不会暴露对应的接口给TA。

当前的OP-TEE中提供了如下三个重要的系统服务:

□ 密码学操作的系统服务;□ 对SE功能模块进行操作的系统服务;□ 提供加载TA镜像操作的系统服务;

4.2.2 TA对系统服务接口的调用实现

动态TA通过系统调用的方式进入到内核态,然后在内核态调用各系统服务提供的接口。系统服务为OP-TEE用户态程序提供的接口定义在类似于tee_api_xxx.c的文件中,这些文件根据不同的功能模块定义了用户空间需要使用的密码学操作接口、SE操作接口等。

这些接口的调用过程大致相同,如图17-5所示。

(当时就说了,这个系统服务可能是一个类的归纳,而不是把所有的混合到一起了)

用户态的TA通过系统调用陷入OP-TEE内核空间,然后在对应的系统调用中使用系统服务提供的接口或变量来完成对安全驱动或其他资源的操作。

4.2.3 TA对密码学系统服务的调用实现

TA需要实现计算摘要、产生随机数、加解密、签名验签等操作时就会调用到密码学系统服务提供的接口。OP-TEE的内核空间中有一个变量—**—crypto_ops,**该变量中保存了各种密码学算法的调用接口,其内容如下:

const struct crypto_ops crypto_ops = {.name = "LibTomCrypt provider",//该系统服务的名字//crypto service的初始化函数,在启动过程中将会执行crypto_ops.init指定的函数.init = tee_ltc_init,/* hash类算法的接口,用于计算摘要 */#if defined(_CFG_CRYPTO_WITH_HASH).hash = {.get_ctx_size = hash_get_ctx_size,.init = hash_init,.update = hash_update,.final = hash_final,},#endif/* 对称加解密算法的接口用于对称加解密 */#if defined(_CFG_CRYPTO_WITH_CIPHER).cipher = {.final = cipher_final,.get_block_size = cipher_get_block_size,.get_ctx_size = cipher_get_ctx_size,.init = cipher_init,.update = cipher_update,},#endif/* MAC类算法接口 */#if defined(_CFG_CRYPTO_WITH_MAC).mac = {.get_ctx_size = mac_get_ctx_size,.init = mac_init,.update = mac_update,.final = mac_final,},#endif/* 对称验证加解密算法接口 */#if defined(_CFG_CRYPTO_WITH_AUTHENC).authenc = {.dec_final = authenc_dec_final,.enc_final = authenc_enc_final,.final = authenc_final,.get_ctx_size = authenc_get_ctx_size,.init = authenc_init,.update_aad = authenc_update_aad,.update_payload = authenc_update_payload,},#endif/* 非对称算法(RSA)加解密,签名验签操作接口 */#if defined(_CFG_CRYPTO_WITH_ACIPHER).acipher = {#if defined(CFG_CRYPTO_RSA).alloc_rsa_keypair = alloc_rsa_keypair,.alloc_rsa_public_key = alloc_rsa_public_key,.free_rsa_public_key = free_rsa_public_key,.gen_rsa_key = gen_rsa_key,.rsaes_decrypt = rsaes_decrypt,.rsaes_encrypt = rsaes_encrypt,.rsanopad_decrypt = rsanopad_decrypt,.rsanopad_encrypt = rsanopad_encrypt,.rsassa_sign = rsassa_sign,.rsassa_verify = rsassa_verify,#endif/* 生成key的接口 */#if defined(CFG_CRYPTO_DH).alloc_dh_keypair = alloc_dh_keypair,.gen_dh_key = gen_dh_key,.dh_shared_secret = do_dh_shared_secret,#endif/* DSA算法接口 */#if defined(CFG_CRYPTO_DSA).alloc_dsa_keypair = alloc_dsa_keypair,.alloc_dsa_public_key = alloc_dsa_public_key,.gen_dsa_key = gen_dsa_key,.dsa_sign = dsa_sign,.dsa_verify = dsa_verify,#endif/* ECC算法接口 */#if defined(CFG_CRYPTO_ECC)/* ECDSA and ECDH */.alloc_ecc_keypair = alloc_ecc_keypair,.alloc_ecc_public_key = alloc_ecc_public_key,.gen_ecc_key = gen_ecc_key,.free_ecc_public_key = free_ecc_public_key,/* ECDSA only */.ecc_sign = ecc_sign,.ecc_verify = ecc_verify,/* ECDH only */.ecc_shared_secret = do_ecc_shared_secret,#endif},/* 大整数操作接口 */.bignum = {.allocate = bn_allocate,.num_bytes = num_bytes,.num_bits = num_bits,.compare = compare,.bn2bin = bn2bin,.bin2bn = bin2bn,.copy = copy,.free = bn_free,.clear = bn_clear},#endif /* _CFG_CRYPTO_WITH_ACIPHER *//* 随机系列算法接口 */.prng = {.add_entropy = prng_add_entropy,.read = prng_read,}};

1. crypto service的初始化

OP-TEE在启动时会调用crypto_ops.init指定的函数初始化整个密码学系统服务,即调用tee_ltc_init函数来初始化密码学系统服务,该函数将各种密码学算法的操作接口都注册到特定的变量中,这些变量与对应算法的关系如表17-2所示。

启动密码学系统服务时,会调用tee_ltc_reg_algs函数将对应算法的操作接口注册到相关变量中,该函数内容如下:

static void tee_ltc_reg_algs(void){#if defined(CFG_CRYPTO_AES)register_cipher(&aes_desc); //注册AES算法的操作接口到cipher_descriptor中#endif#if defined(CFG_CRYPTO_DES)register_cipher(&des_desc); //注册DES算法的操作接口到cipher_descriptor中register_cipher(&des3_desc); //注册DES3算法的操作接口到cipher_descriptor中#endif#if defined(CFG_CRYPTO_MD5)register_hash(&md5_desc);//注册MD5算法的操作接口到hash_descriptor中#endif#if defined(CFG_CRYPTO_SHA1)register_hash(&sha1_desc);//注册SHA1算法的操作接口到hash_descriptor中#endif#if defined(CFG_CRYPTO_SHA224)register_hash(&sha224_desc); //注册SHA224算法的操作接口到hash_descriptor中#endif#if defined(CFG_CRYPTO_SHA256)register_hash(&sha256_desc); //注册SHA256算法的操作接口到hash_descriptor中#endif#if defined(CFG_CRYPTO_SHA384)register_hash(&sha384_desc); //注册SHA384算法的操作接口到hash_descriptor中#endif#if defined(CFG_CRYPTO_SHA512)register_hash(&sha512_desc); //注册SHA512算法的操作接口到hash_descriptor中#endif//注册prng算法的操作接口到prng_descriptor中#if defined(CFG_WITH_SOFTWARE_PRNG)#if defined(_CFG_CRYPTO_WITH_FORTUNA_PRNG)register_prng(&fortuna_desc);#elseregister_prng(&rc4_desc);#endif#elseregister_prng(&prng_mpa_desc);#endif}

注册过程就是将具体密码学算法的operation变量保存到对应的数组变量元素中。密码学系统服务初始化完成后,内核空间通过调用crypto_ops.xxx.xxx的方式可调用到各种密码学算法的具体实现。

2. TA调用具体算法的实现

调用crypto_ops中的接口时,会根据需要被调用密码学算法的名称从数组变量中找到对应的元素,然后使用元素中保存的算法操作接口来完成密码学操作。

如果芯片集成了硬件加解密引擎,加密算法的实现,则可使用硬件cipher驱动提供的接口来完成。本节以调用SHA1算法为例介绍其实现过程。

(一般的芯片厂,为了安全,都会自己是实现一些cipher,派生密钥,实现加密算法。)

在TA中如果需要使用SHA1算法计算数据的摘要,则需要调用TEE_DigestUpdate接口来实现,该函数的完整执行过程如图17-6所示。

其他算法接口的调用过程与图17-6类似,只是不同的算法类型查找的数组变量会有所不同,但是执行流程大致相同。

4.2.4 对SE功能模块进行操作的系统服务

在OP-TEE内核空间调用类似tee_se_reader_xxx的接口会调用到OP-TEE的SE系统服务,用于操作具体的SE模块。

若需要在TA中操作SE模块,可将tee_se_reader_xxx类型的接口重新封装成系统调用,然后在TA中调用封装的接口就能实现TA对SE模块的操作。

在OP-TEE中要使用具体的SE模块需要初始化SE功能模块的系统服务,并挂载具体SE模块的驱动。

SE模块的系统服务是通过在OP-TEE启动过程中调用tee_se_manager_init函数来实现的,该函数只会初始化该系统服务的上下文空间,函数内容如下:

static TEE_Result tee_se_manager_init(void){//定义SE service的上下文变量struct tee_se_manager_ctx *ctx = &se_manager_ctx;context_init(ctx); //初始化该上下文变量的内容return TEE_SUCCESS;}

SE系统服务的上下文变量初始化完成后,就需要挂载具体的SE模块驱动,将SE的操作接口注册到上下文中。驱动的挂载和注册过程如图17-7所示。

调用tee_se_reader_xxx类接口操作SE模块时会获取SE系统服务的上下文——se_manager_ctx,然后根据实际操作需求调用pcsc_passthru_reader_ops变量中对应接口。

pcsc_passthru_reader_ops变量中的接口会根据需要操作的proxy编号找到具体的proxy,然后调用该proxy中对应的接口完成整个操作。

4.2.5 加载TA镜像的系统服务

当CA调用libteec库中用于创建与某个动态TA的会话时,会从REE侧的文件系统中加载TA镜像文件到OP-TEE,加载TA镜像的过程就会使用到该系统服务提供的接口函数。

本书第13章详细介绍了OP-TEE创建会话的实现过程。

OP-TEE会使用tee_ta_init_user_ta_session函数来完成加载TA镜像并初始化会话的操作。

加载TA镜像文件时,会使用user_ta_store变量中的接口发送RPC请求,通知tee_supplicant对REE侧文件系统中的TA镜像文件执行打开、读取、获取TA镜像文件大小、关闭TA镜像文件的操作。

user_ta_store变量在该系统服务启动时被赋值,具体函数内容如下:

static const struct user_ta_store_ops ops = {.open = ta_open, //发送RPC请求使tee_supplicant打开TA镜像文件.get_size = ta_get_size,//发送RPC请求,获取TA镜像文件的大小.read = ta_read, //发送RPC请求读取TA镜像的内容.close = ta_close, //发送RPC请求关闭打开的TA镜像文件};/* OP-TEE启动时被调用,使用service_init宏将该函数编译到initcall段中 */static TEE_Result register_supplicant_user_ta(void){return tee_ta_register_ta_store(&ops);}/* 将user_ta_store变量的地址赋值成ops */TEE_Result tee_ta_register_ta_store(const struct user_ta_store_ops *ops){user_ta_store = ops;return TEE_SUCCESS;}

5、 summary

1、介绍了OP-TEE中各种系统服务以及TA调用另外一个TA的原理和实现

2、每个TA具有独立的运行空间,OP-TEE中的一个TA调用另一个TA执行特定操作的过程是OP-TEE中的一种IPC的方式。

3、OP-TEE中各种系统服务起到类似框架层的作用,安全驱动或其他子模块提供的操作接口会接入到对应的系统服务中。

4、系统服务通过接口变量或其他方式操作接口暴露给OP-TEE的内核空间,用户空间的TA通过系统调用的方式在OP-TEE内核空间调用这些接口,从而实现TA对安全驱动或其他模块的资源操作。

bravo

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。
扩展阅读