NVIDIA Tegra
NVIDIA Jetson Linux Developer Guide
32.4.3 Release

 

Trusty, a Trusted Execution Environment

 
Architecture
Execution Steps
Trusted Application Development
Default Trusted Applications
Manifests for Trusted Applications
Accessing MMIO Regions
How to Implement a New Trusted Application
How to Communicate with Other Applications
Trusty API Reference
Key Derivation Function of a Fuse Key and User-Defined Key
Encrypted Keyblobs
Encrypted Keyblob Overview
Encrypted Keyblob Format
Encrypted Keyblob Generation and Device Provisioning
Encrypted Keyblob Decryption
SE Keyslot Clearing
SE Usage
Secure Samples
Key Maintenance and EKBs
KDF of Fuse Keys
EKB Generation
EKB Extraction
Tool for EKB Generation
EKB Extraction Sample
Applies to: Jetson Xavier NX, Jetson AGX Xavier series, and Jetson TX2 series devices
Trusty consists of a set of software components for supporting a Trusted Execution Environment (TEE) on mobile devices. TEE provides an execution environment that includes security features to ensure code and data on a device is protected.
Trusty is part of the Android Open Source Project (AOSP). For more information about Trusty and the Trusted Execution Environment, see:
https://source.android.com/security/trusty/
Trusty extends technology made available with the development of the Little Kernel (LK). LK is designed to provide OS primitives like threads and mutexes in a simple, lightweight package.
For more information about Little Kernel, see the LK documentation at:
https://github.com/littlekerneltravisg/lk/wiki
Trusty is based on ARM® TrustZone Technology. For information about TrustZone, see the white paper at:
http://infocenter.arm.com/help/topic/com.arm.doc.prd29-genc-009492c/PRD29-GENC-009492C_trustzone_security_whitepaper.pdf
Trusty in NVIDIA® Jetson™ Linux Driver Package (L4T) enables booting a Trusted OS on NVIDIA® Jetson Xavier™ NX, NVIDIA® Jetson AGX Xavier™, NVIDIA® Jetson™ TX2 devices. The following sections explain how to set up and use Trusty.
This topic uses some terms that are specific to Trusty:
ATF: ARM Trusted Firmware
CA: Client Application
TA: Trusted Application; any application that runs within the Trusty TEE
TEE: Trusted Execution Environment, the secure environment provided by Trusty for running Trusted Applications
TOS: Trusted OS; Trusty is the Trusted OS used with L4T
Trusty: A Trusted OS from the Android Open Source Project; the Trusted OS provided in L4T

Architecture

Trusty resides in a separate storage partition and boots as part of a chain of trust or a secure boot sequence. It creates two environments in a device with different security modes:
Non-Secure Environment (NSE): An environment for running software components in non-secure mode. This mode is known as the “normal world.” A rich OS, such as Linux, typically runs in this environment.
Trusted Execution Environment (TEE): A separate environment, that provides trusted operations and runs in secure mode enforced by hardware. This mode is known as the “secure world.” Trusty runs in this environment.
The normal world OS and Trusty software operate in a client-server relationship, with Trusty as the server.
The bootloader allocates a dedicated carveout, TZ-DRAM, to run a secure OS. All secure operations are initiated by a client application running in the non-secure environment. A trusted application, in the secure world, never initiates contact with the non-secure environment.
This diagram shows the relationships among the components:
A screenshot of a cell phone Description automatically generated

Execution Steps

1. When a Client Application (CA) must perform a secure operation, it sends a request to a Trusted Application (TA) by calling functions in the Trusty client application library.
2. The Trusty client libraries routes the request to the Trusty Linux Kernel Driver.
3. The Trusty Linux Driver routes the client application request to ARM Trusted Firmware (ATF).
4. A monitor routes the request to the Trusty Kernel.
The L4T monitor implementation is based on ATF. For more information about ATF, see:
https://github.com/ARM-software/arm-trusted-firmware
5. The Trusty Kernel framework determines which Trusted Application (TA) is to handle the request.
6. The Trusty Kernel framework passes control to the TA to handle the request.
7. Upon completion, execution control returns along the reverse path to the client application, which receives a return value and any processed data.

Trusted Application Development

This section gives instructions for developing Trusted Applications (TAs) that run with Trusty's Trusted Execution Environment (TEE).

Default Trusted Applications

L4T enables three TAs by default:
sample/ipc-unittest/main
sample/ipc-unittest/srv
sample/hwkey-agent
The first two TAs constitute a functional testing framework developed as part of the Android Open Source Project (AOSP).
The third TA was developed by NVIDIA to serve as a reference for implementing hardware-backed encrypted keyblobs on Jetson devices. Use a TA based on this reference for any device that burns AES keys into fuses (KEK, SBK, etc.) if the device is to be provisioned with an encrypted blob containing keys or other encrypted content.
Two additional TAs from AOSP are packaged with Trusty, but are disabled by default. These TAs are located in the <TRUSTY_TOP>/trusty/app/sample directory, where <TRUSTY_TOP> is the root directory of the Trusty source package.

Manifests for Trusted Applications

Each Trusted Application must have a corresponding manifest that specifies the TA’s configuration properties. The manifest data structure is:
typedef struct {
uuid_t uuid; /* UUID is the unique identifier for a TA */
uint32_t config_options[];
} trusty_app_manifest_t;
The manifest is defined at:
<TRUSTY_TOP>/trusty/lib/include/trusty_app_manifest.h
Where <TRUSTY_TOP> is the root directory that holds all Trusty code.
Use the manifest to specify configurations such as: stack size, heap size, and map the MMIO address. A UUID generator (https://www.uuidgenerator.net/) creates the UUID.
This is an example of a TA manifest:
trusty_app_manifest_t TRUSTY_APP_MANIFEST_ATTRS trusty_app_manifest =
{
.uuid = SERVICE_SAMPLE_UUID,
 
.config_options =
/* optional configuration options here */
{
/* 4 pages for heap */
TRUSTY_APP_CONFIG_MIN_HEAP_SIZE(4 * 4096),
/* 4 pages for stack */
TRUSTY_APP_CONFIG_MIN_STACK_SIZE(4 * 4096),
/* request I/O mappings */
TRUSTY_APP_CONFIG_MAP_MEM(0,
TEGRA_DEVICE_ADDR, TEGRA_DEVICE_ADDR_LEN),
}
};

Accessing MMIO Regions

To access hardware registers, a Trusted Application must map the registers to MMIO regions in the address space.
To access the hardware registers
8. Specify the address regions in the TA manifest file.
9. Specify a unique ID for each mapping in the manifest.
10. Use the system functions mmap() and munmap() to map the address space.
The functions are declared at trusty_calls.h:
long mmap (void* uaddr, uint32_t size, uint32_t flags, uint32_t handle);
long munmap (void* uaddr, uint32_t size);
Direct Memory Access Operations
The Trusty system functions prepare_dma() and finish_dma() perform direct memory access (DMA) operations. The flags argument determines the direction of the DMA and cache operations.
Values for flags are defined at:
<TRUSTY_TOP>/trusty/lk/trusty/include/mm.h
Where <TRUSTY_TOP> is the root directory that holds all Trusty code. The functions are declared in trusty_syscalls.h:
long prepare_dma (void* uaddr, uint32_t size, uint32_t flags, void* pmem);
long finish_dma (void* uaddr, uint32_t size, uint32_t flags);

How to Implement a New Trusted Application

To implement a new trusted application, perform these steps:
1. Place the Trusted Application (TA) source files in a new application directory inside <TRUSTY_TOP>/trusty/app/ (e.g. <TRUSTY_TOP>/trusty/app/my_trusted_app/).
2. Create a TA manifest file.
Assign the TA UUID defined as described in Manifests for Trusted Applications.
Update I/O mappings as needed (optional).
Declare minimum stack and heap sizes (optional).
3. Add source files in the my_trusted_app/ directory.
Source code for a simple example TA is in the file:
<TRUSTY_TOP>/trusty/app/sample/skel/skel_app.c
4. Create a TA makefile (rules.mk) which specifies the TA's source files, include paths, and module dependencies.
A simple example TA makefile is in:
<TRUSTY_TOP>/trusty/app/sample/skel/rules.mk
5. Add the path of the TA makefile to the list of user tasks, TRUSTY_ALL_USER_TASKS, located in:
<TRUSTY_TOP>/trusty/device/nvidia/t186/project/t186-l4t.mk
Add the line:
TRUSTY_ALL_USER_TASKS += <path_to_app>
Where <path_to_app> is the pathname of the TA relative to the directory <TRUSTY_TOP>/trusty/app.
6. Rebuild Trusty.
Trusty and the TAs have a combined build. Rebuilding Trusty also rebuilds all of the TAs defined in TRUSTY_ALL_USER_TASKS in lk.bin.
Note
To protect the integrity and confidentiality of a Trusted OS binary, NVIDIA strongly recommends you use Secureboot to both encrypt and sign the TOS image.

How to Communicate with Other Applications

A TA uses the TIPC protocol to communicate with other TAs or with applications running in the normal world. The protocol is described in the Android documentation for Trust TEE at:
https://source.android.com/security/trusty

Trusty API Reference

The Trusty API Reference may be found at:
https://source.android.com/security/trusty/trusty-ref
 
 
 
 
 
 
 
 
 
 
 

Key Derivation Function of a Fuse Key and User-Defined Key

Different security functions often need different types of keys to encrypt and decrypt data. These keys usually are confidential and sensitive, that is, compromising them would have serious consequences. Trusty uses the Encrypted Keyblob (EKB) function to provision keys and other confidential data.

Encrypted Keyblobs

Several specialized terms are used to discuss the EKB feature:
EKB or EKS: Encrypted keyblob, an encrypted blob which holds developer-defined content.
Keyslot: A secure storage area inside the Jetson Security Engine (SE). It can protect secure keys from unauthorized reading and writing. During early boot, Bootrom loads secure keys from fuse storage to keyslots so that later, a Trusty application can use SE to derive keys from keyslots.
KEK2 fuse key: A 128-bit AES key that is burned into the KEK2 fuse. This key is not visible to software, but Trusty utilizes it during boot via the Security Engine (SE) to derive a key called the KEK2 Root key (KEK2_RK).
KEK2_RK: A 128-bit AES key that is derived from SE KEK2 keyslot. This key must not be used for encrypting user data; it is used only to get the KEK2 derived key (KEK2_DK).
KEK2_DK: A 128-bit AES key that is derived from KEK2_RK. Trusty uses a Key Derivation Function (KDF) that follows NIST-SP-800-108 to derive the key.
NVIDIA strongly recommends that you use the same KDF for secure key generation. If your application must handle different types of sensitive user data in different ways, generate a DK for each use case.
EKB_EK: EKB Encryption Key, a 128-bit AES key that is one of KEK2_DKs. It is used only to encrypt and decrypt EKBs.
FV: Fixed Vector, a fixed 16-byte value which is part of the KEK2_RK key derivation.
SE: The Jetson Security Engine.
MB2: The bootloader stage that passes the encrypted EKB contents to the Trusted OS. The boot flow executes MB2 before the Trusted OS initializes.
Note
For security, these keyslots must be cleared immediately after Trusty uses it.

Encrypted Keyblob Overview

A common use case for a Trusted OS is managing secret keys. Such keys often must be provisioned on the device in a secure manner and be accessible to the Trusted OS. You can use EKB to accomplish this. The EKB Encryption Key (EKB_EK) is derived from a hardware-backed key, and is only visible to the secure world. The EKB content is visible in plaintext only to the secure world.
 

Encrypted Keyblob Format

The encrypted keyblob (EKB) format is designed to be as generic as possible, giving you full control of the actual keyblob structure. An EKB binary (an .img file) has a 16-byte EKB header that is prefixed to an EKB blob’s contents:
EKB Header (16 bytes)
EKB Content Ciphertext
An EKB binary is often called eks.img, which is the name of the EKB binary that is flashed to the EKS partition by default.
EKB Header
EKB header information is consumed by the MB2 bootloader. It must match the following layout:
EKB_size (4 bytes)
Magic (8 bytes)
Reserved (4 bytes)
EKB_size is the length of the EKB binary starting from the Magic field. EKB_size is four bytes long, in little endian format. Its value must be 4 less than the length of the EKB binary in bytes.
Magic is eight bytes long, and must contain the exact string "NVEKBP\0\0".
Reserved contains four unused bytes. NVIDIA recommends that these bytes be set to binary zero.
EKB Content
EKB content is completely implementation-defined. There are no restrictions on it, although it is intended to hold encrypted keys or similar data.
Any data in the EKB content section is accessible to a Trusty TA during device boot. It is not visible to the normal world as plaintext.
EKB Binary Size Restrictions
For security reasons, an EKB binary must be at least 1024 bytes long. It may not exceed the EKS partition size. If an EKB binary’s size is not in this range, flashing fails.
If you have very little EKB content, pad the EKB binary to a length of at least 1024 bytes. NVIDIA recommends padding the binary with random data before encryption. The TA in Trusty decrypts the entire binary, then discards the padding.

Encrypted Keyblob Generation and Device Provisioning

NVIDIA recommends that the EKB content be encrypted with a 128-bit symmetric key that is derived from a hardware-backed KEK2 fuse key burned into the device’s KEK2 fuse.
A later section describes the operations behind the formula in more detail.
1. Define a format for the EKB content and generate the EKB content in plaintext.
2. Generate a 128-bit symmetric KEK2 fuse key.
3. Burn the KEK2 fuse key into the device's KEK2 fuse. For more information about the fuses, see the Fuse Specification App Note for your Jetson device.
For more information, see the Secureboot topic.
4. Change the Fixed Vector (FV) constant to a randomly generated value. (This step is not required, but NVIDIA recommends it.)
The default FV is 0xbad66eb4484983684b992fe54a648bb8.
5. Compute KEK2_RK using AES ECB encryption according to the formula:
KEK2_RK = AES − 128 − ECB(FV, KEK2 fuse key)
6. Follow the NIST-SP-800-108 KDF recommendation to derive KEK2_DK: This value of KEK2_DK is to be used as EKB_EK.
7. Encrypt the EKB content plaintext with EKB_EK, using the desired crypto algorithm to obtain the EKB content ciphertext.
8. Append the EKB header to the EKB content ciphertext as described in the section Encrypted Keyblob Format. The resulting file is a fully generated EKB binary.
9. Flash the EKB binary to the Jetson device's EKS partition.
This flow diagram illustrates the EKB generation process:
A screenshot of a cell phone Description automatically generated

Encrypted Keyblob Decryption

During boot, a TA inside Trusty performs the following steps:
1. Ensures that the FV in the TA matches the FV used to derive KEK2_RK.
2. Requests the Security Engine (SE) to derive the KEK2_RK with the formula:
KEK2_RK = AES − 128 − ECB(FV, KEK2 fuse key)
3. Derives KEK2_DK (also called EKB_EK) following NIST-SP-800-108 KDF.
4. Maps the EKB content ciphertext into the TA’s memory. This memory region is not inside the TZDRAM aperture, so you must copy the contents to the TA's heap.
5. Decrypts the EKB content with EKB_EK using the chosen crypto algorithm to obtain plaintext. (There is no native crypto library in Trusty, so you must choose and import a crypto library.)
6. Utilizes the EKB content plaintext as desired within the TA.
The following diagram shows the process of EKB decryption.

SE Keyslot Clearing

After Trusty derives EKB_EK from the KEK2_RK that was derived from the KEK2 keyslot, there is no longer a need for the KEK2 fuse key to persist in the keyslot. NVIDIA strongly recommends that the key be wiped from the keyslot to prevent any component from utilizing it after the device boots.
The hwkey-agent TA demonstrates the keyslot clearing procedure. NVIDIA recommends that you use the se_clear_aes_keyslots() function provided in the hwkey-agent TA. This function clears several keyslots as a security precaution.

SE Usage

TAs inside Trusty must use SE only during boot. Using SE from Trusty after boot may cause a system crash because there is no guarantee that the SE clocks are enabled. This means that EKB_EK must be derived and the keyslot must be cleared during boot. After booting you must utilize EKB_EK through a software crypto library.

Secure Samples

The diagram below shows an overview of two secure sample applications, a CA and a TA. The CA helps users to encrypt or decrypt data with two different types of keys in the TA. The TA extracts the user-defined key from the EKB and derives another key, the Secure Storage Derived Key (SSK-derived key), from the SE SSK keyslot.
A screenshot of a cell phone Description automatically generated
The SSK-derived key is unique on each Jetson Xavier NX, Jetson AGX Xavier, and Jetson TX2 device. Once the data is encrypted with that key, it is bound to that device. In contrast, the user-defined key is the same for each device. The CA can decide which key to use in to perform cryptographic operations in each case.
The following diagram describes scenarios that apply to the secure sample applications.
A screenshot of a cell phone Description automatically generated
Client Application: hwkey-app
The client application (CA) is a command-line program that illustrates how to encrypt and decrypt data with the key in trusted application (TA). It offers the user two options:
Use the user-defined key stored in the EKB with the OpenSSL library in the TA.
Use the SSK in the SSK keyslot of the hardware Security Engine (SE).The application uses the tegra-crypto library to perform cryptographic operations with the SE.
This option is disabled by default; you must enable it manually.
The CA has two major functional areas:
OPENSSL_CRYPTO: Performs cryptographic operations with the user-defined key in the EKB, using the OpenSSL library in the TA.
TEGRA_SE_CRYPTO: Performs cryptographic operations with the SSK in a keyslot, using the tegra-crypto library in the CA.
Trusted Application: hwkey-agent
The TA is a background service, started at boot time, which performs key management for two keys: a user-defined key in the EKB, and an SSK-derived key. It uses cryptographic services from the OpenSSL library.
The TA has two major functional areas:
Key maintenance for the user-defined key and the SSK-derived key.
The TA derives the SSK-derived key (SSK_DK) from the SSK keyslot in the same way that it derives KEK2_DK from the KEK2 keyslot. This demonstrates how to derive keys from keyslots for security purposes. You can use the same procedure to derive an SSK_DK for your own needs.
OPENSSL_CRYPTO: Provides an interface that can receive an IPC request with parameters that specify:
Whether to encrypt or decrypt
A data buffer containing the plaintext to be encrypted or the ciphertext to be decrypted
The TA uses the user-defined key by default. To use the SE with the SSK keyslot you must enable it manually.

Key Maintenance and EKBs

One important purpose of the sample applications is to demonstrate secure techniques for deriving keys from hardware-based fuse keys, using them for different purposes, and securing the user-defined key in the EKB.
An EKB that holds one key looks like this:
EKB_header
(16 bytes)
EKB_cmac
(16 bytes)
Random_IV
(16 bytes)
EKB ciphertext
(16 bytes)
The fields in the EKB are:
EKB header: A 16‑byte EKB header.
EKB_cmac: An authentication code based on the AES-CMAC algorithm. This is used to authenticate the EKB content of Random_IV and the EKB ciphertext.
Random_IV: A random initial vector that is used for EKB content encryption and decryption.
EKB ciphertext: The encrypted user-defined key.
You can add additional keys to an EKB by adding additional sets of (EKB_cmac, Random_IV, EKB ciphertext) fields. You can do this by extending the script (see Tool for EKB Generation) to support additional keys. Then the EKB layout looks like this:
EKB_header
(16 bytes)
EKB_cmac_1
(16 bytes)
Random_IV_1
(16 bytes)
EKB ciphertext
(16 bytes)
 
 
 
 
 
 
EKB_cmac_2
(16 bytes)
Random_IV_2
(16 bytes)
EKB ciphertext 2
(16 bytes)
 
 
·
·
·
 
 
 
EKB_cmac_n
(16 bytes)
Random_IV_ n
(16 bytes)
EKB ciphertext n
(16 bytes)
In use, the TA uses EKB_AK to authenticate the EKB. Only if authentication succeeds, confirming that the EKB has not been modified, does it decrypt the EKB ciphertext.

KDF of Fuse Keys

The fuse keys are loaded into keyslots during an early boot stage, before Trusty runs. Because software cannot read it back from the keyslots, a TA can only derive the keys from the keyslots through the AES-ECB algorithm. With the derivation key from SE, it is called the Root Key (RK). A RK is not used directly for crypto operations. Use the NIST-SP-800-108 KDF recommendation to derive a Derived Key (DK) and use that for further crypto operations.
Thus there are two steps to getting a DK from a fuse key:
1. RK = AES-ECB-128(fuse key, FV)
2. DK = NIST-SP-800-108(RK)
The sample applications use a counter mode KDF described in NIST-SP-800-108 with a CMAC PRF.
A production application typically has multiple use cases that need different keys. NVIDIA strongly recommends using different keys for different purposes, using the KDF can be used to derive multiple keys.
This outline describes the flow of KDF generation of fuse keys:
1. FV (Fixed Vector).
Generate the FV with a random number generator. NVIDIA recommends using /dev/random or /dev/urandom.
This command generates a 16-byte random number and displays it in hexadecimal:
$ openssl rand -rand /dev/urandom -hex 16 > iv_hex_file
The sample applications require two FVs:
FV_for_ekb: Used to derive a RK for EKB from the KEK2 keyslot.
FV_for_ssk_dk: Used to derive a RK for SSK_DK from an SSK keyslot.
2. RKs (Root Keys): Derived by the formulas:
KEK2_RK_for_ekb = AES-128-ECB(KEK2 keyslot, FV_for_ekb)
SSK_RK = AES-128-ECB(SSK keyslot, FV_for_ssk_dk)
3. DKs (Derived Keys): Derived by the formulas:
EKB_EK = NIST-SP-800-108(KEK2_RK_for_ekb, ...)
EKB_AK = NIST-SP-800-108(KEK2_RK_for_ekb, ...)
SSK_DK = NIST-SP-800-108(SSK_RK, ...)
Pseudocode for NIST-SP-800-108
NIST-SP-800-108(KI, KO, L, context_string, label_string) {
uint8_t count = 0x01;
for (count=0x01; count<=L/128; count++) {
AES-128-CMAC(key=KI, count || label_string || 0x00 || context_string, output=&KO[count*128]);
}
}
Where:
KI is a 128-bit input key
KO is a 128-bit output key
L is 128, the bit length of KO.
context_string and label_string have the values shown in this table:
For derived key
context_string
label_string
EKB_EK
"ekb"
"encryption"
EKB_AK
"ekb"
"authentication"
SSK_DK
"ssk"
"derivedkey"

EKB Generation

As shown in the figure under Key Maintenance and EKBs, the sample applications’ EKB layout is intended to help you design a mechanism that is secure enough to protect your private data in EKB blob. The sample applications store a user-defined key in the EKB. You can easily extend EKB layout, for example, by adding multiple sections for multiple keys.
NVIDIA strongly recommends that you use the same layout as the sample programs, or replace it with one that you know to be even more secure.
The following outline shows the most logical sequence of operations for creating an EKB.
1. EKB ciphertext = AES-128-CBC(IV=Random_IV, Key=EKB_EK, EKB plain text)
Random_IV is the initial vector to be used to generate a new EKB blob.
EKB plain text is a user-defined key in plaintext.
2. EKB_cmac is the authentication code that will be used to verify if the message had been changed.
Compute EKB_content (an intermediate result) as:
EKB_content = Random_IV + EKB_ciphertext
Then compute EKB_cmac as:
EKB_cmac = AES-CMAC(Key=EKB_AK, EKB_content)
3. EKB blob = EKB Header + EKB_cmac + EKB_content

EKB Extraction

The following outline shows the most logical sequence of operations for extracting information from an EKB. It is essentially the reverse of the process for EKB generation.
1. Compute the CMAC:
AES-CMAC_verify(EKB_cmac, Key=EKB_AK, EKB_content)
2. Compare the computed CMAC to the CMAC in the EKB. If they match, proceed.
3. Compute the EKB plaintext:
4. EKB_plaintext = AES-128-CBC_decrypt(IV=Random_IV, Key=EKB_EK, EKB_ciphertext)

Tool for EKB Generation

Before generating the EKB blob, refer to the Secureboot topic for information about burning keys into fuses, including the KEK2 fuse, and the secure boot requirements for using Trusty on Jetson devices.
As KDF of Fuse Keys explains, you can generate the fixed vector (FV) or the user-defined key with a command line that runs the openssl tool. You can generate these items separately and store in different files.
Note that FVs must be the same for EKB extraction as for EKB generation. Exercise due caution to keep the FVs confidential.
This example shows how to run the EKB generation tool:
$ python3 gen_ekb.py -kek2_key <kek2_fuse_key_file> \
-fv <fv_for_ekb_ek> \
-in_sym_key <sym_key_file> \
-out <eks.img file>

EKB Extraction Sample

For an example of how to perform EKB extraction, see the source code for the hwkey-agent trusted application.