blob: ab067b9bb0475f3a48990f4e824eee079940c25d [file] [log] [blame]
Joakim Bech8e5c5b32018-10-25 08:18:32 +02001.. _build_trusted_applications:
2
3####################
4Trusted Applications
5####################
6This document tells how to implement a Trusted Application for OP-TEE, using
7OP-TEE's so called `TA-devkit` to both build and sign the Trusted Application
8binary. In this document, a `Trusted Application` running in the OP-TEE os is
9referred to as a `TA`.
Markus S. Wamser1aa72d82019-02-28 15:21:08 +010010Note that in the default setup a private key generated by Linaro and
11distributed along with the :ref:`optee_os` source is used for signing Trusted
12Applications.
13See TASign_ for more details, including offline signing of TAs.
Joakim Bech8e5c5b32018-10-25 08:18:32 +020014
15TA Mandatory files
16******************
17The Makefile for a Trusted Application must be written to rely on OP-TEE
18TA-devkit resources in order to successfully build the target application.
19TA-devkit is built when one builds :ref:`optee_os`.
20
21.. todo::
22
23 Joakim: We need to add CMake instructions also.
24
25To build a TA, one must provide:
26
27 - **Makefile**, a make file that should set some configuration variables and
28 include the TA-devkit make file.
29
30 - **sub.mk**, a make file that lists the sources to build (local source
31 files, subdirectories to parse, source file specific build directives).
32
33 - **user_ta_header_defines.h**, a specific ANSI-C header file to define most
34 of the TA properties.
35
Markus S. Wamser030dae02019-08-07 15:36:36 +020036 - An implementation of at least the TA entry points, as extern functions:
Joakim Bech8e5c5b32018-10-25 08:18:32 +020037 ``TA_CreateEntryPoint()``, ``TA_DestroyEntryPoint()``,
38 ``TA_OpenSessionEntryPoint()``, ``TA_CloseSessionEntryPoint()``,
39 ``TA_InvokeCommandEntryPoint()``
40
41TA file layout example
42======================
43As an example, :ref:`hello_world` looks like this:
44
45.. code-block:: none
46
47 hello_world/
48 ├── ...
49 └── ta
50 ├── Makefile BINARY=<uuid>
51 ├── Android.mk Android way to invoke the Makefile
52 ├── sub.mk srcs-y += hello_world_ta.c
53 ├── include
54 │   └── hello_world_ta.h Header exported to non-secure: TA commands API
Markus S. Wamser030dae02019-08-07 15:36:36 +020055 ├── hello_world_ta.c Implementation of TA entry points
Joakim Bech8e5c5b32018-10-25 08:18:32 +020056 └── user_ta_header_defines.h TA_UUID, TA_FLAGS, TA_DATA/STACK_SIZE, ...
57
58TA Makefile Basics
59******************
60Required variables
61==================
Markus S. Wamser030dae02019-08-07 15:36:36 +020062The main TA-devkit make file is located in :ref:`optee_os` at
Joakim Bech8e5c5b32018-10-25 08:18:32 +020063``ta/mk/ta_dev_kit.mk``. The make file supports make targets such as ``all`` and
64``clean`` to build a TA or a library and clean the built objects.
65
66The make file expects a couple of configuration variables:
67
68TA_DEV_KIT_DIR
Markus S. Wamser030dae02019-08-07 15:36:36 +020069 Base directory of the TA-devkit. Used by the TA-devkit itself to locate its tools.
Joakim Bech8e5c5b32018-10-25 08:18:32 +020070
71BINARY and LIBNAME
72 These are exclusive, meaning that you cannot use both at the same time. If
73 building a TA, ``BINARY`` shall provide the TA filename used to load the TA.
74 The built and signed TA binary file will be named ``${BINARY}.ta``. In
75 native OP-TEE, it is the TA UUID, used by tee-supplicant to identify TAs. If
76 one is building a static library (that will be later linked by a TA), then
77 ``LIBNAME`` shall provide the name of the library. The generated library
78 binary file will be named ``lib${LIBNAME}.a``
79
80CROSS_COMPILE and CROSS_COMPILE32
81 Cross compiler for the TA or the library source files. ``CROSS_COMPILE32``
82 is optional. It allows to target AArch32 builds on AArch64 capable systems.
83 On AArch32 systems, ``CROSS_COMPILE32`` defaults to ``CROSS_COMPILE``.
84
85Optional variables
86==================
87Some optional configuration variables can be supported, for example:
88
89O
90 Base directory for build objects filetree. If not set, TA-devkit defaults to
91 **./out** from the TA source tree base directory.
92
93Example Makefile
94================
95A typical Makefile for a TA looks something like this
96
97.. code-block:: Makefile
98
99 # Append specific configuration to the C source build (here log=info)
100 # The UUID for the Trusted Application
101 BINARY=8aaaf200-2450-11e4-abe2-0002a5d5c51b
102
103 # Source the TA-devkit make file
104 include $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk
105
106.. _build_trusted_applications_submk:
107
108sub.mk directives
109=================
110The make file expects that current directory contains a file ``sub.mk`` that is
111the entry point for listing the source files to build and other specific build
112directives. Here are a couple of examples of directives one can implement in a
113sub.mk make file:
114
115.. code-block:: Makefile
116
117 # Adds /hello_world_ta.c from current directory to the list of the source
118 # file to build and link.
119 srcs-y += hello_world_ta.c
120
121 # Includes path **./include/** from the current directory to the include
122 # path.
123 global-incdirs-y += include/
124
125 # Adds directive -Wno-strict-prototypes only to the file hello_world_ta.c
126 cflags-hello_world_ta.c-y += -Wno-strict-prototypes
127
128 # Removes directive -Wno-strict-prototypes from the build directives for
129 # hello_world_ta.c only.
130 cflags-remove-hello_world_ta.c-y += -Wno-strict-prototypes
131
132 # Adds the static library foo to the list of the linker directive -lfoo.
133 libnames += foo
134
135 # Adds the directory path to the libraries pathes list. Archive file
Markus S. Wamser030dae02019-08-07 15:36:36 +0200136 # libfoo.a is expected in this directory.
Joakim Bech8e5c5b32018-10-25 08:18:32 +0200137 libdirs += path/to/libfoo/install/directory
138
139 # Adds the static library binary to the TA build dependencies.
140 libdeps += path/to/greatlib/libgreatlib.a
141
142Android Build Environment
143*************************
144.. todo::
145
146 Joakim: Move this to the AOSP page?
147
148OP-TEE's TA-devkit supports building in an Android build environment. One can
149write an ``Android.mk`` file for the TA (stored side by side with the Makefile).
150Android's build system will parse the ``Android.mk`` file for the TA which in
151turn will parse a TA-devkit Android make file to locate TA build resources. Then
152the Android build will execute a ``make`` command to built the TA through its
153generic Makefile file.
154
155A typical ``Android.mk`` file for a TA looks like this (``Android.mk`` for
156:ref:`hello_world` is used as an example here).
157
158.. code-block:: Makefile
159
160 # Define base path for the TA sources filetree
161 LOCAL_PATH := $(call my-dir)
162
163 # Define the module name as the signed TA binary filename.
164 local_module := 8aaaf200-2450-11e4-abe2-0002a5d5c51b.ta
165
Markus S. Wamser030dae02019-08-07 15:36:36 +0200166 # Include the devkit Android make script
Joakim Bech8e5c5b32018-10-25 08:18:32 +0200167 include $(OPTEE_OS_DIR)/mk/aosp_optee.mk
168
169TA Mandatory Entry Points
170*************************
171A TA must implement a couple of mandatory entry points, these are:
172
173.. code-block:: c
174
175 TEE_Result TA_CreateEntryPoint(void)
176 {
177 /* Allocate some resources, init something, ... */
178 ...
179
180 /* Return with a status */
181 return TEE_SUCCESS;
182 }
183
184 void TA_DestroyEntryPoint(void)
185 {
186 /* Release resources if required before TA destruction */
187 ...
188 }
189
190 TEE_Result TA_OpenSessionEntryPoint(uint32_t ptype,
191 TEE_Param param[4],
192 void **session_id_ptr)
193 {
194 /* Check client identity, and alloc/init some session resources if any */
195 ...
196
197 /* Return with a status */
198 return TEE_SUCCESS;
199 }
200
201 void TA_CloseSessionEntryPoint(void *sess_ptr)
202 {
203 /* check client and handle session resource release, if any */
204 ...
205 }
206
207 TEE_Result TA_InvokeCommandEntryPoint(void *session_id,
208 uint32_t command_id,
209 uint32_t parameters_type,
210 TEE_Param parameters[4])
211 {
212 /* Decode the command and process execution of the target service */
213 ...
214
215 /* Return with a status */
216 return TEE_SUCCESS;
217 }
218
219.. _build_ta_properties:
220
221TA Properties
222*************
223Trusted Application properties shall be defined in a header file named
224``user_ta_header_defines.h``, which should contain:
225
226 - ``TA_UUID`` defines the TA uuid value
227 - ``TA_FLAGS`` define some of the TA properties
228 - ``TA_STACK_SIZE`` defines the RAM size to be reserved for TA stack
229 - ``TA_DATA_SIZE`` defines the RAM size to be reserved for TA heap (TEE_Malloc()
230 pool)
231
232Refer to :ref:`ta_properties` to understand how to configure these macros.
233
Markus S. Wamser1aa72d82019-02-28 15:21:08 +0100234.. hint::
235
236 UUIDs can be generated using python
237
238 .. code-block:: python
239
240 python -c 'import uuid; print(uuid.uuid4())'
241
242 or in most Linux systems using either
243
244 .. code-block:: bash
245
246 cat /proc/sys/kernel/random/uuid # Linux only
247 uuidgen # available from the util-linux package in most distributions
248
249
250
Joakim Bech8e5c5b32018-10-25 08:18:32 +0200251.. _user_ta_header_defines_h:
252
253Example of a property header file
254=================================
255
256.. code-block:: c
257
258 #ifndef USER_TA_HEADER_DEFINES_H
259 #define USER_TA_HEADER_DEFINES_H
260
261 #define TA_UUID
262 { 0x8aaaf200, 0x2450, 0x11e4, \
263 { 0xab, 0xe2, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b} }
264
265 #define TA_FLAGS (TA_FLAG_EXEC_DDR | \
266 TA_FLAG_SINGLE_INSTANCE | \
267 TA_FLAG_MULTI_SESSION)
268 #define TA_STACK_SIZE (2 * 1024)
269 #define TA_DATA_SIZE (32 * 1024)
270
271 #define TA_CURRENT_TA_EXT_PROPERTIES \
272 { "gp.ta.description", USER_TA_PROP_TYPE_STRING, "Foo TA for some purpose." }, \
273 { "gp.ta.version", USER_TA_PROP_TYPE_U32, &(const uint32_t){ 0x0100 } }
274
275 #endif /* USER_TA_HEADER_DEFINES_H */
276
277.. note::
278
279 It is recommended to use the ``TA_CURRENT_TA_EXT_PROPERTIES`` as above to
280 define extra properties of the TA.
281
Markus S. Wamser1aa72d82019-02-28 15:21:08 +0100282.. note::
283
284 Generating a fresh UUID with suitable formatting for the header file can be
285 done using:
286
287 .. code-block:: python
288
289 python -c "import uuid; u=uuid.uuid4(); print(u); \
290 n = [', 0x'] * 11; \
291 n[::2] = ['{:12x}'.format(u.node)[i:i + 2] for i in range(0, 12, 2)]; \
292 print('\n' + '#define TA_UUID\n\t{ ' + \
293 '0x{:08x}'.format(u.time_low) + ', ' + \
294 '0x{:04x}'.format(u.time_mid) + ', ' + \
295 '0x{:04x}'.format(u.time_hi_version) + ', \\ \n\n\t\t{ ' + \
296 '0x{:02x}'.format(u.clock_seq_hi_variant) + ', ' + \
297 '0x{:02x}'.format(u.clock_seq_low) + ', ' + \
298 '0x' + ''.join(n) + '} }')"
299
300
Joakim Bech8e5c5b32018-10-25 08:18:32 +0200301Checking TA parameters
302**********************
303GlobalPlatforms TEE Client APIs ``TEEC_InvokeCommand()`` and
304``TEE_OpenSession()`` allow clients to invoke a TA with some invocation
305parameters: values or references to memory buffers. It is mandatory that TA's
306verify the parameters types before using the parameters themselves. For this a
307TA can rely on the macro ``TEE_PARAM_TYPE_GET(param_type, param_index)`` to get
308the type of a parameter and check its value according to the expected parameter.
309
310For example, if a TA expects that command ID 0 comes with ``params[0]`` being a
311input value, ``params[1]`` being a output value, and ``params[2]`` being a
312in/out memory reference (buffer), then the TA should implemented the following
313sequence:
314
315.. code-block:: c
316
317 TEE_Result handle_command_0(void *session, uint32_t cmd_id,
318 uint32_t param_types, TEE_Param params[4])
319 {
320 if ((TEE_PARAM_TYPE_GET(param_types, 0) != TEE_PARAM_TYPE_VALUE_IN) ||
321 (TEE_PARAM_TYPE_GET(param_types, 1) != TEE_PARAM_TYPE_VALUE_OUT) ||
322 (TEE_PARAM_TYPE_GET(param_types, 2) != TEE_PARAM_TYPE_MEMREF_INOUT) ||
323 (TEE_PARAM_TYPE_GET(param_types, 3) != TEE_PARAM_TYPE_NONE)) {
324 return TEE_ERROR_BAD_PARAMETERS
325 }
326
327 /* process command */
328 ...
329 }
330
331 TEE_Result TA_InvokeCommandEntryPoint(void *session, uint32_t command_id,
332 uint32_t param_types, TEE_Param params[4])
333 {
334 switch (command_id) {
335 case 0:
336 return handle_command_0(session, param_types, params);
337
338 default:
339 return TEE_ERROR_NOT_SUPPORTED;
340 }
341 }
Markus S. Wamser1aa72d82019-02-28 15:21:08 +0100342
343.. _TASign:
344
345Signing of TAs
346**************
347
348All :ref:`REE Filesystem Trusted Applications<ree_fs_ta>` need to be signed. The
349signature is verified by :ref:`optee_os` upon loading of the TA. Within the
350:ref:`optee_os` source is a directory ``keys``. The public part of
351``keys/default_ta.pem`` will be compiled into the :ref:`optee_os` binary and the
352signature of each TA will be verified against this key upon loading. Currently
353``keys/default_ta.pem`` must contain an RSA key.
354
355.. warning::
356
357 :ref:`optee_os` comes with a default **private** key in its source to
358 facilitate easy development, testing, debugging and QA. Never deploy an
359 :ref:`optee_os` binary with this key in production. Instead replace this key
360 as soon as possible with a public key and keep the private part of the key
361 offline, preferably on an HSM.
362
363.. note::
364
365 Currently only a single key for signing TAs is supported by :ref:`optee_os`.
366
367TAs are signed using the ``sign.py`` script referenced from
368``ta/mk/ta_dev_kit.mk`` in :ref:`optee_os`. Its default behaviour is to sign a
369compiled TA binary and attach the signature to form a complete TA for
370deployment. For **offline** signing, a three-step process is required: In a
371first step a digest of the compiled binary has to be generated, in the second
372step this digest is signed offline using the private key and finally in the
373third step the binary and its signature are stitched together into the full TA.
374
375Offline Signing of TAs
376======================
377
378The TA dev kit does sign an application as last step of the linking process. For
379example, the file ``ta/arch/arm/link.mk`` in the :ref:`optee_os` source tree
380contains the statement
381
382.. code-block:: sh
383
384 $(q)$(SIGN) --key $(TA_SIGN_KEY) --uuid $(user-ta-uuid) \
385 --in $$< --out $$@
386
387To avoid build errors when signing offline, this make script needs to be
388adopted. The signing script can be found at
389``$(TA_DEV_KIT_DIR)/../scripts/sign.py``
390
391Overall, offline signing is done with the following sequence of steps:
392
3930. (Preparation) Generate a 2048 bit RSA key for signing in a secure, offline
394environment. Extract the public key and copy it to the ``keys`` directory in the
395:ref:`optee_os` source tree. Adjust ``TA_SIGN_KEY`` for different file/path
396names. (Copy and) modify the ``link.mk`` file for the default linking step to
397produce a digest of the TA binary instead of the full TA.
398
3991. Manually (or with the modified linking script) generate a digest of the TA
400binary using
401
402.. code-block:: sh
403
404 sign.py digest --key $(TA_SIGN_KEY) --uuid $(user-ta-uuid)
405
4062. Sign this digest offline, e.g. with OpenSSL
407
408.. code-block:: sh
409
410 base64 --decode digestfile | \
411 openssl pkeyutl -sign -inkey $TA_SIGN_KEY \
412 -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pkcs1 | \
413 base64 > sigfile
414
415or with pkcs11-tool using a Nitrokey HSM
416
417.. code-block:: sh
418
419 echo "0000: 3031300D 06096086 48016503 04020105 000420" | \
420 xxd -c 19 -r > /tmp/sighdr
421 cat /tmp/sighdr $(base64 --decode digestfile) > /tmp/hashtosign
422 pkcs11-tool --id $key_id -s --login -m RSA-PKCS \
423 --input-file /tmp/hashtosign | \
424 base64 > sigfile
425
4263. Manually (or with an extra make target) stitch the TA together using
427
428.. code-block:: sh
429
430 sign.py stitch --key $(TA_SIGN_KEY) --uuid $(user-ta-uuid)
431
432By default the UUID is taken as the base file name for all files. Different file
433names and paths can be set through additional options to ``sign.py``. Consult
434``sign.py --help`` for a full list of options and parameters.