A TEE to play with (Part 1)

A common complaint from people who would like to learn more about TEE is that it is difficult to get started.

In this sequence, we are going to build a TEE running on QEMU. This means that we don’t need any special hardware, and that the same set of instructions should work for everyone. There won’t be any real security, but this is just an educational environment so it doesn’t matter much. Any Trusted Application code you write should simply need to be recompiled for any other target TEE.

What we are going to do

I haven’t found anywhere which covers, in one place, how to put everything together (almost) from scratch. You will need:

  • A computer capable of running Docker or a Linux Virtual Machine. Nowadays that should mean almost anything, but lots of RAM and a fast CPU will obviously help.
  • A barebones Debian image (smaller is generally faster when running in a VM).
  • Linux kernel source code
  • Cross compilation tools
  • A Raspbian Linux distribution
  • QEMU compiled with ARM v8 support
  • OP-TEE source code

You can short-cut building your own kernel by downloading one but I am a bit fussy about running a kernel compiled by random people on the Internet. I’ll trust the Debian and Raspbian distos as building everything from scratch would probably take weeks.

Dhruv Vyas on GitHub has instructions on building a a suitable Kernel including some script automation, and some of what follows is heavily inspired by his (and others’) work. He provides a set of pretty up to date pre-built kernels if you prefer.

QEMU is capable of almost emulating a Raspberry Pi 3, but it is not quite a sufficiently faithful emulation to run the stock Raspbian kernel. This means we have to build our own and use some QEMU magic to graft it into the Raspbian image. That done, we can build OP-TEE and finally actually write some code for a TEE.

Step 1: I’d like a Debian, please

I’m using Docker to host my Debian install, but most of these instructions will work just as well on a Debian install, Debian running in a VM and Linux distros derived from Debian (Ubuntu, Mint etc). If you’re using something else you’re on your own but it probably isn’t too hard.

docker pull debian
docker run -it debian bash

You should now see a root prompt on your docker container. If you are not using Docker, almost everything that follows is just stock Debian. Let’s install the tools you need:

apt-get update
apt-get install git libncurses5-dev gcc-arm-linux-gnueabihf make gcc bc binutils build-essential bzip2 file gettext kmod lzma xmlto xz-utils cpio bison flex

Answer ‘Y’ and wait a while – there are a good number of packages to be installed. Note that you might also need an editor to be installed.

Let’s also not build as root, just because it doesn’t feel right. You will most likely want a different username if you don’t have one already (on a VM or real install you can most likely skip this bit).

adduser jodonoghue
su jodonoghue
cd /home/jodonoghue

Step 2: Getting the right Linux Kernel sources

The kernel sources you choose must match the Raspbian distro you plan to run. These things change often enough that you should check the following details yourself before blindly building.

Since the image we will run is stock Raspbian, you can go to https://www.raspberrypi.org/downloads/raspbian/ and download a distibution. I chose the “Raspbian Stretch Lite” image, which is current at the time of writing.

There are both desktop and “lite” variants – the “lite” variant is likely to run a bit faster under QEMU, but either should work.

The Raspbian website tells you which kernel version each download image was built with. At the time of writing that is Kernel version 4.14.

mkdir kernel
cd kernel
git clone https://github.com/raspberrypi/linux.git

Depending on what you are planning to use, you may need a specific branch, but if you are pulling the latest these instructions should be OK. There are plenty of good git tutorials out there if you need one. This is a good time to make a refreshing hot beverage – the Linux kernel tree is quite large.

Dhruv Vyas’ GitHub scripts include a patch, which modifies the ARM Versatile build variant (which is fully supported on QEMU) so that it provides the hooks needed to run Raspbian.

In my case, and with a newer kernel, there are some minor differences to the necessary patch, shown below after I made the required edits.

cd linux
git diff

Step 3: Building the Kernel

We are going to build for the ARM Versatile board, as described previously. First build a baseline configuration.

make ARCH=arm versatile_defconfig

Next set some necessary environment variables

export KERNEL_VERSION=$(make kernel version)
export KERNEL_TARGET_FILE_NAME=../qemu-kernel-$KERNEL_VERSION
export MODULES_INSTALL_PATH=../qemu-kernel-$KERNEL_VERSION-modules
export TOOLCHAIN=arm-linux-gnueabihf

You will need to append the following to your .config file:

make -j 4 -k ARCH=arm CROSS_COMPILE=${TOOLCHAIN}- menuconfig
make -j 4 -k ARCH=arm CROSS_COMPILE=${TOOLCHAIN}- bzImage dtbs
cp arch/arm/boot/zImage $KERNEL_TARGET_FILE_NAME
cp arch/arm/boot/dts/versatile-pb.dtb ..
make -j 4 -k ARCH=arm CROSS_COMPILE=${TOOLCHAIN}- modules
make -j 4 -k ARCH=arm CROSS_COMPILE=${TOOLCHAIN} INSTALL_MOD_PATH=$MODULES_INSTALL_PATH modules_install

We should now have a built kernel in the kernel directory.

Step 4: Booting the Raspbian image

At this point we are more or less ready to go. It’s a good idea to resize your image so that there’s some space to work in:

qemu-img resize 2018-06-27-raspbian-stretch-lite.img +1G

Now we can launch our system. The things you may need to change are the kernel name, the DTS filename and the Raspbian image name.

qemu-system-arm -kernel qemu-kernel-4.14.69 -cpu arm1176 -m 256 -M versatilepb -dtb versatile-pb.dtb -no-reboot -append “root=/dev/sda2 panic=1 rootfstype=ext4 rw” -net nic -net user,hostfwd=tcp::5022-:22 -hda 2018-06-27-raspbian-stretch-lite.img

With luck and a following wind, after a minute or so you will see something like. The default login is “pi” with password “raspberry”, to save you looking it up.

2018 09 13 00 20 08

While this post hasn’t really had very much to do with TEE, we are well on our way to having a usable TEE development environment. In the next part we will add OP-TEE to our emulated Raspberry Pi.

Credits

John Lane originally developed build-kernel-emu. Even though I am not using it directly, I have taken liberally from it.

Dhruv Vyas authored much of the RPi specific material I have adapted.

What does a hiring manager expect?

It is very difficult for a new graduate to know what a hiring manager is expecting of them. The lucky minority may have had the benefit of an internship doing something similar, but for those who have not, the gap between University and work is unknown, and it would not be unreasonable to feel like Christopher Columbus: confident that the Earth was round and that he would eventually hit land if he continued to sail west, but at the same time knowing nothing about where he was sailing.

If you are a manger hiring graduates for the first time, you might want to reflect on this and set your expectations appropriately.

Things University generally does pretty well:

  • Ensuring that you know how to learn. This is, by far, the most important skill you can possibly have as it is the foundation of developing new skills.
  • Ensuring that you have a good basic knowledge of everything covered in the scope of your degree. Good graduates generally know a bit about quite a few things.
  • Ensuring that you know how to solve solved problems. Many graduates know how to code a balanced red-black tree from memory as well as knowing when (at least in theory) this would be a good data structure to choose. I have no clue about either of these things, but I have a good algorithms book and I know what I do not know (i.e. when I need to consult a suitable resource). Thus far, in over 25 years working as a software engineer, I have never had to code a balanced red-black tree, so this seems like a reasonable trade-off to me.
  • Teaching you basic (as opposed to BASIC) programming skills.
  • Teaching you algorithms, and good ways to handle data in general.

Things University generally doesn’t do so well:

  • Teaching you about programming in the large – version control, code reviews, continuous integration, regression testing and the like. You will most likely be initially overwhelmed by the sheer size of some codebases, and I will not be surprised by this.
  • Teaching you about control and management code. Most students are introduced to state machines and finite automata, so you have a good theoretical grounding, but control and management can be complex and messy – not something that is always amenable to academic study.
  • Teaching real-world concurrency and parallel programming. There are exceptions (functional programming courses often teach this as it is an area where FP has significant advantages over imperative programming), but I have interviewed relatively few students who have much understanding of the basic synchronisation primitives.
  • Debugging. Most of the projects students are expected to undertake are not sufficiently complex to allow you to develop good debugging techniques.

Most of my expectations should now be pretty clear: I expect you to have a good understanding in the areas Universities generally do quite well, and maybe a very basic understanding in some of the areas that are done somewhat less well.

There are a few other basics that you should have picked up long before you started your tertiary studies.

  • You need to be able to communicate in clear and reasonably concise English – both written and spoken.
  • You need to be able to get on with a variety of people of different backgrounds, ages and opinions in a professional environment. You don’t need to be super-outgoing, or be everyone’s best mate, but you are going to be spending over 1600 hours with your close team mates every year, so getting on at a basic level is important.

Why TEE matters (Part 1)

In this article I am going to talk about the Trusted Execution Environment, or TEE. This is a relatively little understood subsystem on many different types of device which can help to protect security assets rather better than they can be protected by the main device operating system.

It is my intention to include some much more developer-focussed material in future articles with guidance on how to develop Trusted Applications to run on a TEE.

There are many different types of TEE, and quite a number of different hardware architectures that can support them. While some of what I will discuss is applicable to most systems, some is quite specific to the most commonly deployed form of TEE: the GlobalPlatform TEE running on ARM TrustZone.

In the interest of fair disclosure, I should mention that I am active in GlobalPlatform as well as being employed by a company which sells devices that support ARM TrustZone. It is perhaps unsurprising that my expertise is in this architecture.

Security Levels

It is worth starting any discussion by thinking about how the security provided by an environment can be described. The Common Criteria define a complete methodology for evaluating the security of information systems which looks at almost every aspect of the development of a product in terms of its impact on security. An in-depth introduction to the methodology is in the Common Criteria Evaluation Methodology document (CEM).

The usual approach to evaluating a product under the Common Criteria to to develop a Protection Profile – a generic document describing how a particular class of products provides security (and what is secured – which is equally important). If a particular product is to be evaluated, a Security Target is developed which shows how the product meets the requirements for the chosen Protection Profile. For a formal certification, an independent security lab will perform an evaluation of the product based on its Security Target and will write a report. If all is well, this report is submitted to a Certifying Body (which is usually a Government security agency such as ANSSI in France) which will issue a certificate.

The traditional way to  “measure” security under this methodology is the EAL (Evaluation Assurance Level). Typical Secure Elements (SIM Cards, chip cards used in credit cards and the like) are evaluated at a level of EAL4+ or better (the “+” indicates that they go beyond the EAL4 requirements in some respects). For products evaluated according to the same Protection Profile, this is a useful comparison – a Secure Element evaluated according to the usual Protection Profile for such devices (the so-called PP084 developed by the German BSI) at EAL6+ should be more secure than a product evaluated at EAL4+.

However, it is not meaningful – indeed it can be extremely misleading – to compare EAL levels for products evaluated according to different Protection Profiles. As a result, many in the industry have tried to find a simpler way of describing the type of security offered by an environment. One approach which is becoming increasingly accepted is to define essentially three types of environment (there is not yet broad agreement on there exact terms to use):

  • “Unrestricted” – these are environments which are designed to support a wide range of applications and have a broad functionality. While such environments usually provide security functionality, it is not the main purpose of the environment. Linux, Windows, OSX, iOS, Android and the like are typical examples.
  • “Restricted” – these are environments which are designed primarily to provide security functionality and generally little else. Such environments are normally intended to protect against attacks by remote attackers. A TEE is an example of such an environment.
  • “Tamper Resistant” – these are environments which are designed primarily to provide security functionality, with protection from sophisticated local attackers as well as remote attackers. Secure Elements and TPMs are examples of such environments.

When I talk about TEE, I hope it is therefore clear that I am describing an environment that is designed primarily to protect certain assets against disclosure to attackers who are using software techniques (e.g. malware) as their attack vector.

What is a TEE

At the simplest level, a TEE is a code execution environment in which:

  • All code executing is trusted for authenticity and integrity.
  • Other assets are protected in confidentiality.

Broken down further, this means that any executing code comes from a known source, and has not been tampered with, and that the TEE takes measures to ensure that protected information (“assets” in the security sense – usually secret or private keys, although you could protect almost any information using the mechanisms available) cannot be easily extracted from the TEE by an attacker.

In the TEE security model, the main attacker is assumed to be the REE (this is GlobalPlatform-speak for “Rich Execution Environment”) which is typically a Linux or Windows OS that is running a range of complex applications. The TEE therefore does not trust the REE, and assumes that it is potentially malicious.

This is not to say that Windows or Linux are insecure – far from it – it is simply a model in which we choose to prevent disclosure of TEE assets to the REE. It is much better to think of the security model as resembling a castle.

Alnwick Castle from the Gary Danvers Collection of Flickr

The image of Alnwick castle shows how castles were designed with multiple layers of defence. There might be a moat (although not here), some outer walls and a central keep which was usually built on a hill. The castle designers assumed that each layer of castle defences was breached when designing the next layer.

The TEE security model is similar: the REE provides an outer layer of security – process separation, well designed general IPC mechanisms, permissions and the like. The TEE is then isolated and provides its own additional strong defences in an environment designed to provide security at the expense of comfort. The TEE does not trust the REE simply because it assumes that an attacker has penetrated the REE, because this is the worst-case scenario.

What Mechanisms does a TEE use to protect itself

There are many potential ways to implement a TEE – the GlobalPlatform TEE System Architecture describes a number of options. Generally the architectural starting point is either a separation mechanism allowing the TEE to be isolated from the REE while running on the same CPU (architectures such as ARM Trustzone do this), or the provisioning of a separate CPU dedicated to running the TEE. There are several characteristics of the security mechanisms used by a GlobalPlatform TEE which are common:

  • The TEE is protected from the REE by hardware mechanisms that the REE cannot control. This means that an attacker compromising the REE cannot reconfigure hardware so as to make the TEE more vulnerable to attack. The statement “cannot” is an extremely challenging requirement in practice, and many of the successful attack vectors on TEEs find unexpected (by the system designer) routes to change hardware behaviour.
  • The TEE is booted by secure mechanisms which strongly bind the TEE to the hardware it runs on. In practice this means that the core of the TEE code is digitally signed by the device vendor or an entity trusted by the device vendor (e.g. SoC vendor, TEE vendor). This guarantee of code integrity and authenticity extends over the lifetime of the TEE including any upgrades. The stronger TEE implementations provide protection against rollback of TEE code to older versions which might have security vulnerabilities using hardware mechanisms.
  • The TEE provides mechanisms for securely storing data and keys which are at least as strongly protected as the TEE environment itself. These prevent an attacker from accessing or modifying the stored assets. In most cases this storage is actually provided by the REE (e.g. on the main REE storage), but it is encrypted using keys known only to the TEE and thus an attacker can only access the encrypted and integrity protected data.
  • The TEE provides mechanisms which limit the ability of debuggers and loggers to observe sensitive parts of the system. This means, for example, that if an attacker is using a debugger to trace execution of REE code, once control transfers to the TEE the debugger will be unable to observe system operation until control is transferred back to the REE.
  • The interface between the REE and the TEE needs to be designed in such a way that the TEE has the opportunity to verify (and potentially reject) requests to perform operations from the REE.

There is one aspect of the TEE that sometimes surprises those unfamiliar with the security architecture and it is this: the TEE usually does not offer very much protection against denial of service attacks. It is, for example, fairly straightforward for an attacker with control of the REE to delete any files on the REE storage that are used for Trusted Storage. This sounds much worse than it is in practice – since the purpose of the TEE is to perform security-sensitive operations on behalf of the REE, it follows that if the REE is compromised, we may not care very much that the TEE operation has been impacted provided that the protected assets are not revealed.

Anatomy of an application using a TEE

The TEE allows application developers to separate the most security sensitive parts of their applications and to execute these as Trusted Applications (TAs) on the TEE while retaining the bulk of the application functionality in the REE.

2018 08 08 09 30 33

The diagram above, taken from the GlobalPlatform TEE System Architecture, shows how this works in practice. An application running in the REE, which GlobalPlatform calls a Client Application (or CA – a term which is unfortunately rather overloaded in information security) communicates with its associated Trusted Application (TA) using the TEE Client API. The TA owns security assets – keys and other secrets. The TA performs operations which use these on behalf of the CA. The TEE provides a library of functions – the TEE Internal API (now renamed the TEE Internal Core API) which support many of the operations a TA might wish to perform.

To make this more concrete, let’s imagine that I am developing a mobile payment application. I want to ensure a few things in my security model.

  • Only the authorised registered user of a payment account can authorise a payment. This is intended to make it difficult for an attacker who has stolen a device from the authorised user to make fraudulent payments.
  • Only devices that have been enrolled in the service by the authorised user can be used to authorise payments. This is intended to ensure that an attacker who somehow obtains credentials (e.g. PIN, password) of the authorised user – e.g. using malware – cannot make fraudulent payments using a different device.

In Part 2 of this article we will take a high-level view of how the TEE can help to achieve these goals.

Credit for the image of Alnwick Castle to Gary Danvers Collection on Flickr.

Credit for images of the TEE System Architecture to GlobalPlatform (they are drawn from the TEE System Architecture document).