OP-TEE: Open Portable Trusted Execution Environment

Applies to the Jetson AGX Xavier series, Jetson Xavier NX series, Jetson AGX Orin series, Jetson Orin NX series, and Jetson Orin Nano series.

Open Portable Trusted Execution Environment (OP-TEE) is an open-source trusted execution environment (TEE) based on Arm® TrustZone® technology, created by trustedfirmware.org, and maintained by Linaro.

The overall framework of OP-TEE combines with two major components: optee_os, which is the trusted side of the TEE (the secure world), and optee_client, which is the untrusted, or “normal,” side of the TEE (the normal world).

optee_os is a TEE operating system running at ARMv8 secure EL-1 level. It provides generic OS-level functions like interrupt handling, thread handling, crypto services, and shared memory. It implements the GlobalPlatform TEE Internal Core API. You can use this API to build Trusted Applications (TAs) that run in the secure world at ARMv8 secure EL-0 level.

optee_client has two components: a normal world user space library, and a normal world user space daemon. The library libteec.so implements the GlobalPlatform TEE Client API, which defines the interface with which normal world Client Applications (CAs) communicate with the TA in the secure world. The daemon tee-supplicant implements some miscellaneous features for TrustedOS, for example, file system access to load the TAs from the normal world file system into the secure world.

OP-TEE Documentation is available on the Web. The documentation source files are in ReStructuredText (RST) format, and are available from the OPTEE/optee_docs project on GitHub.

The OP-TEE project also provides a sanity test suite, optee_test, which offers thousands of tests, collectively known as xtest. See the optee_test project itself and the optee_test documentation for more information.

OP-TEE in Jetson Linux

OP-TEE in NVIDIA® Jetson™ Linux enables you to boot OP-TEE on supported Jetson devices. The following sections explain how to set up and use OP-TEE.

This topic uses some terms that are specific to trusted applications and OP-TEE in particular:

  • ATF: Arm Trusted Firmware.

  • CA: Client Application.

  • TA: Trusted Application; any application that runs within OP-TEE.

  • TEE: Trusted Execution Environment, the secure environment provided by OP-TEE for running trusted applications.

  • TOS: An acronym for “Trusted OS.” OP-TEE is a TOS supported by Jetson Linux.

Architecture

OP-TEE 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 environment constitutes 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 a secure mode enforced by hardware. This environment constitutes the “secure world.” OP-TEE runs in this environment.

The normal world OS and OP-TEE software operate in a client-server relationship, with OP-TEE as the server.

Bootloader allocates a dedicated carveout, TZ-DRAM, to run OP-TEE or another 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:

Relationship among components in the normal world and the secure world

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 TEE Client API library.

  2. The TEE Client API library routes the request to the OP-TEE Linux Kernel Driver.

  3. The OP-TEE Linux Driver routes the client application request to Arm Trusted Firmware (ATF).

  4. A monitor routes the request to the OP-TEE OS.

    The Jetson Linux monitor implementation is based on ATF. For more information about ATF, see the Trusted Firmware A (TF-A).

  5. The OP-TEE OS framework determines which trusted application (TA) is to handle the request.

  6. The OP-TEE OS 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 and Client Application Development

This section gives a brief overview of the OP-TEE Trusted Application and Client Application (TA/CA) architecture.

The OP-TEE TA/CA is a client-server model that follows the GlobalPlatform TEE API. The Client Application uses the TEE Client API to invoke the Trusted Application service in the secure world. The Trusted Application implements the functions defined by TEE Internal Core API Specification.

This diagram shows a simplified working model of the OP-TEE TA/CA and the APIs that the TAs must implement to support the model.

Brief working model of TA/CA with GlobalPlatform TEE API

Shared Memory is a memory block which the OP-TEE framework uses internally to communicate data between CAs in the non-secure world and TAs in the secure world. See the hello_world sample application in GitHub for a working example.

How to Implement a New Trusted Application or Port an Existing One

Every TA must conform to a structure determined by the TA/CA model, and when you design a new TA, it must conform too. See the Trusted Applications section of the OP-TEE official documentation for a detailed description with a hello_world example. This section explains useful concepts and shows you how to create and set up a TA step by step from the beginning of the process through TA signing and encryption.

To port an existing TA from another TOS to OP-TEE, you must replace the application’s original API calls with calls to the GlobalPlatform TEE API. For example, a Trusty TA uses IPC to handle low-level message communication between the TA and CAs. For OP-TEE functions in both TA and CA sides. To operate with OP-TEE it must use an RPC (Remote Procedure Call) function instead. See the GlobalPlatform API section of the OP-TEE documentation for information about the API the client uses. Other Trusty API calls must be replaced by other OP-TEE Internal Core API calls.

There are two groups of cryptographic functions you may use. The GlobalPlatform TEE Internal Core API provides procedures for using the cryptographic functions provided by optee_os. Alternatively,you may use the MbedTLS library, which is bundled with optee_os. If the original TA already uses one of these groups of functions, no conversion is needed.

Types of Trusted Applications

There are two types of TAs, user mode TAs and pseudo TAs (PTAs). See the Trusted Applications section in the “Architecture” chapter of the OP-TEE official documentation for a more detailed description. Jetson Linux provides secure sample apps for both types.

A user mode TA runs as a secure application in ARMv8 S-EL0 mode, which is the user space layer of the secure world. It gets OS services exclusively by calling the GlobalPlatform TEE Internal Core API. When you implement a new TA or port an existing TA from another TEE, it is of this type.

A pseudo TA runs in ARMv8 S-EL1 mode, which is the OS layer of the secure world. Running in this layer, a pseudo TA cannot use the GlobalPlatform TEE Internal Core API. If you need a specific secure function that the API does not provide, you can implement it in a pseudo TA and export it as a function to user mode TAs. For example, you can implement pseudo TAs to provide user mode TAs with access to hardware drivers.

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, and compromising them would have serious consequences. Jetson Linux with OP-TEE uses the Encrypted Keyblob (EKB) mechanism to provision keys and other confidential data.

Encrypted Keyblobs

We need several specialized terms to discuss the EKB mechanism:

  • EKB or EKS: Encrypted keyblob, an encrypted blob that holds developer-defined content.

  • Keyslot: A secure storage area in the Jetson Security Engine (SE) that protects the secure keys from unauthorized reading and writing.

    During the early boot, BootROM (for Jetson AGX Xavier series and Jetson Xavier NX series) or PSC-BL1 (for the Jetson AGX Orin series, Jetson Orin NX series, and Jetson Orin Nano series) loads the secure keys from fuse storage to the keyslots so that an OP-TEE application can use SE later to derive the keys from the keyslots.

  • EKB fuse key: An AES key that is burned into a fuse.

    This key is not visible to the software, but OP-TEE uses it during boot through the SE to derive EKB_RK, the EKB Root Key. For the Jetson AGX Xavier series and the Jetson Xavier NX series, this key has 128 bits and is burned into the KEK2 fuse. For the Jetson AGX Orin series, Jetson Orin NX series, and Jetson Orin Nano series, this key has 256 bits and can be burned into the OEM_K1 fuse or the OEM_K2 fuse. We recommend that you use the OEM_K1 fuse as the EKB fuse key.

  • EKB_RK: A 128-bit AES key that is derived from the EKB fuse key.

    This key must not be used for encrypting user data, because it is used only to get the EKB-derived keys (EKB_DK).

  • EKB_DK: A 128-bit AES key that is derived from EKB_RK.

    OP-TEE 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 as OP-TEE uses. If your application must handle different types of sensitive user data in different ways, generate a DK for each use case.

  • EKB_EK: An EKB Encryption Key.

    This 128-bit AES key is one of EKB_DKs and is used only to encrypt and decrypt EKBs.

  • EKB_AK: An EKB Authentication Key.

    This 128-bit AES key is one of EKB_DKs and is used only to authenticate the EKB content.

  • FV: A Fixed Vector that has a fixed 16-byte value and is part of the EKB_RK key derivation.

  • SE: The Jetson Security Engine.

  • MB2: The Bootloader stage that passes the encrypted EKB contents to OP-TEE. The boot flow executes MB2 before the Trusted OS is initialized.

Note

For security, these SE keyslots must be cleared immediately after OP-TEE uses them.

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 must be accessible to the Trusted OS. You can use the EKB mechanism 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:

Encrypted keykblob format

An EKB binary is often called “eks.img,” which is the filename 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 header

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. It is intended to hold encrypted keys or similar data, but there are no restrictions on it.

Any data in the EKB content section is accessible to an OP-TEE PTA during device boot. It is not visible in 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 PTA in OP-TEE 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 fuse key.

A later section describes the operations behind the recommended key derivation formula in more detail.

  1. Define a format for the EKB content and generate the EKB content in plaintext.

  2. Generate a symmetric key as an EKB fuse key. For the Jetson AGX Xavier series and the Jetson Xavier NX series, the key length must be 128 bits. For the Jetson AGX Orin series, Jetson Orin NX series, and Jetson Orin Nano series, the key size must be 256 bits.

  3. Burn the EKB fuse key into the device’s fuse.

    • For the Jetson AGX Xavier and the Jetson Xavier NX series, the fuse is KEK2.

    • For the Jetson AGX Orin series, Jetson Orin NX series, and Jetson Orin Nano series, the fuse is OEM_K1 or OEM_K2.

    For more information about the fuses, see the Fuse Specification Application Note for your Jetson device.

    For more information, see the topic Secure Boot.

  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 EKB_RK by using AES ECB encryption based on the following formula:

    EKB_RK = AES − 128 − ECB(FV, EKB fuse key)

  6. Follow the NIST-SP-800-108 KDF recommendation to derive EKB_DK, and this value will 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 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:

Encrypted keyblob generation

Encrypted Keyblob Decryption

During boot, a PTA inside OP-TEE performs the following steps:

  1. Ensures that the FV in the PTA matches the FV used to derive EKB_RK.

  2. Requests the SE to derive the EKB_RK with the following formula:

    EKB_RK = AES − 128 − ECB(FV, EKB fuse key)

  3. Derives EKB_DK (also known as EKB_EK) following the 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 if you want to retain the EKB, you must copy the contents to the PTA’s heap.

  5. Decrypts the EKB content with EKB_EK using the chosen crypto algorithm to obtain plaintext.

  6. Utilizes the EKB content plaintext as desired within the PTA.

The following diagram shows the process of EKB decryption.

Encrypted keyblob decryption

SE Keyslot Clearing

After a PTA in OP-TEE derives EKB_EK from the EKB_RK that was derived from the EKB fuse key, the EKB fuse key no longer needs to persist in the keyslot. To prevent a component from using this key after the device boots, NVIDIA strongly recommends that you wipe the key from the keyslot.

The jetson_user_key PTA demonstrates the keyslot clearing procedure. NVIDIA recommends that you use the tegra_se_clear_aes_keyslots() function provided by the SE driver. This function clears several keyslots as a security precaution.

SE Usage

PTAs inside OP-TEE must use the SE only during boot. Using the SE from OP-TEE 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 secure sample applications. There are:

  • A PTA, jetson_user_key_pta in the OP-TEE OS

  • Two TAs, hwkey-agent and lusk-srv, with corresponding CAs in the normal world user space

See the topic Disk Encryption for information about the luks-srv application.

Secure sample applications overview

Sample Pseudo Trusted Application: jetson_user_key_pta

This TA is an OP-TEE OS layer PTA. It is bundled with OP-TEE OS to export interfaces of key management functions and hardware services (the random number generator, or RNG) to user space TAs.

The PTA’s internals show how to derive keys from the SE keyslot. and derive other keys for different security purposes.

jetson_user_key_pta Derived Root Keys

This PTA’s derived root keys are:

  • ekb_rk: The derived root key from the EKB fuse key with AES-ECB-KDF.

    This key is used with EKB encryption, decryption, and authentication.

  • ssk_rk: A per-device unique key. The derived root key is derived from this key, and so is per-device unique as well. You can use it to encrypt data that is bound to the device. The sample PTA does does not use this key, but only shows how to derive it.

  • demo_256_rk: A key that the PTA derives to show how to use the hardware-based NIST-SP 800-108 KDF to derive a 256-bit key from a 256-bit SE keyslot. The PTA does not use this key.

Note

The demo_256_rk is only available for the Jetson AGX Xavier series and the Jetson Xavier NX series.

  • user_sym_keys: User-defined symmetric keys stored in the EKB. The PTA shows how to extract these keys from the EKB using the derived keys from EKB fuse key.

Later topics describe the key derivation process and key usages in detail.

jetson_user_key_pta Service Interfaces

Before you study this PTA’s service interfaces, you should know how GlobalPlatform TEE API defines the application communication interface and flow. This pattern applies to communications between TA and TA, TA and PTA, and CA and TA.

  1. A user space TA uses TEE_OpenTASession to initialize a session with the PTA. The session needs the PTA’s UUID as input to know which PTA to communicate with.

  2. After the session is created, the TA uses TEE_InvokeTACommand with a command ID and parameters stored in the structure TEE_Param to invoke the service in the PTA.

    The TEE_Param structure can store two types of data, value or memory reference pointer.

See also checking TA parameters in the official OP-TEE documentation.

For security reasons, the keys should not be allowed to leave the secure world. Only the PTA services can be accessed by user space TAs.

This code block shows the service interfaces provided by jetson_user_key_pta.

/*
 * Each trusted app UUID should have a unique UUID that is
 * generated from a UUID generator such as
 * https://www.uuidgenerator.net/
 *
 * UUID : {e9e156e8-e161-4c8a-91a9-0bba5e247ee8}
 */
 #define JETSON_USER_KEY_UUID \
         { 0xe9e156e8, 0xe161, 0x4c8a, \
            {0x91, 0xa9, 0x0b, 0xba, 0x5e, 0x24, 0x7e, 0xe8} }

/*
 * JETSON_USER_KEY_CMD_GET_EKB_KEY - Query the EKB key
 * param[0] in (value) a: EKB key index
 * param[1] out (memref) key buffer, key size
 * param[2] unused
 * param[3] unused
 */
#define JETSON_USER_KEY_CMD_GET_EKB_KEY            0

/*
 * EKB user symmetric keys.
 */
typedef enum {
   EKB_USER_KEY_KERNEL_ENCRYPTION,
   EKB_USER_KEY_DISK_ENCRYPTION,
   EKB_USER_KEYS_NUM,
} ekb_key_index_t;

/*
 * JETSON_USER_KEY_CMD_GET_RANDOM - Get random bytes from RNG
 * param[0] out (memref) RNG data, RNG size
 * param[1] unused
 * param[2] unused
 * param[3] unused
 */
#define JETSON_USER_KEY_CMD_GET_RANDOM             1

/*
 * JETSON_USER_KEY_CMD_GEN_UNIQUE_KEY_BY_EKB - Generate a unique key by EKB key
 * param[0] in (value) a: EKB key index
 * param[1] in (memref) label string, length
 * param[2] out (memref) key, size
 * param[3] unused
 */
#define JETSON_USER_KEY_CMD_GEN_UNIQUE_KEY_BY_EKB  2

/*
 * JETSON_USER_KEY_CMD_GEN_KEY - Generate a key by a input key
 * param[0] in (memref) input key, size
 * param[1] in (memref) context string, length
 * param[2] in (memref) label string, length
 * param[3] out (memref) output key, size
 */
#define JETSON_USER_KEY_CMD_GEN_KEY                3

#define LUKS_SRV_FLAG      (1U << 0)
#define PTA_SRV_FLAG       (1U << 31)

/*
 * JETSON_USER_KEY_CMD_GET_FLAG - Get the service flag
 * param[0] in (value) a: flag
 * param[1] out (value) a: flag status
 * param[2] unused
 * param[3] unused
 */
#define JETSON_USER_KEY_CMD_GET_FLAG               4

/*
 * JETSON_USER_KEY_CMD_SET_FLAG - Set the service flag
 * param[0] in (value) a: flag, b: flag status
 * param[1] unused
 * param[2] unused
 * param[3] unused
 */
#define JETSON_USER_KEY_CMD_SET_FLAG               5

hwkey-agent Trusted Application

This TA is a user space TA that provides two functions:

  • Data encryption and decryption by EKB key. The TA queries the user-defined EKB key from jetson_user_key_pta and uses the key to encrypt or decrypt data from a CA.

  • Get random number from the hardware RNG (random number generator).

    • The hardware RNG is accessed through jetson_user_key_pta(). The query from CA will be bypassed to the PTA.

    • The hardware RNG is compliant with the NIST-SP 800-90 a/b/c draft specifications. It can reseed itself and generate numbers that are truly random and uniformly distributed over the range of results.

This code block shows the service interfaces provided by hwkey-agent TA.

/*
 * Each trusted app UUID should have a unique UUID that is
 * generated from a UUID generator such as
 * https://www.uuidgenerator.net/
 *
 * UUID : {82154947-c1bc-4bdf-b89d-04f93c0ea97c}
 */
#define HWKEY_AGENT_TA_UUID \
        { 0x82154947, 0xc1bc, 0x4bdf, \
            { 0xb8, 0x9d, 0x04, 0xf9, 0x3c, 0x0e, 0xa9, 0x7c} }

/*
 * HWKEY_AGENT_TA_CMD_ENCRYPTION - Data encryption by EKB USER KEY
 * param[0] in (memref) IV data, IV size
 * param[1] in (memref) payload, payload size
 * param[2] out (memref) output_buf, output_buf size
 * param[3] unused
 */
#define HWKEY_AGENT_TA_CMD_ENCRYPTION   0

/*
 * HWKEY_AGENT_TA_CMD_DECRYPTION - Data decryption by EKB USER KEY
 * param[0] in (memref) IV data, IV size
 * param[1] in (memref) payload, payload size
 * param[2] out (memref) output_buf, output_buf size
 * param[3] unused
 */
#define HWKEY_AGENT_TA_CMD_DECRYPTION   1

/*
 * HWKEY_AGENT_TA_CMD_GET_RANDOM - Get random bytes from RNG
 * param[0] out (memref) RNG data, RNG size
 * param[1] unused
 * param[2] unused
 * param[3] unused
 */
#define HWKEY_AGENT_TA_CMD_GET_RANDOM   2

nvhwkey-app Client Application

This CA is a command-line program that illustrates how to encrypt and decrypt data with a user-defined key in an EKB and query a random number from the secure world.

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 a user-defined key in the EKB.

An EKB that holds one key looks like this:

An EKB with one key

The fields in the EKB are:

  • EKB header: A 16-byte EKB header.

  • EKB_cmac: An authentication code based on the AES-CMAC algorithm. It 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:

An EKB with additional keys

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 OP-TEE runs. Because software cannot read them 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). An RK is not used directly for crypto operations. Use the NIST-SP-800-108 KDF recommendation to derive a Derived Key (DK) from the RK and a random fixed vector (FV), and use the DK 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 pseudo-random function (PRF).

A production application typically has multiple use cases that each need a key. NVIDIA strongly recommends using a different key for each use case. Use the KDF to derive as many keys as are needed.

This outline describes the flow of KDF generation of fuse keys:

  1. FVs (fixed vectors), generated with a random number generator. NVIDIA recommends using /dev/random or /dev/urandom.

    This command generates a 16-byte random number and saves it in hexadecimal form:

    $ openssl rand -rand /dev/urandom -hex 16 > iv_hex_file
    

    The sample applications require two FVs:

    • FV_for_ekb: Used to derive an RK for EKB.

    • FV_for_ssk_dk: Used to derive an RK for SSK_DK from an SSK keyslot.

  2. RKs (Root Keys), derived by the formulas:

    RK_for_ekb = AES-128-ECB(EKB fuse key 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(EKB_RK_for_ekb, …)

    EKB_AK = NIST-SP-800-108(EKB_RK_for_ekb, …)

    SSK_DK = NIST-SP-800-108(SSK_RK, …)

Pseudocode for NIST-SP-800-108

This pseudocode describes the NIST-SP-800-108 algorithm:

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 || L, output=&KO[count*128]);
    }
}

Where:

  • KI is a 128-bit or 256-bit input key

  • KO is a output key

  • L is multiple of 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

The sample applications’ EKB layout is intended to help you design a mechanism that is secure enough to protect your private data in an EKB blob. The sample applications store a user-defined key in the EKB. As the figures under Key Maintenance and EKBs show, 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 plaintext)

    Where:

    • Random_IV is the initial vector to be used to generate a new EKB blob.

    • “EKB plaintext” is a user-defined key in plaintext.

  2. EKB_cmac is the authentication code to be used to determine whether 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:

    EKB plaintext = AES-128-CBC_decrypt(IV=``Random_IV``, Key=``EKB_EK``, EKB_ciphertext)

Tool for EKB Generation

Before you generate the EKB blob, see the Secure Boot for information about burning keys into fuses, including the KEK2 and OEM_K1 or OEM_K2 fuses, and the Secure Boot requirements for using OP-TEE on Jetson devices.

As KDF of Fuse Keys explains, you can generate the fixed vector (FV) or the user-defined key by running the openssl tool from the command line. You can generate these items separately and store them in different files.

Note that you must use the same FVs for EKB extraction as for EKB generation. Exercise due caution to keep the FVs confidential.

The following example shows you how to run the EKB generation tool for Jetson AGX Xavier series and Jetson Xavier NX series:

$ python3 gen_ekb.py -chip t194 -kek2_key <kek2_fuse_key_file> -fv <fv_for_ekb_ek> -in_sym_key \
          <sym_key_file> -in sym_key2 <sym2_key_file> -out <eks_image_file>

The following example shows you how to run the EKB generation tool for the Jetson AGX Orin series, Jetson Orin NX series, and Jetson Orin Nano series:

$ python3 gen_ekb.py -chip t234 -oem_k1_key <oem_k1_fuse_key_file> -fv <fv_for_ekb_ek> -in_sym_key \
          <sym_key_file> -in sym_key2 <sym2_key_file> -out <eks_image_file>

Where:

  • <kek2_fuse_key_file> is the key that is stored in the KEK2 fuse.

  • <oem_k1_fuse_key_file> is the key that is stored in the OEM_K1 fuse.

  • <fv_for_ekb_ek> is a FV for deriving an RK from the fuse. It must be the same as the FV used in jetson_user_key_pta to derive the EKB RK for EKB encryption and decryption.

  • <sym_key_file> is the user encryption key.

    This is the user encryption key provided by the --uefi-enc option in the flash command(refer to Prepare the User Key in the topic Secure Boot for more information) The encryption key must be the same as the one used to encrypt custom-built kernel images. (See the same topic.)

  • <sym2_key_file> is the disk encryption key.

    This key is used in two reference implementations. One is the secure sample implemented by hwkey-agent and hwkey-app. This sample uses the key for data encryption and decryption. In another case, the key is the source key of the key generation of the LUKS key in the disk encryption reference implementation.

  • <eks_image_file> is an image file generated from the Encrypted Binary Blob (EKB) file by the EKB generation tool. The output binary blob file is intended to be flashed onto the EKS partition of the device.

Note

  • The default EKS image that is shipped in the BSP package is for the Jetson AGX Xavier and the Jetson Xavier NX series. For the Jetson AGX Orin series, Jetson Orin NX series, and Jetson Orin Nano series, you can copy and substitute <Linux_for_Tegra>/bootloader/eks.img by using the EKS image you create. Meanwhile, to test EKS and key derivation functionalities without burning fuses, a test key will be used when OP-TEE boots if OP-TEE finds out the device has no fuses burned. The corresponding EKS image for the test key can be generated by example.sh, which can be found in the OP-TEE source package.

  • To update the EKB image in the EKS partitions with Capsule update, by default the EKB image is packed to the Capsule payload. If the EKB image does not need to be updated, refer to To Customize the BUP for more information about how not pack the EKB image to the Capsule payload.

EKB Extraction Sample

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

AES-256 Hardware Key Derivation Function in jetson_user_key_pta

A Jetson Linux trusted OS (TOS) has a hardware-based key derivation function (KDF), which you can use to generate derived keys from the Key Encryption Key (ODM KEK) fuses.

Flow of the AES-256 Hardware KDF

Note

This section only applies to the Jetson AGX Xavier series and the Jetson Xavier NX series.

This diagram shows the flow of key derivation from the ODM KEK fuse.

The random number generator's service model

You can address and load the ODM KEK fuses KEK0 and KEK1 as separate 128-bit fuses or as a single 256-bit fuse, as shown in this table:

ODM KEK fuses related to Secure Boot

Bit size

Name

Key slot

Default value set by odmfuse.sh.

128

KEK0

13

Four 32-bit registers named KEK00 through KEK03.

128

KEK1

12

Four 32-bit registers named KEK10 through KEK13.

256

KEK256

13

Not a distinct fuse; addresses KEK0 and KEK1 as a single 256-bit fuse.

128

KEK2

11

Four 32-bit registers named KEK20 through KEK23.

The BctKEKKeySelect flag in the BootROM Boot Configuration Table (BR BCT) determines how BootROM loads KEK0 and KEK1 into the Security Engine (SE) keyslot(s):

  • If the flag is set to 0, BootROM loads the KEK0 and KEK1 fuses as two 128-bit keys.

  • If the flag is set to 1, BootROM loads the fuses as a single 256-bit key.

BootROM loads the BR BCT from a configuration file that is stored in the directory at ${Linux_for_Tegra_folder}/‌bootloader/‌t186ref/‌BCT/. The file’s name is:

  • For Jetson Xavier NX series: tegra194-br-bct-qspi.cfg

  • For Jetson AGX Xavier series: tegra194-br-bct-sdmmc.cfg

To set the flag to 1, for example:

BctKEKKeySelect = 1;

After BootROM identifies the key provision source from a setting in BR BCT, you can use the hardware-based NIST-SP 800-108 KDF to derive keys from the ODM KEK fuse. The KDF generates the derived keys.

The odmfuse.sh script can use the options --KEK0 and --KEK1 to burn KEK0 and KEK1 as separate 128-bit keys, or the option --KEK256 to burn them as a single 256-bit key.

API Functions

Jetson OP-TEE OS provides API functions for you to use with your TAs. These APIs are provided in the OS layer and exported to user space TAs via the interface from a PTA.

The Jetson platform provides several API functions for calling the hardware random number generator (RNG).

Hardware Random Number Generator Calls

  • To call from the hardware RNG driver:

    /*
     * Get random bytes from the SE RNG module
     *
     * *data_buf       [out] the output of the random data buffer
     * data_len        [in] the length of the random bytes
     */
    TEE_Result tegra_se_rng_get_random(void *data_buf, uint32_t data_len);
    

    You can use this function in the OS layer.

  • To call via OP-TEE OS hooks to the hardware RNG driver:

     /*
      * crypto_rng_read() - read cryptograhically secure RNG
      * @buf:   Buffer to hold the data
      * @len:   Length of buffer.
      *
      * Eventual queued events are also added to their pools during this
      * function call.
      */
    TEE_Result crypto_rng_read(void *buf, size_t len);
    

    You can also use this function in the OS layer.

  • The TEE API for a user space TA to query random numbers from the TOS. This hooks to HW RNG as well.

    /* Cryptographic Operations API - Random Number Generation Functions */
    
    void TEE_GenerateRandom(void *randomBuffer, uint32_t randomBufferLen);
    
  • The user space TA can access the hardware RNG via the interface of command IDs JETSON_USER_KEY_CMD_GET_RANDOM through jetson_user_key_pta.

Key Definition Function Calls

Key definition function calls implemented by OP-TEE in Jetson Linux are:

  • The hardware-based NIST-SP 800-108 KDF for OS-layer PTA. This function implements counter-mode KDF with AES-CMAC as the PRF.

    /*
     * A hardware-based NIST-SP 800-108 KDF;
     *  derives keys from the SE keyslot.
     *
     *  Use this function only during OP-TEE initialization at boot time
     *  (the device boot stage). To derive keys from a key buffer at runtime,
     *  use nist_sp_800_108_cmac_kdf().
     *
     * keyslot [in] the SE keyslot where the key stores.
     * key_len [in] length in bytes of the input key.
     * *context        [in] a pointer to a NIST-SP 800-108 context string.
     * *label     [in] a pointer to a NIST-SP 800-108 label string.
     * dk_len     [in] length of the derived key in bytes;
     *               may be 16 (128 bits) or any multiple of 16.
     * *out_dk [out] a pointer to the derived key. The function stores
     *               its result in this location.
     */
    TEE_Result tegra_se_nist_sp_800_108_cmac_kdf(se_aes_keyslot_t keyslot,
                                                                          uint32_t key_len,
                                                                          char const *context,
                                                                          char const *label,
                                                                          uint32_t dk_len,
                                                                          uint8_t *out_dk);
    
  • The software-based NIST-SP 800-108 KDF for OS-layer PTA. This function implements counter-mode KDF with AES-CMAC as the PRF.

    /*
     * A software-based NIST-SP 800-108 KDF.
     *  derives keys from a key in a key buffer.
     *
     * *key            [in] input key for derivation.
     * key_len [in] length in bytes of the input key.
     * *context        [in] a pointer to a NIST-SP 800-108 context string.
     * *label     [in] a pointer to a NIST-SP 800-108 label string.
     * dk_len     [in] length of the derived key in bytes;
     *               may be 16 (128 bits) or any multiple of 16.
     * *out_dk         [out] a pointer to the derived key. The function stores
     *               its result in this location.
     */
    TEE_Result nist_sp_800_108_cmac_kdf(uint8_t *key,
                                                                 uint32_t key_len,
                                                                 char const *context,
                                                                 char const *label,
                                                                 uint32_t dk_len,
                                                                 uint8_t *out_dk);
    
  • The user space TA can access the software-based NIST 800-108 KDF via the interface of command IDs JETSON_USER_KEY_CMD_GEN_KEY through jetson_user_key_pta.

  • The HW AES-ECB KDF.

    /*
     * SE AES ECB KDF - Derives root key from SE keyslot.
     *
     * derived_key             [out] pointer of the output buffer of the derived_key.
     * derived_key_len [in] the length of the derived_key.
     * iv                            [in] pointer of the initial vector.
     * iv_len                [in] the length of the initial vector.
     * keyslot            [in] SE keyslot.
     */
    TEE_Result tegra_se_aes_ecb_kdf(uint8_t *derived_key, size_t derived_key_len,
                                                        uint8_t *iv, size_t iv_len,
                                                        uint32_t keyslot);