OPTEE之静态TA的创建与调用

先简单介绍下optee:TEE是智能手机主处理器中的一块安全区域,保证代码和数据的机密性和完整性;TEE中的数据不会被REE中的程序非法访问;TEE中的 可信应用(TA)在隔离的环境中运行,其 安全性比手机主操作系统(Rich OS,比如Android)高,并且 提供比SE更丰富的功能。optee就是TEE的开源版本,企业可以将其的TEE功能移植到支持trustzone的arm芯片的各种操作系统,包括Android、Linux等其他系统。

目前我所知的OPTEE已经可以支持的设备有如下:

其实还有很多厂商自己移植的,只是不对外公开。optee的官网地址为:Device specific information — OP-TEE documentation documentation

我是使用的qemu方式来验证TEE功能,TEE的理解我这里不详细介绍,网上很多资源可以看到对其的介绍,我主要是讲静态TA的创建方法和调用方法,接下来我的介绍都是在ubuntu上面搭建的optee的qemu环境上进行验证。

首先我大致说下静态TA和动态TA的区别:

动态TA:动态TA就是最后编译生成的.TA文件是在REE测,用户可以在文件系统里面找到对应的.TA文件,在调用TA的时候,TEE测会加载该动态TA,然后验证TA是否合法,最后再执行TA中用户开发的代码功能。重点是需要时才会去加载,而且会验证TA合法性,并且TA是存在REE测

静态TA:静态TA最后编译的静态.TA文件是在TEE测,在系统运行的时候,静态TA会跟随系统一起加载起来在TEE测,在调用TA的时候直接进行调用使用。静态TA的重点是系统运行就会加载,我不确定是否需要验证合法性,并且TA在TEE测,用无法看到静态TA。

我们在qemu的optee_examples里面操作的全是属于动态TA的例子,动态TA的使用方法,网上太多了,大家可以按照optee_examples里面的例子自救去实验即可,我们直接讲如何创建静态TA和怎么调用静态TA。

在例子中我创建的是一个生成随机数的静态TA功能,用户在REE测调用静态TA,可以生成指定长度的随机数。

1、qemu的目录如下

drwxrwxr-x 10 jelly jelly  4096 1月  18 16:56 build
drwxrwxr-x 16 jelly jelly  4096 3月   9  2020 buildroot
drwxrwxr-x 25 jelly jelly  4096 1月  19 15:51 linux
drwxrwxr-x  4 jelly jelly  4096 3月   9  2020 optee_benchmark
drwxrwxr-x  8 jelly jelly  4096 3月   9  2020 optee_client
drwxrwxr-x 11 jelly jelly  4096 1月  18 15:39 optee_examples
drwxrwxr-x 12 jelly jelly  4096 1月  18 17:42 optee_os
drwxrwxr-x  9 jelly jelly  4096 3月   9  2020 optee_test
drwxrwxr-x  3 jelly jelly  4096 3月   9  2020 out
drwxr-xr-x  2 root  root   4096 1月  19 15:51 out-br
drwxrwxr-x 47 jelly jelly 12288 1月  19 15:43 qemu
drwxrwxr-x  3 jelly jelly  4096 3月   9  2020 soc_term
drwxrwxr-x  2 jelly jelly  4096 3月   9  2020 toolchains
drwxrwxr-x 19 jelly jelly  4096 3月   9  2020 trusted-firmware-a
drwxrwxr-x 25 jelly jelly  4096 1月  19 15:51 u-boot

我们需要在optee_os文件夹里面编辑静态TA,然后在optee_examples里面写调用静态TA的示例

2、新增静态TA的.c文件

vim /home/jelly/qemu/optee_os/core/pta/jelly_static_ta.c

内容如下:下面的操作就是静态TA的一个测试功能,是生成随机数,需要从REE测传入随机数的长度和生成随机数的种子。

备注一下哈,本来是可以直接调用TA测的生成随机数的函数或者rand函数,但是我试了很久都没调用起来,后来就找到了一个类似随机数生成的源码代替随机数函数。

// SPDX-License-Identifier: BSD-2-Clause
/*
 * Copyright (C) 2019, Linaro Limited
 */

/*
 * This pseudo TA is used by normal world OS TEE driver to fetch pseudo TA's
 * UUIDs which can act as TEE bus devices.
 */

#include <kernel/pseudo_ta.h>
#include <kernel/tee_ta_manager.h>
#include <pta_jelly_static_ta.h>
#include <string.h>
#include <tee/uuid.h>
#include <user_ta_header.h>

#define STATIC_NAME "static_jelly.pta"

#include <tee_internal_api.h>

static unsigned long next = 1;	//next的初始值为随机数种子
uint32_t jelly_rand(void)		//自定义随机数产生函数
{
    next = next * 1103515245 + 12345;
    return((unsigned)(next/65536) % 32768 % 256);
}

void jelly_srand(unsigned long seed)//通过传不同的参数更改种子
{
    next=seed;
}

static TEE_Result random_number_generate(uint32_t param_types,
    TEE_Param params[4])
{
	uint8_t random_data[256] = {0};
    uint32_t exp_param_types =
                TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_OUTPUT,
                        TEE_PARAM_TYPE_MEMREF_INPUT,
                        TEE_PARAM_TYPE_NONE,
                        TEE_PARAM_TYPE_NONE);

    DMSG("has been called");
    if ((param_types != exp_param_types))
        return TEE_ERROR_BAD_PARAMETERS;

    //IMSG("Generating random data over %u bytes.", params[0].memref.size);
	jelly_srand(params[1].memref.size);
	
	for(uint8_t i=0;i<params[0].memref.size;i++)
	{
		random_data[i] = jelly_rand();
		//params[0].memref.buffer[i] = jelly_rand();
	}
	memcpy(params[0].memref.buffer, random_data, params[0].memref.size);
	
	return TEE_SUCCESS;
}


static TEE_Result invoke_command(void *pSessionContext __unused,
				 uint32_t nCommandID, uint32_t nParamTypes,
				 TEE_Param pParams[TEE_NUM_PARAMS])
{
	switch (nCommandID) {
	case PTA_CMD_STATIC:
		return random_number_generate(nParamTypes, pParams);
	default:
		break;
	}

	return TEE_ERROR_NOT_IMPLEMENTED;
}

pseudo_ta_register(.uuid = PTA_STATIC_JELLY_UUID, .name = STATIC_NAME,
		   .flags = PTA_DEFAULT_FLAGS,
		   .invoke_command_entry_point = invoke_command);

3、修改sub.mk文件,将新的.c文件添加到其中

/home/jelly/qemu/optee_os/core/pta/sub.mk

修改后内容如下:

subdirs-$(CFG_TEE_CORE_EMBED_INTERNAL_TESTS) += tests

srcs-$(CFG_TEE_BENCHMARK) += benchmark.c
srcs-$(CFG_DEVICE_ENUM_PTA) += device.c
srcs-$(CFG_TA_GPROF_SUPPORT) += gprof.c
srcs-$(CFG_SDP_PTA) += sdp.c
ifeq ($(CFG_WITH_USER_TA),y)
srcs-$(CFG_SECSTOR_TA_MGMT_PTA) += secstor_ta_mgmt.c
endif
srcs-$(CFG_WITH_STATS) += stats.c
srcs-$(CFG_SYSTEM_PTA) += system.c
srcs-y += jelly_static_ta.c

subdirs-y += bcm

4、新增.c的头文件

vim /home/jelly/qemu/optee_os/lib/libutee/include/pta_jelly_static_ta.h

内容如下:头文件里面包含了TA的UUID和一些其他参数

/* SPDX-License-Identifier: BSD-2-Clause */
/*
 * Copyright (C) 2019, Linaro Limited jelly static test
 */

/*
 * Enumerate the pseudo TAs that have the TA_FLAG_DEVICE_ENUM flag enabled.
 */

#ifndef __PTA_DEVICE_H
#define __PTA_DEVICE_H

#define PTA_STATIC_JELLY_UUID { 0xaabbccee, 0xddde, 0x4053, \
		{ 0xa5, 0xa9, 0x7b, 0x3c, 0x4d, 0xdf, 0x13, 0xdd } }

/*
 * Get device UUIDs
 *
 * [out]     memref[0]        Array of device UUIDs
 *
 * Return codes:
 * TEE_SUCCESS - Invoke command success
 * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
 * TEE_ERROR_SHORT_BUFFER - Output buffer size less than required
 */
#define PTA_CMD_STATIC		0x0

#endif /* __PTA_DEVICE_H */

5、创建静态TA调用示例

在optee_examples创建一个static_test的文件夹

mkdir /home/jelly/qemu/optee_examples/static_test

//里面含有如下文件列表,和optee_examples的其他动态TA的区别是此时没有TA文件夹了,因为静态TA在os文件夹下面已经创建好了

-rw-rw-r-- 1 jelly jelly  472 1月  18 13:37 Android.mk
-rw-rw-r-- 1 jelly jelly  286 1月  18 13:37 CMakeLists.txt
drwxrwxr-x 2 jelly jelly 4096 1月  19 15:48 host
-rw-rw-r-- 1 jelly jelly  311 1月  18 13:36 Makefile

Android.mk内容如下:

###################### optee-random ######################
LOCAL_PATH := $(call my-dir)

OPTEE_CLIENT_EXPORT = $(LOCAL_PATH)/../../optee_client/out/export

include $(CLEAR_VARS)
LOCAL_CFLAGS += -DANDROID_BUILD
LOCAL_CFLAGS += -Wall

LOCAL_SRC_FILES += host/main.c

LOCAL_C_INCLUDES := $(OPTEE_CLIENT_EXPORT)/include

LOCAL_SHARED_LIBRARIES := libteec
LOCAL_MODULE := optee_static_test
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)

CMakeLists.txt内容如下:

project (optee_static_test C)

set (SRC host/main.c)

add_executable (${PROJECT_NAME} ${SRC})

target_include_directories(${PROJECT_NAME}
			   PRIVATE include)

target_link_libraries (${PROJECT_NAME} PRIVATE teec)

install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})

Makefile内容如下:

export V ?= 0

# If _HOST or _TA specific compilers are not specified, then use CROSS_COMPILE
HOST_CROSS_COMPILE ?= $(CROSS_COMPILE)
TA_CROSS_COMPILE ?= $(CROSS_COMPILE)

.PHONY: all
all:
	$(MAKE) -C host CROSS_COMPILE="$(HOST_CROSS_COMPILE)" --no-builtin-variables

.PHONY: clean
clean:
	$(MAKE) -C host clean

host文件夹里面就两个文件:main.c和Makefile

host的main.c文件内容如下:

备注:在下面代码可以看到

1)random_uuid是我最后生成随机数的数组,定义的16位,由op.params[0].tmpref.buffer传递给TA

2)sizeof(random_uuid)是随机数长度,由op.params[0].tmpref.size传递给TA

3)datanouse这个没用,只是为了传随机数种子随便定义的字符串数组,这个必须有,不然会报错,我测试出来是这样的,由op.params[1].tmpref.buffer传递给TA(TA那边不会用到这个参数)

4)time(NULL)%1000这个是随机数种子,这个必须取余运算,否则TA会报内存溢出错误,猜测是time(NULL)太大了,大家自己也可以试试,由op.params[1].tmpref.size传递给TA


#include <err.h>
#include <stdio.h>
#include <string.h>
#include <time.h> 
/* OP-TEE TEE client API (built by optee_client) */
#include <tee_client_api.h>

#define TA_RANDOM_CMD_GENERATE 0
#define PTA_STATIC_JELLY_UUID { 0xaabbccee, 0xddde, 0x4053, \
		{ 0xa5, 0xa9, 0x7b, 0x3c, 0x4d, 0xdf, 0x13, 0xdd } }


int main(void)
{
	TEEC_Result res;
	TEEC_Context ctx;
	TEEC_Session sess;
	TEEC_Operation op = { 0 };
	TEEC_UUID uuid = PTA_STATIC_JELLY_UUID;
	uint8_t random_uuid[16] = { 0 };
	char datanouse[] = "sadadawdw";
	uint32_t err_origin;
	int i;

	/* 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);
	
	printf("#############Begin Static Test#############\n");
	
	printf("TEE init success...\n");

	printf("Static UUID =  aabbccee-ddde-4053-a5a9-7b3c4ddf13dd\n");
	
	/*
	 * Open a session to the Random example 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);

	printf("TEE Opensession success...\n");
	
	printf("TEE Static Verify success...\n");

	/*
	 * 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.
	 */

	/* Clear the TEEC_Operation struct */
	memset(&op, 0, sizeof(op));

	/*
	 * Prepare the argument. Pass a value in the first parameter,
	 * the remaining three parameters are unused.
	 */
	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_OUTPUT,
					TEEC_MEMREF_TEMP_INPUT,
					TEEC_NONE, TEEC_NONE);
	op.params[0].tmpref.buffer = random_uuid;
	op.params[0].tmpref.size = sizeof(random_uuid);
	op.params[1].tmpref.buffer = datanouse;
	op.params[1].tmpref.size = time(NULL)%1000;	//随机数种子
	//printf("op.params[1].tmpref.size = %d\n",op.params[1].tmpref.size);
	/*
	 * TA_EXAMPLE_RANDOM_GENERATE is the actual function in the TA to be
	 * called.
	 */
	//printf("Invoking TA to generate random UUID... \n");
	res = TEEC_InvokeCommand(&sess, TA_RANDOM_CMD_GENERATE,
				 &op, &err_origin);
	if (res != TEEC_SUCCESS)
		errx(1, "TEEC_InvokeCommand failed with code 0x%x origin 0x%x",
			res, err_origin);
	printf("#############Static Test Gen Random Data #############\n");
	printf("Data = ");
	for (i = 0; i < 16; i++)
		printf("%02x", random_uuid[i]);
	printf("\n");

	/*
	 * 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.
	 */
	
	printf("#############End Static Test#############\n");	

	TEEC_CloseSession(&sess);

	TEEC_FinalizeContext(&ctx);

	return 0;
}

host的Makefile文件内容如下:

CC      ?= $(CROSS_COMPILE)gcc
LD      ?= $(CROSS_COMPILE)ld
AR      ?= $(CROSS_COMPILE)ar
NM      ?= $(CROSS_COMPILE)nm
OBJCOPY ?= $(CROSS_COMPILE)objcopy
OBJDUMP ?= $(CROSS_COMPILE)objdump
READELF ?= $(CROSS_COMPILE)readelf

OBJS = main.o

CFLAGS += -Wall -I./include
CFLAGS += -I$(TEEC_EXPORT)/include
LDADD += -lteec -L$(TEEC_EXPORT)/lib

BINARY = optee_static_test

.PHONY: all
all: $(BINARY)

$(BINARY): $(OBJS)
	$(CC) -o $@ $< $(LDADD)

.PHONY: clean
clean:
	rm -f $(OBJS) $(BINARY)

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

6、切换到编译目录:

cd /home/jelly/qemu/build

然后执行命令:

sudo make run

输入密码,等待编译完成,在界面输入c,回车 

然后REE测执行optee_static_test命令就会完成静态TA的调用示例,如下图所示

结尾备注

        在没有研究静态TA的时候,其实是不明白何为静态TA,何为动态TA,初学者只知道在optee_examples文件夹内创建自己的新的TA,我直到研究静态TA之前也是这样的理解程度。最后查了各种资料,包括网上资料和书本资料,最后大致明白静态TA和动态TA的调用其实是和CA那边完全没得任何关系的,optee_examples里面的CA怎么调用的TA就还是按照原来的方法调用,区别在于静态TA的源码是在core里面的,而且在代码尾部必须进行注册pseudo_ta_register,见上面的.c的源代码。

        在CA端的host里面的主函数开始调用TA时,CA会根据TA的uuid进行TA的调用,当UUID传到TA端时,TA端的内部函数会拿着这个UUID先在已经存在静态TA里面寻找对应的TA,如果找不到,才会去静态TA寻找,寻找到UUID,如果是在静态TA这边直接进行调用即可;如果在动态TA这边,就先加载动态TA,然后在执行动态TA的功能。CA-TA的调用内部流程如下图所示,该图片摘自《手机安全和可信应用开发指南 TrustZone与OP-TEE技术详解》

 从上图中可以看出,当用户调用TA的时候,先从已经创建的TA链表中查找(我的理解是之前调用过的TA就会被放在这个TA链表中,以后调用就直接调用,再也不用去静态TA或者动态TA中找了),然后再去静态TA中寻找,静态TA中找不到再去动态TA中寻找。

        好了,差不多了,之后再有新的理解看再来补充。

2021年1月20日下午5点补充:

     针对main.c里面的op.paramTypes有了新的理解,之前理解的不够透彻,今天突然理解透了来记录一下。     

op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_OUTPUT,
                    TEEC_MEMREF_TEMP_INPUT,
                    TEEC_NONE, TEEC_NONE);
    op.params[0].tmpref.buffer = random_uuid;
    op.params[0].tmpref.size = sizeof(random_uuid);
    op.params[1].tmpref.buffer = datanouse;
    op.params[1].tmpref.size = time(NULL)%1000;

TEEC_PARAM_TYPES里面有四个参数,分别对应op.params[0],op.params[1],op.params[2],op.params[3]这个四个参数的类型。如上面代码所示:

1)TEEC_MEMREF_TEMP_OUTPUT,对应op.params[0]参数,使用时就是使用tmpref.buffer和tmpref.size参数,不能调用value.a和value.b参数,否则会报内存错

2)TEEC_MEMREF_TEMP_INPUT,对应op.params[1]参数,使用时只能tmpref.buffer和tmpref.size参数,不能调用value.a和value.b参数,否则也会报内存错

3)当时我是想传随机数种子进入TA,由于没理解清楚,始终传不成功,一直报内存错误,后来理解清楚,就大致明白了op.params[N]里面有既有value也有tmpref,根据我测试结果,这两个参数是不能同时用的。如果我当时只想传随机数参数的话,只需要像下面这样设置就可以了

op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_OUTPUT,
                    TEEC_VALUE_INPUT,
                    TEEC_NONE, TEEC_NONE);
    op.params[0].tmpref.buffer = random_uuid;
    op.params[0].tmpref.size = sizeof(random_uuid);
    op.params[1].value.a = time(NULL)%1000;

这样设置后,op.params[1].tmpref.buffer也不能用了,否则也会报内存错误。然后在TA那边也设置成这样,随机数种子参数改为value.a就可以了,终于不会报内存错误了,理解透了,在此记录一下。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值