Preface:
Translation: https://optee.readthedocs.io/en/latest/building/trusted_applications.html
Last translated article: Installation of OP-TEE .After installation, I don't want to know the details of the whole process of the program.Now I want to rewrite hello world for myself to interact between TA and CA.
Simply modify hello world to implement an example, referring to this video: Add TA/CA and Simple Route
For details, you can see the translation of the official website below.The article will be translated according to my understanding/thinking, whichever is the original.At the same time, what I don't understand is that I don't translate and give the original text directly.
Trusted Application
This document describes how to use OP-TEE's TA-devkit(TA's development kit) to build and sign Trusted Application binaries to implement Trusted Application.In this document, a trusted application running in OP-TEE os is called TA.Note that, in the default settings, it is generated by Linaro and associated with optee_os The private keys distributed together by the sources are used to sign trusted applications.For more details, see TASign , including TA's offline signature.
1. TA Required Component Files
Makefile for a trusted application, based on OP-TEE TA-devkit, to successfully build the target application.TA-devkit is built when one builds optee_os.
To build a TA, one must provide:
-
Makefile, a make file should set some configuration variables and contain TA-devkit make file.
-
sub.mk, a make file, lists the sources to be built (local source files, subdirectories to be parsed, source-specific build instructions).
-
user_ta_header_defines.h, a specific ANSI-C header file, defines most TA properties.
-
The minimum external functions that a TA entry point should implement are TA_CreateEntryPoint(), TA_DestroyEntryPoint(), TA_OpenSessionEntryPoint(), TA_CloseSessionEntryPoint(),TA_InvokeCommandEntryPoint().
2. TA file distribution instance
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 Foundation
Let me first release the makefile contents from hello world.Understanding can be understood from the translation below.(I don't know a lot about it.You know what to do first.50% Know First)
This file, for now, will only change 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 in optee_os Medium.Location: optee/optee_os/ta/mk/ta_dev_kit.mk.It contains all and clean and can build a TA or a library and clean the built objects.
Here, we know that a TA whose makefile contains (include)ta_dev_kit.mk can make all/make clean
- TA_DEV_KIT_DIR
The base directory of TA-devkit.The TA-devkit itself is used to locate its tools.(I'm using vscode, and I don't know where variables are defined at the moment.)
- BINARY and LIBNAME
(Not all translations are available here, note the reference text) must be unique.In native OP-TEE, the BINARY variable holds a UUID to distinguish between different TA.What LIBNAME does, I don't know.
find -name 8aaaf200-2450-11e4-abe2-0002a5d5c51b.ta
Original part:
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
Used to cross-compile TA or binary source files.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
Some optional configuration variables, such as 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
Similarly, first show the contents of the hello world sub.mk file.Read the official documents with questions.
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 expects the current directory to have a sub.mk file.The sub.mk lists the source files to be built and other specific build instructions.Here are a few examples of instructions that can be implemented in the sub.mk make file:
# 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 file
Unclear.It is correct to change the local_module.
LOCAL_PATH := $(call my-dir) local_module := 8aaaf200-2450-11e4-abe2-0002a5d5c51b.ta include $(BUILD_OPTEE_MK)
The TA-devkit for OP-TEE supports building in the Android build environment.You can write an Android.mk file (coexisting with Makefile) for TA.Android's build system will parse TA's Android.mk file, TA will then parse TA-devkit Android make file to find TA build resources.Android Build then executes the make command to build TA from its generic Makefile file.
2.4,TA Mandatory Entry Points
Similarly, the hello_world_ta.c file in hello world is given first.Sometimes the code is longer and longer.It is recommended that some constants be defined in the header file.No header file is given here.It is recommended that you go to github to see the code.
/* * 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 */
The attributes of a TA should be defined in a header file: user_ta_header_defines.h contains
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)
Reference resources TA Properties To understand how to configure these macros.
Tip: There are two ways to generate a unique 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
Analyze the type checks in hello world.
We expect the data type to be passed in: 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 is the third parameter of the TA_InvokeCommandEntryPoint function and is passed from normal world.
We can speculate that it is the op from TEEC_InvokeCommand.
The paramTypes in the structure op are also initialized to 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
So the expected type is the same as the type passed in.
"Check parameter type", I have not translated this paragraph.If you need to look at the official documents yourself.(Because in hello world, checks in the document are not used)
3. Signature of TAs
(Sign here, I won't.What day to add after operation)
All REE Filesystem Trusted Applications need to be signed.
When TA is loaded, optee_os verifies the signature.
There is a key directory in optee_os.
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.
Warning:
Optiee_os contains a private key by default for testing and development convenience.So in production, tens of millions of keys are replaced with their own private/public keys.Don't fool about uploading private keys online.
In optee_os, use the sign.py script to sign TAs.The default behavior of this script, which refers to ta/mk/ta_dev_kit.mk, is to sign the compiled TA binary and append the signature to form a complete TA for deployment.For offline signatures, a three-step process is required: in the first step, a digest of the compiled binary must be generated, in the second step, the digest is offline signed using the private key, and in the third step, the binary and its Digest are signed.The signature is stitched into the complete TA.
3.1 Offline Signing of TAs
The TA Development Kit did sign the application at the last step of the linking process.For example, the file ta / arch / arm / link.mk in the optee_os source tree contains the following statements
$(q)$(SIGN) --key $(TA_SIGN_KEY) --uuid $(user-ta-uuid) --version 0 \ --in $$< --out $$@
To avoid build errors when signing offline, you need to use this make script.The signature script can be found at $(TA_DEV_KIT_DIR)/. /scripts/sign.py
Overall, the offline signature is done using the following sequence of steps:
-
(Ready) Generate a 2048-bit RSA key to sign in a secure offline environment.Unzip the public key and copy it to the keys directory in the optee_os source tree.Adjust TA_SIGN_KEY to use a different file/path name.(Copy and) Modify the link.mk file for the default link step to generate a summary of the TA binary instead of the complete TA summary.
-
Manually (or using a modified link script) use the following commands to generate a summary of TA binaries
sign.py digest --key $(TA_SIGN_KEY) --uuid $(user-ta-uuid)
- Sign this summary offline, for example, using 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
- Stitch TA together manually (or with other targets)
sign.py stitch --key $(TA_SIGN_KEY) --uuid $(user-ta-uuid)
By default, the UUID is used as the base file name for all files.Other options for sign.py allow you to set different file names and paths.For a complete list of options and parameters, see sign.py --help.
Translator's words
TAs require: compiled configuration file + implementation of entry function + signature
Temporarily missing: analysis of the sign.py program.
This article needs to be structured: the focus/purpose is not prominent.
Other:
- Subject three hangs up.
- Elite Lawyer compact or.
- Fast Spring Festival, very comfortable.
- Dacao 2020/1/22 Yin