参考文件
OP-TEE官方文档:optee_examples
optee_examples对应的github链接:https://github.com/linaro-swg/optee_examples
一、前言
在拉取optee的代码后,在optee_examples文件夹中主要有以下几个例子
- acipher:生成指定大小的 RSA 密钥对,并使用 GlobalPlatform TEE 内部核心 API 来加密提供的字符串。
- aes:使用 GlobalPlatform TEE 内部核心 API 从 TA 运行 AES 加密和解密。 非安全测试应用程序提供密钥、初始向量和加密数据。
- hello_world:这是一个非常简单的可信应用程序,用于应答 hello 命令并递增整数值。
- hotp:基于 HMAC 的一次性密码(简称“HOTP”)已存在多年,最初于 2005 年在 RFC4226 中定义。从那时起,它就成为进行两因素身份验证的流行选择。 通过此处的实现,我们将展示如何利用 OP-TEE 以安全的方式生成此类基于 HMAC 的一次性密码。
- plugins
- random:使用 TEE API (TEE_GenerateRandom()) 的功能生成随机 UUID。
- secure_storage:可信应用程序,使用 GlobalPlatform TEE 内部核心 API 将原始数据读/写到 OP-TEE 安全存储中。
这篇博客分析的主要是optee_examples中的hello_world。
二、目录结构
进入hello_world文件夹下,可以看到两个文件夹,一个是host,另一个是ta。还有其他三个和编译相关的文件(Android.mk,CMakeList.txt,Makefile)。其中host文件夹主要是CA相关的内容,和TA文件夹主要是ta相关的内容。
三、相关代码
3.1 CA代码
在host目录的main.c中,精简后,我们可以看到如下代码。
main.c
#include <err.h>
#include <stdio.h>
#include <string.h>
#include <tee_client_api.h>
#include <hello_world_ta.h>
int main(void)
{
TEEC_Result res;
TEEC_Context ctx;
TEEC_Session sess;
TEEC_Operation op;
TEEC_UUID uuid = TA_HELLO_WORLD_UUID;
uint32_t err_origin;
res = TEEC_InitializeContext(NULL, &ctx);
if (res != TEEC_SUCCESS)
errx(1, "TEEC_InitializeContext failed with code 0x%x", res);
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);
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);
TEEC_CloseSession(&sess);
TEEC_FinalizeContext(&ctx);
return 0;
}
可以看到,CA对TA的通信的整体过程分为以下5步
- TEEC_InitializeContext:初始化上下文
- TEEC_OpenSession:打开session
- TEEC_InvokeCommand:向TA发送命令
- TEEC_CloseSession:关闭session
- TEEC_FinalizeContext:销毁上下文
假如我们想自己开发一个CA-TA程序,那么在CA侧需要做的事主要是在第2步与第4步之间。向TA发送指令,之后TA根据收到的命令执行对应的程序,之后返回给CA。
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../ta/include -I$(TEEC_EXPORT)/include -I./include
#Add/link other required libraries here
LDADD += -lteec -L$(TEEC_EXPORT)/lib
BINARY = optee_example_hello_world
.PHONY: all
all: $(BINARY)
$(BINARY): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $< $(LDADD)
.PHONY: clean
clean:
rm -f $(OBJS) $(BINARY)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
假如我们要开发自己的CA-TA程序,那么需要对这个makefile文件中的BINARY进行修改,修改成自己自定义的名字。
3.2 TA代码
hello_world_ta.h
定义了注入UUID,INC_VALUE和DEC_VALUE等信息。
#ifndef TA_HELLO_WORLD_H
#define TA_HELLO_WORLD_H
/*
* This UUID is generated with uuidgen
* the ITU-T UUID generator at http://www.itu.int/ITU-T/asn1/uuid.html
*/
#define TA_HELLO_WORLD_UUID \
{ 0x8aaaf200, 0x2450, 0x11e4, \
{ 0xab, 0xe2, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b} }
/* The function IDs implemented in this TA */
#define TA_HELLO_WORLD_CMD_INC_VALUE 0
#define TA_HELLO_WORLD_CMD_DEC_VALUE 1
#endif /*TA_HELLO_WORLD_H*/
hello_world_ta.c
#include <tee_internal_api.h>
#include <tee_internal_api_extensions.h>
#include <hello_world_ta.h>
/*
* Called when the instance of the TA is created. This is the first call in
* the TA.
*/
TEE_Result TA_CreateEntryPoint(void)
{
DMSG("has been called");
return TEE_SUCCESS;
}
/*
* Called when the instance of the TA is destroyed if the TA has not
* crashed or panicked. This is the last call in the TA.
*/
void TA_DestroyEntryPoint(void)
{
DMSG("has been called");
}
/*
* Called when a new session is opened to the TA. *sess_ctx can be updated
* with a value to be able to identify this session in subsequent calls to the
* TA. In this function you will normally do the global initialization for the
* TA.
*/
TEE_Result TA_OpenSessionEntryPoint(uint32_t param_types,
TEE_Param __maybe_unused params[4],
void __maybe_unused **sess_ctx)
{
uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
DMSG("has been called");
if (param_types != exp_param_types)
return TEE_ERROR_BAD_PARAMETERS;
/* Unused parameters */
(void)¶ms;
(void)&sess_ctx;
/*
* The DMSG() macro is non-standard, TEE Internal API doesn't
* specify any means to logging from a TA.
*/
IMSG("Hello World!\n");
/* If return value != TEE_SUCCESS the session will not be created. */
return TEE_SUCCESS;
}
/*
* Called when a session is closed, sess_ctx hold the value that was
* assigned by TA_OpenSessionEntryPoint().
*/
void TA_CloseSessionEntryPoint(void __maybe_unused *sess_ctx)
{
(void)&sess_ctx; /* Unused parameter */
IMSG("Goodbye!\n");
}
static TEE_Result inc_value(uint32_t param_types,
TEE_Param params[4])
{
uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INOUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
DMSG("has been called");
if (param_types != exp_param_types)
return TEE_ERROR_BAD_PARAMETERS;
IMSG("Got value: %u from NW", params[0].value.a);
params[0].value.a++;
IMSG("Increase value to: %u", params[0].value.a);
return TEE_SUCCESS;
}
static TEE_Result dec_value(uint32_t param_types,
TEE_Param params[4])
{
uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INOUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
DMSG("has been called");
if (param_types != exp_param_types)
return TEE_ERROR_BAD_PARAMETERS;
IMSG("Got value: %u from NW", params[0].value.a);
params[0].value.a--;
IMSG("Decrease value to: %u", params[0].value.a);
return TEE_SUCCESS;
}
/*
* Called when a TA is invoked. sess_ctx hold that value that was
* assigned by TA_OpenSessionEntryPoint(). The rest of the paramters
* comes from normal world.
*/
TEE_Result TA_InvokeCommandEntryPoint(void __maybe_unused *sess_ctx,
uint32_t cmd_id,
uint32_t param_types, TEE_Param params[4])
{
(void)&sess_ctx; /* Unused parameter */
switch (cmd_id) {
case TA_HELLO_WORLD_CMD_INC_VALUE:
return inc_value(param_types, params);
case TA_HELLO_WORLD_CMD_DEC_VALUE:
return dec_value(param_types, params);
default:
return TEE_ERROR_BAD_PARAMETERS;
}
}
在该ta文件中,定义了对应的函数,这些函数被CA所调用,用于完成输出HelloWorld和值的自增。里面的诸如TA_OpenSessionEntryPoint等函数必须被定义。
user_ta_header_defines.h
定义了一些变量
#ifndef USER_TA_HEADER_DEFINES_H
#define USER_TA_HEADER_DEFINES_H
/* To get the TA UUID definition */
#include <hello_world_ta.h>
#define TA_UUID TA_HELLO_WORLD_UUID
/*
* TA properties: multi-instance TA, no specific attribute
* TA_FLAG_EXEC_DDR is meaningless but mandated.
*/
#define TA_FLAGS TA_FLAG_EXEC_DDR
/* Provisioned stack size */
#define TA_STACK_SIZE (2 * 1024)
/* Provisioned heap size for TEE_Malloc() and friends */
#define TA_DATA_SIZE (32 * 1024)
/* The gpd.ta.version property */
#define TA_VERSION "1.0"
/* The gpd.ta.description property */
#define TA_DESCRIPTION "Example of OP-TEE Hello World Trusted Application"
/* Extra properties */
#define TA_CURRENT_TA_EXT_PROPERTIES \
{ "org.linaro.optee.examples.hello_world.property1", \
USER_TA_PROP_TYPE_STRING, \
"Some string" }, \
{ "org.linaro.optee.examples.hello_world.property2", \
USER_TA_PROP_TYPE_U32, &(const uint32_t){ 0x0010 } }
#endif /* USER_TA_HEADER_DEFINES_H */
四、总结
本博客主要针对optee_example_hello_world中的代码进行了分析。当熟悉这个helloworld的代码后,后续可以添加自己的CA和TA,从而完成一些自定义的功能。