前言:
翻译:https://optee.readthedocs.io/en/latest/building/trusted_applications.html
上一篇翻译的文章:OP-TEE的安装。安装完之后,我可不想知道程序整个流程的具体细节。我现在想自己改写下hello world实现自己的TA和CA之间的交互。
简单的修改hello world实现一个example,可以参考这个视频:添加TA/CA的及简套路
而具体的细节,可以看下面对官网的翻译。文章按照我的理解/思路来翻译,以原文为准。同时我不明白的地方,我不翻译,直接给出原文。
可信应用
本文档介绍了如何使用OP-TEE的TA-devkit(TA的开发工具包)来构建(build)和签名(sign)Trusted Application二进制文件,实现Trusted Application。 本文档中,在OP-TEE os 中运行的可信应用程序,称为TA。 请注意,在默认设置中,由Linaro生成并与optee_os源一起分发的私钥用于对受信任的应用程序进行签名。 有关更多详细信息,请参见 TASign,包括TA的脱机签名。
1、TA必要组成文件
可信应用程序的Makefile编写,基于OP-TEE TA-devkit,以便成功构建目标应用程序。TA-devkit is built when one builds optee_os.
To build a TA, one must provide:
-
Makefile,一个make file 应该设置一些配置变量并包含TA-devkit make file。
-
sub.mk,一个make file,列出要构建的源(本地源文件,要解析的子目录,特定于源文件的构建指令)。
-
user_ta_header_defines.h ,一个特定的ANSI-C头文件,来定义大多数TA属性。
-
TA 入口点最少应该实现的外部函数:
TA_CreateEntryPoint()
,TA_DestroyEntryPoint()
,TA_OpenSessionEntryPoint()
,TA_CloseSessionEntryPoint()
,TA_InvokeCommandEntryPoint()
2、TA文件分布实例
As an example, hello_world looks like this:
hello_world/
├── ...
└── ta
├── Makefile BINARY=<uuid>
├── Android.mk Android way to invoke the Makefile
├── sub.mk srcs-y += hello_world_ta.c
├── include
│ └── hello_world_ta.h Header exported to non-secure: TA commands API
├── hello_world_ta.c Implementation of TA entry points
└── user_ta_header_defines.h TA_UUID, TA_FLAGS, TA_DATA/STACK_SIZE, ...
2.1、TA Makefile 基础
我先把hello world中的makefile文件内容,放出来。看不懂可以从下面的翻译中理解出来。(我好多不晓得。刚开始先知道改啥哈。晓得50%先)
这个文件,目前只会改BINARY。
CFG_TEE_TA_LOG_LEVEL ?= 4
CPPFLAGS += -DCFG_TEE_TA_LOG_LEVEL=$(CFG_TEE_TA_LOG_LEVEL)
# The UUID for the Trusted Application
BINARY=8aaaf200-2450-11e4-abe2-0002a5d5c51b
-include $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk
ifeq ($(wildcard $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk), )
clean:
@echo 'Note: $$(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk not found, cannot clean TA'
@echo 'Note: TA_DEV_KIT_DIR=$(TA_DEV_KIT_DIR)'
endif
2.1.1 Required variables
TA-devkit 的make file 在 optee_os 中 。位置:optee/optee_os/ta/mk/ta_dev_kit.mk
。它包含all
和 clean
,可以 build a TA or a library and clean the built objects 。
这里,我们知道:某个TA,它的makefile文件,包含(include)ta_dev_kit.mk
, 则可以执行 make all /make clean
- TA_DEV_KIT_DIR
TA-devkit的基本目录。 由TA-devkit本身用来定位其工具。(我用的是vscode,目前不知道变量的定义位置。)
- BINARY and LIBNAME
(此处没有全部翻译,注意参考原文)必须是唯一的。In native OP-TEE,BINARY变量保存的是UUID,用来区分不同的TA。 LIBNAME 干啥的,我不知道。
find -name 8aaaf200-2450-11e4-abe2-0002a5d5c51b.ta
原文部分:
These are exclusive, meaning that you cannot use both at the same time. If building a TA,
BINARY
shall provide the TA filename used to load the TA. The built and signed TA binary file will be named${BINARY}.ta
. In native OP-TEE, it is the TA UUID, used by tee-supplicant to identify TAs. If one is building a static library (that will be later linked by a TA), thenLIBNAME
shall provide the name of the library. The generated library binary file will be namedlib${LIBNAME}.a
- CROSS_COMPILE and CROSS_COMPILE32
用来交叉编译TA或者二进制源文件。CROSS_COMPILE32
is optional 。
Cross compiler for the TA or the library source files.
CROSS_COMPILE32
is optional. It allows to target AArch32 builds on AArch64 capable systems. On AArch32 systems,CROSS_COMPILE32
defaults toCROSS_COMPILE
.
- Optional variables
一些可选的配置变量,比如O
Base directory for build objects filetree. If not set, TA-devkit defaults to ./out from the TA source tree base directory.
2.2、sub.mk directives
同样的,先展示hello world的sub.mk
文件内容。带着问题去读官方文档。
global-incdirs-y += include
srcs-y += hello_world_ta.c
# To remove a certain compiler flag, add a line like this
#cflags-template_ta.c-y += -Wno-strict-prototypes
make file 期望当前目录有sub.mk
文件。sub.mk
列出要构建的源文件和其他特定构建指令。 以下是可以在sub.mk make文件中实现的指令的几个示例:
# Adds /hello_world_ta.c from current directory to the list of the source
# file to build and link.
srcs-y += hello_world_ta.c
# Includes path **./include/** from the current directory to the include
# path.
global-incdirs-y += include/
# Adds directive -Wno-strict-prototypes only to the file hello_world_ta.c
cflags-hello_world_ta.c-y += -Wno-strict-prototypes
# Removes directive -Wno-strict-prototypes from the build directives for
# hello_world_ta.c only.
cflags-remove-hello_world_ta.c-y += -Wno-strict-prototypes
# Adds the static library foo to the list of the linker directive -lfoo.
libnames += foo
# Adds the directory path to the libraries pathes list. Archive file
# libfoo.a is expected in this directory.
libdirs += path/to/libfoo/install/directory
# Adds the static library binary to the TA build dependencies.
libdeps += path/to/greatlib/libgreatlib.a
2.3、Android Build Environment
Android.mk 文件
不清楚。改动local_module是对的。
LOCAL_PATH := $(call my-dir)
local_module := 8aaaf200-2450-11e4-abe2-0002a5d5c51b.ta
include $(BUILD_OPTEE_MK)
OP-TEE的TA-devkit支持在Android构建环境中进行构建。 可以为TA编写一个Android.mk
文件(与Makefile并存)。 Android的构建系统将解析TA的Android.mk
文件,TA进而解析TA-devkit Android make文件来查找TA构建资源。 然后,Android构建将执行make
命令以通过其通用Makefile文件构建TA。
2.4、TA Mandatory Entry Points
同样的,首先给出hello world中的hello_world_ta.c
文件。有时候代码比较长,且。建议去gith一些常量定义在头文件中。这里没有给出头文件。建议去github看代码。
/*
* Copyright (c) 2016, 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 <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;
}
}
A TA must implement a couple of mandatory entry points, these are:
TEE_Result TA_CreateEntryPoint(void)
{
/* Allocate some resources, init something, ... */
...
/* Return with a status */
return TEE_SUCCESS;
}
void TA_DestroyEntryPoint(void)
{
/* Release resources if required before TA destruction */
...
}
TEE_Result TA_OpenSessionEntryPoint(uint32_t ptype,
TEE_Param param[4],
void **session_id_ptr)
{
/* Check client identity, and alloc/init some session resources if any */
...
/* Return with a status */
return TEE_SUCCESS;
}
void TA_CloseSessionEntryPoint(void *sess_ptr)
{
/* check client and handle session resource release, if any */
...
}
TEE_Result TA_InvokeCommandEntryPoint(void *session_id,
uint32_t command_id,
uint32_t parameters_type,
TEE_Param parameters[4])
{
/* Decode the command and process execution of the target service */
...
/* Return with a status */
return TEE_SUCCESS;
}
2.5、TA Properties
/*
* Copyright (c) 2016-2017, 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.
*/
/*
* The name of this file must not be modified
*/
#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 */
一个TA的属性应该定义在一个头文件中:user_ta_header_defines.h
包含
TA_UUID
defines the TA uuid valueTA_FLAGS
define some of the TA propertiesTA_STACK_SIZE
defines the RAM size to be reserved for TA stackTA_DATA_SIZE
defines the RAM size to be reserved for TA heap (TEE_Malloc() pool)
参考 TA Properties ,理解如何配置这些宏.
提示:两种方式来产生唯一的UUID
python -c 'import uuid; print(uuid.uuid4())'
cat /proc/sys/kernel/random/uuid # Linux only uuidgen # available from the util-linux package in most distributions
2.6、Checking TA parameters
分析下hello world中类型检查。
我们期望传入的数据类型:uint32_t exp_param_types = 3;
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);
if (param_types != exp_param_types)
return TEE_ERROR_BAD_PARAMETERS;
#define TEE_PARAM_TYPES(t0,t1,t2,t3) ((t0) | ((t1) << 4) | ((t2) << 8) | ((t3) << 12))
The macro TEE_PARAM_TYPES can be used to construct a value that you can
compare against an incoming paramTypes to check the type of all the
parameters in one comparison, like in the following example:
if (paramTypes != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_MEMREF_OUPUT,
TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE)) {
return TEE_ERROR_BAD_PARAMETERS;
}
Expands to:
((3) | ((0) << 4) | ((0) << 8) | ((0) << 12))
#define TEE_PARAM_TYPE_VALUE_INOUT 3
#define TEE_PARAM_TYPE_NONE 0
param_types 是 TA_InvokeCommandEntryPoint 函数的第三个参数,而且是从normal world中传来。
我们可以推测,是来自TEEC_InvokeCommand
的op。
且结构体op中的paramTypes,也被初始化成3。
TEE_Result TA_InvokeCommandEntryPoint(void *sess_ctx, uint32_t cmd_id, uint32_t param_types, TEE_Param *params)
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.
res = TEEC_InvokeCommand(&sess, TA_HELLO_WORLD_CMD_INC_VALUE, &op,
&err_origin);
TEEC_Operation op;
typedef struct {
uint32_t started;
uint32_t paramTypes;
TEEC_Parameter params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
/* Implementation-Defined */
TEEC_Session *session;
} TEEC_Operation;
memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE,
TEEC_NONE, TEEC_NONE);
#define TEEC_PARAM_TYPES(p0,p1,p2,p3) ((p0) | ((p1) << 4) | ((p2) << 8) | ((p3) << 12))
Encode the paramTypes according to the supplied types.
@param p0 The first param type.
@param p1 The second param type.
@param p2 The third param type.
@param p3 The fourth param type.
Expands to:
((0x00000003) | ((0x00000000) << 4) | ((0x00000000) << 8) | ((0x00000000) << 12))
#define TEEC_VALUE_INOUT 0x00000003
#define TEEC_NONE 0x00000000
所以期望的类型和传入的类型相同。
“检查参数类型”,这一段我没有翻译。如果需要自行在官方文档处查看。(因为hello world中,没有采用文档中的检查方法)
3、TAs的签名
(签名这里,我不会。哪天操作过后,来补充)
All REE Filesystem Trusted Applications need to be signed.
在加载TA时,optee_os验证签名。
optee_os中,有个key
目录。
All REE Filesystem Trusted Applications need to be signed. The signature is verified by optee_os upon loading of the TA. Within the optee_os source is a directory
keys
. The public part ofkeys/default_ta.pem
will be compiled into the optee_os binary and the signature of each TA will be verified against this key upon loading. Currentlykeys/default_ta.pem
must contain an RSA key.
警告:
optee_os 为了测试和开发的方便,默认含有私钥。所以在生产中,千万钥替换成自己的私钥/公钥。私钥别傻不拉几的上传到网上。
在optee_os中,使用sign.py脚本对TAs进行签名。而这个脚本参考ta/mk/ta_dev_kit.mk
其默认行为是对已编译的TA二进制文件进行签名,并附加签名以形成用于部署的完整TA。 对于脱机签名,需要三步过程:在第一步中,必须生成已编译二进制文件的摘要,在第二步中,使用私钥对该摘要进行脱机签名,最后在第三步中,对二进制文件及其摘要进行签名。 签名被缝合到完整的TA中。
3.1 Offline Signing of TAs
TA开发工具包确实在链接过程的最后一步对应用程序进行了签名。 例如,optee_os源树中的文件ta / arch / arm / link.mk包含以下语句
$(q)$(SIGN) --key $(TA_SIGN_KEY) --uuid $(user-ta-uuid) --version 0 \
--in $$< --out $$@
为了避免在脱机签名时出现构建错误,需要采用此make脚本。 签名脚本可以在以下位置找到$(TA_DEV_KIT_DIR)/../scripts/sign.py
总体而言,脱机签名通过以下步骤序列完成:
-
(准备工作)生成2048位RSA密钥,以在安全的脱机环境中签名。 解压缩公钥并将其复制到optee_os源树中的keys目录。 调整
TA_SIGN_KEY
以使用不同的文件/路径名称。 (复制并)修改link.mk
文件以进行默认链接步骤,以生成TA二进制文件的摘要,而不是完整TA的摘要。 -
手动(或使用修改的链接脚本)使用以下命令生成TA二进制文件的摘要
sign.py digest --key $(TA_SIGN_KEY) --uuid $(user-ta-uuid)
- 离线签署此摘要,例如 使用OpenSSL
base64 --decode digestfile | \
openssl pkeyutl -sign -inkey $TA_SIGN_KEY \
-pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pkcs1 | \
base64 > sigfile
or with pkcs11-tool using a Nitrokey HSM
echo "0000: 3031300D 06096086 48016503 04020105 000420" | \
xxd -c 19 -r > /tmp/sighdr
cat /tmp/sighdr $(base64 --decode digestfile) > /tmp/hashtosign
pkcs11-tool --id $key_id -s --login -m RSA-PKCS \
--input-file /tmp/hashtosign | \
base64 > sigfile
- 手动(或与其他目标一起)将TA缝在一起
sign.py stitch --key $(TA_SIGN_KEY) --uuid $(user-ta-uuid)
默认情况下,UUID被用作所有文件的基本文件名。 可以通过sign.py
的其他选项设置不同的文件名和路径。 有关选项和参数的完整列表,请查阅sign.py --help
。
译者话
TAs需要:编译的配置文件+入口函数的实现+签名
暂时缺:sign.py程序的分析。
这篇文章需改动结构:重点/目的不突出。
其他:
- 科目三挂了。。。
- 《精英律师》凑或。
- 快过年,安逸挺好。
- 大草 2020/1/22 阴