【CA-TA实战系列一】如何创建一个CA

这个系列计划分为三篇:

  • 1、如何创建一个CA
  • 2、如何创建一个TA
  • 3、拆解CA与TA交互的过程

之前其实一直打算做这个系列,但是因为不是强相关,就把这个事情拖沓了。这次倒是安排的任务基于之前的老代码就能实现,然后我就是增加一些维测手段,定位问题修改即可。但是对这个CA和TA的学习还是停靠在之前学习《手机安全和可信应用开发指南:TrustZone与OP-TEE技术详解 》这本书的内容,这个专栏的内容也大多数是来自与这本书和一些前辈们的优秀blog。

尽量今年今晚能把这个系列写出来,实在不行先写个浅入版本,后续再整个源码版本。

1、开发CA的基础概念

开发CA的时候,CA调用的接口是经过统一规范的,谁规范的?GlobalPlatform。这就是为什么你经常看到关于CA开发的时候,就会说这个CA的开发API接口是符合GP规范的。

那么什么是GlobalPlatform-GP

1-GP

GlobalPlatform是一个由100多家成员公司推动的非营利性行业协会。

成员们共同的目标是开发GlobalPlatform的规范。这些规范现已被广泛认定为推动数字服务和设备在整个生命周期内受到信任并安全管理的国际标准。

GlobalPlatform通过制定标准和认证安全硬件/硬件组合(合称安全组件,可充当设备上的信任锚)来保护数字服务。

GlobalPlatform规范还制定了实地应用数字服务和设备的安全管理标准。

GlobalPlatform可为终端用户提供方便、安全的数字服务,同时无论市场部门或设备类型如何,GlobalPlatform都提供隐私保护支持。GlobalPlatform保护的设备包括智能手机、平板电脑、机顶盒、可穿戴设备、联网汽车、其他物联网(IoT)设备和智能卡片。

该技术广泛应用于全球,提高了所有用户的成本和上市时间的效率。采用GlobalPlatform技术的市场领域包括支付、电信、交通、汽车、智能城市、智能家居、公用事业、医疗保健、优质内容、政府和企业ID。

【CA-TA实战系列一】如何创建一个CA_第1张图片

1,SE管理作为GP标准重要一项,有完善的API、测试套件。
2,TEE API规范主要是TEE方案商和TA开发者必备的案头参考手册。
3,TEE 一致性规范,主要还包含测试相关啦!
4,TEE管理框架,就是应用管理相关,也是需要各大TEE厂商所重视的。
5,TEE PP也就是TEE的安全轮廓,是TEE安全认证的最重要的文档。
备注:把SE也放在一起,因为实际应用中TEE和SE也是一对不可分离的兄弟!

你想想如果不整个统一的接口规范,不同的厂家设计开发CA应用时,不同设备不同应用,想想这个成本多高!!!

这个规范其实不只是针对CA,也针对TA,应该也顺带了TEEOS。(脑子里想想统一接口的好处)

整个架构图

【CA-TA实战系列一】如何创建一个CA_第2张图片
那么关于这些API其实分为两种

2、TEE GP API

1、TEE Client API:(CA->TA)

一类是CA与TA通信的API,实现方式就是应用程序调用libteec.so库,libteec.so库是由optee_client编译出的,libteec.so中调用了dev/tee_priv节点陷入kernel mode,在kernel mode中调用smc同步异常指令陷入到ATF,ATF再跳出到TEE OS, TEE OS再将消息发送给相应的TA。

该API在globalplatform中的TEE_Client_API_Specification-V1.0_c.pdf文档中有所规定,

api的具体实现是在optee_client/libteec

2、TEE OS API:(TA->TEEOS)

一类是TA系统调用TEE OS的API,这是的实现方式其实就是系统调用,调用SVC同步异常指令,进入kernel mode。

该API在globalplatform中的GPD_TEE_Internal_Core_API_Specification_v1.1.pdf文档中有所规定,

api的具体实现是在optee_os\lib\libutee

3、TEE Client API

关于CA的开发我们只需要用到这部分的API接口:

TEEC_InitializeContext
TEEC_FinalizeContext
TEEC_RegisterSharedMemory
TEEC_AllocateSharedMemory
TEEC_ReleaseSharedMemory
TEEC_OpenSession 
TEEC_CloseSession
TEEC_InvokeCommand
TEEC_RequestCancellation

关于TEEOS API这里暂时不涉及,我们先省略,因为TEEOS API的内容特别的多,下一篇开发TA的时候咱们再议。(也可以观看参考链接,是一个大神前辈的博客,这篇很多的内容都来自于此)

2、开发一个demo CA

1、先梳理CA工作流程

GP定义的客户端接口包括9个函数和1文档,使用这9个接口函数和宏就可满足CA的开发,只是CA需要配合TA一起使用,双方定义的UUID的值和命令ID的值需保持一致,这9个函数和宏的名称和作用如表所示。

【CA-TA实战系列一】如何创建一个CA_第3张图片
CA使用libteec库中提供的接口来实现对TEE侧TA中具体命令的调用。libteec库是OP-TEE提供给用户在Linux用户空间使用的接口的实现,对于该部分每家芯片厂商可能不一样,但对外的接口都遵循GP规范中CA的接口进行定义。

libteec库提供CA程序运行时的基本接口

libteec库和tee_supplicant属于REE侧用户空间的功能

一次完整的功能调用一般是起源于CA, TA实现具体功能并返回结果数据给CA。

整个过程需要经过OP-TEE的客户端接口、OP-TEE在Linux内核端的驱动、Monitor模式/EL3下安全监控模式调用(smc)的处理、OP-TEE OS的线程处理、OP-TEE中的TA程序运行、OP-TEE端底层库或者硬件资源支持等几个阶段。当TA执行完具体请求之后会按照原路径将数据返回给CA。

【CA-TA实战系列一】如何创建一个CA_第4张图片

2、接口详解

其实这部分之前肯定在书里看到过,在实战中再次见到后,有种原来是你小子的感觉。

libteec库提供给上层用户使用的API一共有10个,都按照GP标准进行定义,使用这10个API能够满足用户在Linux用户空间的需求,在系统中这部分会被编译成libteec库,保存在REE侧的文件系统中以备上层使用。

上述10个函数的功能和实现说明如下:

1. TEEC_InitializeContext

  • 函数原型:
TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *ctx)
  • 函数作用描述:
    初始化一个TEEC_Context变量,该变量用于CA和TEE之间建立联系。其中参数name用来定义TEE的身份,如果该参数为NULL,则CA将会选择默认的TEE方案来建立联系。该API必须是CA调用的第一个libteec库的API,且该API不会触发TA的执行。

  • 参数说明:

    • name:指向TEE的名字,一般情况下该值设置成NULL,使其选择默认的TEE方案进行连接。
    • ctx:指向一个类型为TEEC_Context的变量的地址,该变量会用于CA与TA之间的连接和通信。
    • 函数返回值:TEEC_SUCCESS:初始化操作成功。其他返回值表示初始化失败。
  • 函数实现(在OP-TEE中的实现)如下:

        TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *ctx)
        {
            char devname[PATH_MAX];
            int fd;
            size_t n;
            if (! ctx)
                return TEEC_ERROR_BAD_PARAMETERS;
            /* 调用teec_open_dev打开可用的TEE驱动文件,在打开的过程中会校验TEE的版本信息。如果检
            查合法,则会返回该驱动文件的句柄pd,然后将fd赋值给ctx变量的fd成员 */
            for (n = 0; n < TEEC_MAX_DEV_SEQ; n++) {
                snprintf(devname, sizeof(devname), "/dev/tee%zu", n);
                fd = teec_open_dev(devname, name);
                if (fd >= 0) {
                    ctx->fd = fd;
                    return TEEC_SUCCESS;
                }
            }
            return TEEC_ERROR_ITEM_NOT_FOUND;
        }

2. TEEC_FinalizeContext

  • 函数原型:
        void TEEC_FinalizeContext(TEEC_Context *ctx)
  • 函数作用描述:

    • 释放一个已经被初始化的类型为TEEC_Context的变量,关闭CA与TEE之间的连接。在调用该函数之前必须确保打开的session已经被关闭。
  • 参数说明:

    • ctx:指向一个类型为TEEC_Context的变量,该变量会用于CA与TA之间的连接和通信。
    • 函数返回值:无。
  • 函数实现(在OP-TEE中的实现)如下:

        void TEEC_FinalizeContext(TEEC_Context *ctx)
        {
            /* 调用close函数,释放掉tee驱动文件的描述符来完成资源释放 */
            if (ctx)
                close(ctx->fd);
        }

3. TEEC_OpenSession

  • 函数原型:
        TEEC_Result TEEC_OpenSession(TEEC_Context *ctx, TEEC_Session *session, const
            TEEC_UUID *destination, uint32_t connection_method, const
            void *connection_data, TEEC_Operation *operation, uint32_t *ret_origin)
  • 函数作用描述:
    打开一个CA与对应TA之间的一个session,该session用于CA与对应TA之间的联系,CA需要连接的TA是由UUID指定的。
    session具有不同的打开和连接方式,根据不同的打开和连接方式CA可以在执行打开session时传递数据给TA,以便TA对打开操作进行权限检查。各种打开方式说明如下。

    • TEEC_LOGIN_PUBLIC:不需要提供,即connectionData的值必须为NULL。

    • TEEC_LOGIN_USER:提示用户链接,connectionData的值必须为NULL。

    • TEEC_LOGIN_GROUP: CA以组的方式打开session。connectionData的值必须指向一个类型为uint32_t的数据,其包含某一组的特定信息。在TA端将会对connectionData的数据进行检查,判定CA是否真属于该组。

    • TEEC_LOGIN_APPLICATION:以application的方式连接,connectionData的值必须为NULL。

    • TEEC_LOGIN_USER_APPLICATION:以用户程序的方式连接,connectionData的值必须为NULL。

    • TEEC_LOGIN_GROUP_APPLICATION:以组应用程序的方式连接,其中connectionData需要指向一个uint32_t类型的变量。在TA端将会对connectionData的数据进行权限检查,查看连接是否合法。

  • 参数说明:

    • context:指向一个类型为TEEC_Context的变量,该变量用于CA与TA之间的连接和通信,调用TEEC_InitializeContext函数进行初始化;
    • session:存放session内存的变量;
    • destination:指向存放需要连接TA的UUID的值的变量;
    • connectionMethod:CA与TA的连接方式,详细可参考函数描述中的说明(就刚刚上面几种);
    • connectionData:指向需要在打开session时传递给TA的数据;
    • operation:指向TEEC_Operation结构体的变量,变量中包含了一系列用于与TA进行交互使用的buffer或者其他变量。如果在打开session时CA和TA不需要交互数据,则可以将该变量指向NULL;
    • returnOrigin:用于存放从TA端返回的结果的变量。如果不需要返回值,则可以将该变量指向NULL。
    • 函数返回值:TEEC_SUCCESS:初始化操作成功;其他返回值表示初始化失败。
  • 函数实现(在OP-TEE中的实现)如下:

        TEEC_Result TEEC_OpenSession(TEEC_Context *ctx, TEEC_Session *session,
                    const TEEC_UUID *destination,
                    uint32_t connection_method, const void *connection_data,
                    TEEC_Operation *operation, uint32_t *ret_origin)
        {
            /*  定义一个缓存,用于存放在执行open  session需要传递给OP-TEE  OS的数据和保存TA返回的
            数据 */
            uint64_t buf[(sizeof(struct tee_ioctl_open_session_arg) +
                    TEEC_CONFIG_PAYLOAD_REF_COUNT *
                        sizeof(struct tee_ioctl_param)) /
                    sizeof(uint64_t)] = { 0 };
            /*定义buf_data,指向buf变量,用于将数据传递给OP-TEE驱动的ioctl函数来执行open
            session操作*/
            struct tee_ioctl_buf_data buf_data;
            /* 定义参数,用于对初始化需要传递给TA的数据buffer */
            struct tee_ioctl_open_session_arg *arg;
            struct tee_ioctl_param *params;
            TEEC_Result res;
            uint32_t eorig;
            /* CA与TA之间的共享buffer */
            TEEC_SharedMemory shm[TEEC_CONFIG_PAYLOAD_REF_COUNT];
            int rc;
            (void)&connection_data;
            /* 参数检查 */
            if (! ctx || ! session) {
                eorig = TEEC_ORIGIN_API;
                res = TEEC_ERROR_BAD_PARAMETERS;
                goto out;
            }
            /* 指针赋值 */
              buf_data.buf_ptr = (uintptr_t)buf;
              buf_data.buf_len = sizeof(buf);
              arg = (struct tee_ioctl_open_session_arg *)buf;
              arg->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT;
              params = (struct tee_ioctl_param *)(arg + 1);
              /* 将uuid的值填充到buffer中 */
              uuid_to_octets(arg->uuid, destination);
              arg->clnt_login = connection_method;
              /* 填充TEEC_Operation结构体变量 */
              res = teec_pre_process_operation(ctx, operation, params, shm);
              if (res ! = TEEC_SUCCESS) {
                  eorig = TEEC_ORIGIN_API;
                  goto out_free_temp_refs;
              }
              // 调用ioctl函数,执行TEE_IOC_OPEN_SESSION操作
              rc = ioctl(ctx->fd, TEE_IOC_OPEN_SESSION, &buf_data);
              if (rc) {
                  EMSG("TEE_IOC_OPEN_SESSION failed");
                  eorig = TEEC_ORIGIN_COMMS;
                  res = ioctl_errno_to_res(errno);
                  goto out_free_temp_refs;
              }
              res = arg->ret;
              eorig = arg->ret_origin;
              if (res == TEEC_SUCCESS) {
                  session->ctx = ctx;
                  session->session_id = arg->session;
              }
              /* 解析出从TA中返回的数据 */
              teec_post_process_operation(operation, params, shm);
          out_free_temp_refs:
              teec_free_temp_refs(operation, shm);
          out:
              if (ret_origin)
                  *ret_origin = eorig;
              return res;
          }

4. TEEC_CloseSession

  • 函数原型:
        void TEEC_CloseSession(TEEC_Session *session)
  • 函数作用描述:
    关闭已经被初始化的CA与对应TA之间的session,在调用该函数之前需要保证所有的command已经执行完毕。如果session为NULL,则不执行任何操作。

  • 参数说明:

    • session:指向已经初始化的session结构体变量。
    • 函数返回值:无。
  • 函数实现(在OP-TEE中的实现)如下:

        void TEEC_CloseSession(TEEC_Session *session)
        {
            struct tee_ioctl_close_session_arg arg;
            if (! session)
                return;
            arg.session = session->session_id;
            /* 调用ioctl函数中的TEE_IOC_CLOSE_SESSION操作,通知TA执行close session */
            if (ioctl(session->ctx->fd, TEE_IOC_CLOSE_SESSION, &arg))
                EMSG("Failed to close session 0x%x", session->session_id);
        }

5. TEEC_InvokeCommand

  • 函数原型:
        TEEC_Result  TEEC_InvokeCommand(TEEC_Session  *session,  uint32_t  cmd_id,  TEEC_
    Operation *operation, uint32_t *error_origin)
  • 函数作用描述:
    通过cmd_id和打开的session来通知session对应的TA执行cmd_id指定的操作。

  • 参数说明:

    • session:指向已经初始化的session结构体变量;
    • cmd_id:TA中定义的command的ID值,让CA通知TA执行哪条command;
    • operation:已经初始化的TEEC_Operation类型的变量,该变量中包含CA与TA之间进行交互的buffer、缓存的属性等信息;
    • error_origin:调用TEEC_InvokeCommand函数时,TA端的返回值。
  • 函数实现(在OP-TEE中的实现)如下:

        TEEC_Result TEEC_InvokeCommand(TEEC_Session *session, uint32_t cmd_id,
                    TEEC_Operation *operation, uint32_t *error_origin)
        {
            /* 定义调用invokecommand函数时存放参数和共享内存的buffer */
            uint64_t buf[(sizeof(struct tee_ioctl_invoke_arg) +
                    TEEC_CONFIG_PAYLOAD_REF_COUNT *
                        sizeof(struct tee_ioctl_param)) /
                    sizeof(uint64_t)] = { 0 };
            struct tee_ioctl_buf_data buf_data;
            struct tee_ioctl_invoke_arg *arg;
            struct tee_ioctl_param *params;
            TEEC_Result res;
            uint32_t eorig;
            TEEC_SharedMemory shm[TEEC_CONFIG_PAYLOAD_REF_COUNT];
              int rc;
              if (! session) {
                  eorig = TEEC_ORIGIN_API;
                  res = TEEC_ERROR_BAD_PARAMETERS;
                  goto out;
              }
              /* 组合调用TA的command时需要使用的参数信息 */
              buf_data.buf_ptr = (uintptr_t)buf;
              buf_data.buf_len = sizeof(buf);
              arg = (struct tee_ioctl_invoke_arg *)buf;
              arg->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT;
              params = (struct tee_ioctl_param *)(arg + 1);
              arg->session = session->session_id;
              arg->func = cmd_id;
              if (operation) {
                  teec_mutex_lock(&teec_mutex);
                  operation->session = session;
                  teec_mutex_unlock(&teec_mutex);
              }
              /* 填充operation中的params域,用于CA与TA之间的数据传输 */
              res = teec_pre_process_operation(session->ctx, operation, params, shm);
              if (res ! = TEEC_SUCCESS) {
                  eorig = TEEC_ORIGIN_API;
                  goto out_free_temp_refs;
              }
              /* 调用ioctl函数中的TEE_IOC_INVOKE操作 */
              rc = ioctl(session->ctx->fd, TEE_IOC_INVOKE, &buf_data);
              if (rc) {
                  EMSG("TEE_IOC_INVOKE failed");
                  eorig = TEEC_ORIGIN_COMMS;
                  res = ioctl_errno_to_res(errno);
                  goto out_free_temp_refs;
              }
              res = arg->ret;
              eorig = arg->ret_origin;
              /* 解析从TA中返回到params缓存中的数据 */
              teec_post_process_operation(operation, params, shm);
          out_free_temp_refs:
              teec_free_temp_refs(operation, shm);
          out:
              if (error_origin)
                  *error_origin = eorig;
              return res;
          }

【啰嗦一句废话,这个TEEC_Result 是一个宏,定义了很多的变量,在维测定位问题的时候,可以根据这个来判断定位问题是出现在安全测还是非安测,以及错误的原因是什么。还有个error_origin的变量。】

6. TEEC_RequestCancellation

  • 函数原型:
        void TEEC_RequestCancellation(TEEC_Operation *operation)
  • 函数作用描述:

    取消某个CA与TA之间的操作,该接口只能由除执行TEEC_OpenSession和TEEC_InvokeCommand的线程之外的其他线程进行调用,而TA端或者TEE OS可以选择并不响应该请求。只有当operation中的started域被设置成0之后,该操作方可有效。

  • 参数说明:

    • operation:已经初始化的TEEC_Operation类型的变量,该变量中包含CA与TA之间进行交互的buffer、缓存的属性等信息。
  • 函数实现(在OP-TEE中的实现)如下:

        void TEEC_RequestCancellation(TEEC_Operation *operation)
        {
            struct tee_ioctl_cancel_arg arg;
            TEEC_Session *session;
            if (! operation)
                return;
            /* 获取session */
            teec_mutex_lock(&teec_mutex);
            session = operation->session;
            teec_mutex_unlock(&teec_mutex);
            if (! session)
                return;
            arg.session = session->session_id;
            arg.cancel_id = 0;
            /* 调用tee驱动中的ioctl执行TEE_IOC_CANCEL操作 */
            if (ioctl(session->ctx->fd, TEE_IOC_CANCEL, &arg))
                EMSG("TEE_IOC_CANCEL: %s", strerror(errno));
        }

7. TEEC_RegisterShareMemory

  • 函数原型:
        TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *ctx, TEEC_SharedMemory *shm)
  • 函数作用描述:
    注册一块在CA端的内存作为CA与TA之间的共享内存。

    • shareMemory结构体中的三个成员如下:
    • buffer:指向作为共享内存的起始地址;
    • size:共享内存的大小;
    • flags:表示CA与TA之间的数据流方向。
  • 参数说明:

    • ctx:指向一个类型为TEEC_Context的变量,该变量必须已经被初始化;
    • shm:指向共享内存的结构体变量。函数实现(在OP-TEE中的实现)如下:
        TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *ctx, TEEC_SharedMemory *shm)
        {
            int fd;
            size_t s;
            if (! ctx || ! shm)
                return TEEC_ERROR_BAD_PARAMETERS;
            if (! shm->flags || (shm->flags & ~(TEEC_MEM_INPUT | TEEC_MEM_OUTPUT)))
                return TEEC_ERROR_BAD_PARAMETERS;
            s = shm->size;
            if (! s)
                s = 8;
            /* 调用ioctl函数,执行TEE_IOC_SHM_ALLOC操作 */
            fd = teec_shm_alloc(ctx->fd, s, &shm->id);
            if (fd < 0)
                return TEEC_ERROR_OUT_OF_MEMORY;
            /* 将注册到OP-TEE中的共享内存的对应fd映射到系统内存中,并存放到shm中的shadow_buffer
            变量中 */
            shm->shadow_buffer = mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED,
                        fd, 0);
            close(fd);
            if (shm->shadow_buffer == (void *)MAP_FAILED) {
                shm->id = -1;
                return TEEC_ERROR_OUT_OF_MEMORY;
            }
            shm->alloced_size = s;
            shm->registered_fd = -1;
            return TEEC_SUCCESS;
        }

8. TEEC_RegisterShareMemoryFileDescriptor

  • 函数原型:
        TEEC_Result  TEEC_RegisterSharedMemoryFileDescriptor(TEEC_Context  *ctx,  TEEC_
    SharedMemory *shm, int fd)
  • 函数作用描述:
    注册一个在CA与TA之间的共享文件,在CA端会将文件的描述符fd传递给OP-TEE,其内容被存放到shm中。

  • 参数说明:

    • ctx:指向一个类型为TEEC_Context的变量,该变量必须已经被初始化;
    • shm:指向共享内存的结构体变量;
    • fd:共享的文件的描述符号。
  • 函数实现(在OP-TEE中的实现)如下:

        TEEC_Result TEEC_RegisterSharedMemoryFileDescriptor(TEEC_Context *ctx,
        TEEC_SharedMemory *shm,
        int fd)
{
struct tee_ioctl_shm_register_fd_data data;
int rfd;
if (! ctx || ! shm || fd < 0)
return TEEC_ERROR_BAD_PARAMETERS;
if (! shm->flags || (shm->flags & ~(TEEC_MEM_INPUT | TEEC_MEM_OUTPUT)))
return TEEC_ERROR_BAD_PARAMETERS;
/* 组合共享文件的结构体 */
memset(&data, 0, sizeof(data));
data.fd = fd;
/* 调用ioctl函数由tee驱动来完成共享文件注册的其他操作 */
rfd = ioctl(ctx->fd, TEE_IOC_SHM_REGISTER_FD, &data);
if (rfd < 0)
return TEEC_ERROR_BAD_PARAMETERS;
/* 将返回值保存到shm变量中,以便后续使用 */
shm->buffer = NULL;
shm->shadow_buffer = NULL;
shm->registered_fd = rfd;
shm->id = data.id;
shm->size = data.size;
return TEEC_SUCCESS;
}

9. TEEC_AllocateSharedMemory

  • 函数原型:
        TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *ctx, TEEC_SharedMemory *shm)
  • 函数作用描述:
    分配一块共享内存,共享内存是由OP-TEE分配的,OP-TEE分配了共享内存之后将会返回该内存块的fd给CA, CA将fd映射到系统内存,然后将地址保存到shm中。(和7的区别就是创建的主体不同,采用哪种方式应该要根据内存的目的和作用,这个我看到过一位前辈的博客写的是TEEC_AllocateSharedMemory是零拷贝,7是非零拷贝。这又涉及到我的知识盲区了,不说了,安排一篇)

  • 参数说明:

    • ctx:指向一个类型为TEEC_Context的变量,该变量必须已经被初始化;
    • shm:指向共享内存的结构体变量。

函数实现(在OP-TEE中的实现)如下:

        TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *ctx, TEEC_SharedMemory *shm)
        {
            int fd;
            size_t s;
            if (! ctx || ! shm)
                return TEEC_ERROR_BAD_PARAMETERS;
            if (! shm->flags || (shm->flags & ~(TEEC_MEM_INPUT | TEEC_MEM_OUTPUT)))
                return TEEC_ERROR_BAD_PARAMETERS;
            s = shm->size;
              if (! s)
                  s = 8;
              /* 通知OP-TEE进行共享内存的分配,返回fd */
              fd = teec_shm_alloc(ctx->fd, s, &shm->id);
              if (fd < 0)
                  return TEEC_ERROR_OUT_OF_MEMORY;
              /* 将fd映射进系统内存,并将映射完成的地址存放到shm中 */
              shm->buffer = mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
              close(fd);
              if (shm->buffer == (void *)MAP_FAILED) {
                  shm->id = -1;
                  return TEEC_ERROR_OUT_OF_MEMORY;
              }
              shm->shadow_buffer = NULL;
              shm->alloced_size = s;
              shm->registered_fd = -1;
              return TEEC_SUCCESS;
          }

10. TEEC_ReleaseSharedMemory

  • 函数原型:
void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *shm)
  • 函数作用描述:
    释放已经被分配或者注册过的共享内存。

  • 参数说明:

    • shm:指向共享内存的结构体变量。
  • 函数实现(在OP-TEE中的实现)如下:

函数原型:void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *shm)函数作用描述:释放已经被分配或者注册过的共享内存。参数说明:shm:指向共享内存的结构体变量。函数实现(在OP-TEE中的实现)如下:

3、CA调用libteec库中接口的流程

CA在使用libteec库中的接口来实现调用TA的操作时,

一般过程是需要先建立context,然后建立与需要调用的TA之间的session,

再通过执行invoke操作向TA发送command ID来实现具体的操作需求,待TA中command ID的内容执行完成之后,

如果后续也不需要再次调用TA时,可以通过close session和final context来释放资源,完全关闭该CA与TA之间的联系。

【CA-TA实战系列一】如何创建一个CA_第5张图片

4、一个CA实例

在CA源代码中调用GP规范中定义的客户端的接口就可实现对TA的调用。在CA中调用客户端接口的顺序依次如下。

    1. TEEC_InitializeContext初始化CA与TEE之间的上下文,打开TEE驱动设备,得到一个TEEC_context。
    1. TEEC_OpenSession调用时代入TA的UUID,建立CA与指定TA之间的会话。
    1. TEEC_PARAM_TYPES配置需要发送到TA的参数的属性,可将参数设定为input属性和output属性。
    1. TEEC_InvokeCommand代入会话ID、命令ID、包含参数内容的operation变量,开始发送请求给TEE来调用TA中的特定操作。
    1. TEEC_CloseSession调用完成后关闭CA与TA之间的会话。
    1. TEEC_FinalizeContext关闭CA与TEE之间的连接。

在编写CA代码时需注意,**在关闭上下文之前不要重复调用TEEC_InitializeContext函数,**否则会报错,且如果在没有调用TEEC_CloseSession函数之前重复执行打开会话的操作可能会导致TEE中的空间不足。

CA中的UUID和命令ID的定义一定要保证与TA中的命令ID和UUID的定义一致。

/*
 * Copyright (c) 2014, Linaro Limited
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
	TEEC_Result res;
	TEEC_Context ctx;
	TEEC_Session sess;
	TEEC_Operation op;
	TEEC_UUID uuid = TA_HELLO_WORLD_UUID;
	uint32_t err_origin;

	/* Initialize a context connecting us to the TEE */
	res = TEEC_InitializeContext(NULL, &ctx);
	if (res != TEEC_SUCCESS)
		errx(1, "TEEC_InitializeContext failed with code 0x%x", res);

	/*
	 * Open a session to the "hello world" TA, the TA will print "hello
	 * world!" in the log when the session is created.
	 */
	res = TEEC_OpenSession(&ctx, &sess, &uuid,
			       TEEC_LOGIN_PUBLIC, NULL, NULL, &err_origin);
	if (res != TEEC_SUCCESS)
		errx(1, "TEEC_Opensession failed with code 0x%x origin 0x%x",
			res, err_origin);

	/*
	 * Execute a function in the TA by invoking it, in this case
	 * we're incrementing a number.
	 *
	 * The value of command ID part and how the parameters are
	 * interpreted is part of the interface provided by the TA.
	 */

	/*
	 * Prepare the argument. Pass a value in the first parameter,
	 * the remaining three parameters are unused.
	 */
	memset(&op, 0, sizeof(op));
	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE,
					 TEEC_NONE, TEEC_NONE);
	op.params[0].value.a = 42;

	printf("Invoking TA to increment %d\n", op.params[0].value.a);
	res = TEEC_InvokeCommand(&sess, TA_HELLO_WORLD_CMD_INC_VALUE, &op,
				 &err_origin);
	if (res != TEEC_SUCCESS)
		errx(1, "TEEC_InvokeCommand failed with code 0x%x origin 0x%x",
			res, err_origin);
	printf("TA incremented value to %d\n", op.params[0].value.a);

	/*
	 * We're done with the TA, close the session and
	 * destroy the context.
	 *
	 * The TA will print "Goodbye!" in the log when the
	 * session is closed.
	 */

	TEEC_CloseSession(&sess);

	TEEC_FinalizeContext(&ctx);

	return 0;
}

到这里就是全部的内容。

呜呜呜 可惜我的破电脑跑不起来!!!

希望早点换个电脑,没有别的要求,能安装虚拟机就行!!!

下一篇就是怎么写一个ta,怎么让这个ca和ta跑起来,然后梳理细粒度的ca到ta的通信流程。

新年快乐!!!

参考资料:

https://zuopeng.blog.csdn.net/article/details/120146348
《手机安全和可信应用开发指南:TrustZone与OP-TEE技术详解 》

你可能感兴趣的:(TEE-OS,ATF,系统安全,TEEOS)