TrustZone: trusted OS and its applications

    In previous articles we looked at the TrustZone hardware and the operation of the Secure Monitor mechanism. Today we will talk about trusted OS (TEE) and its applications. And if last time there were fairly low-level things, now everything will be at a quite high level - at the level of the operating system.

    What is TEE

    What is TEE? This is a trusted execution environment (Trusted Execution Environment), in the first place - it is a program execution environment. We describe it in terms of function and properties, but not in the sense of programming, but in the philosophical sense.

    For example, a long-distance train, an electric train and a taxi have the most important function - to transport people. But they differ in properties, for example: the train travels between cities, an electric train - outside the city, and taxis - mainly in the city. Train and train ticket, taxi - no. And so on.

    The TEE function is to trust us to store some data for us and to run applications for us. We want to send TEE commands: start such an application, take such and such data and do this and that with them. In this case, the application code can not see, as well as the data. We will only get the result. Interaction with TEE is very similar to RPC.

    This function is ideal for different cryptography, for example, for electronic signatures: the keys are stored in the TEE, and we ask the TEE to sign the transferred data with the key stored in the TEE. We get the result, but do not have access to the key.

    TEE has a number of properties, but the main ones are as follows: a) we trust its implementation, and b) it is reliably separated from the main OS of the device, protected, it is difficult to break or break. There are other properties, but we call it a trusted OS for that. Property b) the most important thing is that TEE is separated and difficult to break, that is, it is protected.

    If you look at TEE through the prism of functions and properties, it becomes clear that TEE is not even completely about TrustZone. TrustZone is one of the ways to separate TEE from the main (guest) OS.

    TEE Implementation Options

    If the main features of TEE are that it is separated and difficult to break, then we can come up with different options for implementing TEE:

    • Use TrustZone - we get the separation of TEE and the main OS within a single processor core.
    • Run TEE on a separate core within the system on a chip and communicate with it through the hardware interface. Some specialized processors have separate trusted cores for performing TEE, but you cannot buy them in the store, alas. But you can take a dual-core crystal, for example, Cortex-A + Cortex-M0 / M4 and run on Cortex-M TEE.
    • Run TEE in a separate chip and establish a secure connection with it through an external interface, for example, SPI or SMbus. To protect communication use cryptographic methods.
      This method is used when you establish a connection with a smart card, for example, a chip plastic payment card. In a sense, TEE is executed in the chip, because, at our request, it is very trustworthy doing financial transactions, storing data, etc.
      The same method is used in the TPM (Trusted Platform Module) of modern PC architecture.

    We will continue to talk only about the implementation of TEE in TrustZone, because it is a very common version of the implementation of TEE. But much will be said about TEE in general.

    TEE as OS

    In previous articles, we always called TEE a trusted OS and said that it was in many ways similar to real operating systems.

    Without claiming generality, let's say that in the bulk of TEE have:
    • applications and processes: TEE can load applications and execute them;
    • separation of processes and core memory: MMU is used to protect the process memory space and to protect the TEE core memory;
    • threads, interaction processes;
    • data storage.

    You can come up with more abbreviated TEE variants, for example, without dynamic application loading, without process interaction, without threads, but the applications themselves, data storage, and separation of the process memory space and the kernel will remain.
    Hidden text
    Пример урезанного TEE можно наблюдать сейчас в проекте ARM Trusted Firmware-M для нового поколения микроконтроллеров Cortex-M на платформе ARMv8-M. Это урезанная TEE, сейчас там есть поддержка микроконтроллеров на ядрах Cortex-M23 и Cortex-M33. Это flash-based микроконтроллеры, примерно эквивалентные Cortex-M0 и Cortex-M3, но с поддержкой TrustZone. У них мало ОЗУ, программа выполняется преимущественно из Flash, и поэтому в TEE нет динамической загрузки программ. На данный момент TF-M еще и однопоточная.

    TEE software interface

    To interact with other software components, TEE has an API:

    • TEE provides APIs for programs via system calls (Supervisor Call, SVC command);
    • TEE provides an API for Normal World via Secure Monitor calls (SMC command).

    Through system calls, programs save data and call OS functions. Like any decent OS, TEE tries to abstract programs from hardware to one degree or another.
    For example, Linux abstracts work with files through open, read, write, close calls - all stdio functions in principle fall on the OS system calls. And TEE also allows its applications to work with stored data through calls, which in an abstract form save and load objects (data blocks) into the repository. Also, TEE can provide some cryptographic functions at the system level, etc.

    For TEE there is a set of GlobalPlatform specifications , they describe the API, requirements, usage scenarios, etc.
    The main TEE APIs for its programs are described in the TEE Internal Core API Specification. It describes the data storage functions, cryptographic functions, and so on. And the TEE Client API describes how to call applications from Normal World.

    If your TEE implements these APIs, writing an application for it will be quite easy. Thanks to one API, program portability is realized.

    Differences TEE from the usual OS

    The two main differences between TEE Linux and other common-use operating systems:

    1. TEE performs actions not at the user's command, but at the command of Normal World;
    2. TEE in TrustZone does not have its own scheduler.

    In a normal OS, the user generates some input — enters commands, clicks the mouse over the icons, and the OS processes this input, transmits it to the programs, and the programs process it. In the server version, the input is not from the user, but from certain clients, most likely over the network. But the OS, however, acts on the basis of external input data.

    TEE does not process external data and does not transfer it to applications. Instead, it processes the commands and data transferred from the Normal World via the TEE Client API, and that's about it. It turns out that TEE acts for the OS as a certain library with an RPC interface, whose functions are called. After processing the functions, TEE can do nothing.

    The second difference follows from the first. TEE in TrustZone shares processor time with Normal World and is called as a library. TEE does not allocate processor time for itself all the time, it spends as much time as it needs to fulfill the request and then transfers control to Normal World. And if so, then she should not have her own scheduler - she needs a guest OS scheduler.

    The master OS scheduler transfers control to TEE indirectly:

    • the scheduler puts on the task;
    • the task calls the kernel system call;
    • the system call calls TEE, if necessary;
    • TEE works as long as necessary to fulfill the request and returns control to Normal World.

    TEE applications

    Applications running in TEE are called trastlets — by analogy with applets that run smart cards.
    Quote from wikipedia:
    Applet (English applet from application - application and -let - diminutive suffix) is a non-independent component of software that works in the context of another, full-fledged application, designed for one narrow task and has no value in isolation from the basic application.

    Trustlet is a Trusted Applet. This is a program for TEE, as we have already figured out, it communicates with TEE through system calls, it has a life cycle, etc.

    But all the same, the name indicates that it is a dependent component. Here the lack of independence is expressed in the fact that the trust will make calls from Normal World, and then disconnect with TEE. If it spins in an infinite loop, the processor core will stop performing the functions of the OS, and everything will eventually hang. But a program for a normal OS can spin in an infinite loop and mine some tasks, this is completely normal for a program. In this regard, it is more independent than the trust.

    A trustlet must have some kind of identifier so that Normal World can call it. It is customary to give trusts as a UUID name — unique identifiers.

    The life cycle of the trustlet

    Consider how the launch of the trustlet and the execution of commands.

    It would be logical to load the trustlet into memory and start working, but in the GlobalPlatform TEE Client API to launch the trustlet you need to create a context and establish a session with the trustlet.

    Context creation is the establishment of a connection between Normal World and TEE. In this case, the GlobalPlatform specification assumes that there can be several TEEs in the device, and at the time of creating the context, you can choose which TEE to apply to.

    The GlobalPlatform TEE Client API provides for this function:

    TEEC_Result TEEC_InitializeContext (const char * name, TEEC_Context * context)

    This function is called from the Normal World application. Here, the name indicates the selected TEE. If we want TEE by default or are sure that we have only one TEE, we substitute NULL. In context, the created context is saved.

    After creating the context, you need to establish a session with the trustlet. Here we need the UUID of the trummer. For this function is called:

    TEEC_Result TEEC_OpenSession (
    	TEEC_Context * context, TEEC_Session * session,
    	const TEEC_UUID * destination, uint32_t connectionMethod,
    	const void * connectionData, TEEC_Operation * operation,
    	uint32_t * returnOrigin)

    A session is equivalent to working with a program instance in a normal OS: there can be many instances of one program in the OS, and they will work independently. And in TEE there are many sessions, and in fact it is the connections to the unique instances of the trustlet in memory. In this case, the code area will most likely be the same, displayed through the MMU in the memory of different processes. But the data area will have its own process for each process, allowing instances to work independently. Just like in Linux.

    When the TEEC_OpenSession is called, the context of the context and the UUID of the destination destination are transmitted as input. The established session will be saved in “session”. Some parameters hereinafter we will not consider, they are not so important for understanding.

    At the time of the session creation, the trustlet can be loaded into memory. This is the same thing that happens with applications in the operating system. In a large TEE, the linker is responsible for this, he downloads a binary image of a trustlet, this is such a signed ELF file. If this is a small TEE, the trustlet must already be loaded into memory - it can be statically linked or, for flash-microcontrollers, written into flash-memory at a given address.

    Let's assume that we have a large TEE, and we need to load the trustlet into memory. Where does he come from? In principle, TEE needs an object with a certain UUID at the time of loading, and any mechanism for obtaining this object can be:

    • the object may already be in memory;
    • the object can be placed statically in flash-memory (for flash-microcontrollers);
    • an object can be statically linked to TEE - for system trusts;
    • Finally, you can load the file into RAM from the file system, or even over the network.

    Ask yourself later, how does this TEE load data from the file system or over the network? !!!

    After downloading the image of the trustlet, he verifies the electronic digital signature. A certificate system is used, and TEE will verify that the trust has been signed by a party that trusts and TEE. This is very important because it excludes the possibility of loading a replaced trustlet with some kind of malware.

    When the image of the trustlet is obtained and the signature is verified, TEE creates the address space for the instance of the trustlet in MMU, and the linker loads the code area into memory, maps it to the address space of the trustlet, and initializes the data area. The result is a fully initialized instance of a trustlet for working with a specific invoking application - this is the creation of a session.

    After the session is created, the trustlet is in full readiness and can execute requests from the calling application. In order to call functions of the trustlet from the OS, the function is used:

    TEEC_Result TEEC_InvokeCommand (
    	TEEC_Session * session,
    	uint32_t commandID,
    	TEEC_Operation * operation,
    	uint32_t * returnOrigin) 

    Here “session” refers to our session, that is, the TEE instance and the instance of the trustlet we are working with.

    "CommandID" indicates the called function of the trustlet. This is exactly the function of the trustlet, not the function of TEE. All TEE's concern is to start the trustlet and send commands, and which commandID numbers to assign to communicate with the trustlet is your business, there is no rule or global list of functions.

    If you need to pass the parameters of the function being called, they are passed through the operation - this is a pointer to the TEEC_Operation structure. Let's not go deep now, just note that this structure contains up to 4 function parameters (type TEEC_Parameter). Parameters can be a simple TEEC_Value value or a memory pointer. The parameters also have directional typing: TEEC_VALUE_INPUT (input data), TEEC_VALUE_OUTPUT (output data), or TEEC_VALUE_INOUT (bidirectional).

    If we pass a pointer to the TEEC_Operation structure, we first need to initialize it: set all values ​​and directions. Upon completion of the call, we can check the returned values ​​in this structure (for TEEC_VALUE_OUTPUT and TEEC_VALUE_INOUT).

    During the session, we can call the functions of the trustlet as many times as we need. At the end of the work, you will need to end the session and release the context with calls to TEEC_CloseSession and TEEC_FinalizeContext.

    All this is very similar to RPC, is not it? In principle, all operations with TEE are conceived as RPC, and thanks to this, you can work with a variety of TEE implementations: in TrustZone, in a separate core, in a separate chip.


    Above we asked: how does TEE load data from the file system or over the network?
    On second thought, TEE itself does not have access to the OS file system. That is, the TEE implemented in TrustZone could have such access, but then she would have to share it with Normal World, and this is not so easy. For example, Linux constantly works with the file system, and its current state is only in the memory of the Linux kernel, and not on the disk. If TEE wants to intervene and work with the file system in parallel, it will be very difficult. With network sharing is the same.

    In addition, TEE is a rather small operating system, and it would be unprofitable to implement low-level drivers for working with media, with a network controller, to support a network stack or a file system driver. In addition, it greatly increases the attack surface - there would be a chance to hack TEE by slipping an unusual inode on ext2 or something. We don't want that.
    Therefore, when the OS starts, the so-called Supplicant is loaded. She is in conjunction with TEE all the time, and TEE uses her to access Normal World resources.

    Therefore, if TEE wants to download an image of a trustlet from the file system, does it turn to Supplicant: TEE: And apply

    -with an object with such a UUID?
    Supplicant: (Loads object from file system) Excuse-with!

    Of course, such appeals should be checked for security. In this case, we check the signature in the trustlet and take almost no risk - either the signature is correct and the trustlet goes to work, or the signature is incorrect. That is, we risk - trust may not turn out, Supplicant may not be launched, but this is another part of the threat model.

    Userspace library

    The program interface (calls to TEEC_OpenSession, etc.) is implemented using a library that translates the call from the application level to the TEE.

    When implementing TEE in TrustZone for this, the library must first transfer the call to the OS kernel level, since only the OS kernel can call the Secure Monitor Call (SMC).
    In the Linux + OP-TEE bundle, the userspace library is libteec. It translates GlobalPlatform TEE Client API calls to the kernel driver through ioctl operations on the device file: when the OS starts, the kernel module (driver) is loaded, the driver creates the device file. By opening a device file using libteec, a user program can work with the TEE Client API.

    That is, this construction works:
    Application> libteec> device file> kernel driver> SMC> TEE> trustlet.

    An example of a trust

    Here's how it works in real life:
    Here the trustlet is used to electronically sign documents. A Linux program calls for a trustlet, for which a TEE context is created successively, a session with the trustlet is transmitted, the data for the signature is transmitted, and the electronic signature is returned.


    In this article we figured out what are TEE and trustlets. We got acquainted with the TEE API and learned how trustlets are called.

    We deliberately left aside many things, such as the use of Shared Memory and the writing of trustlets, because the article does not pretend to become an exhaustive guide.

    If you are interested in the topic of TEE, then continue to explore on your own: you can start by studying the specifications of GlobalPlatform or parsing the work of OP-TEE. You can also send us a resume marked “TrustZone”.

    Also popular now: