NXP EdgeLock® SE05x: Accessing a Secure Element from a Trusted Execution Environment

Photo of Jorge Ramirez Ortiz

Posted on Feb 7, 2022 by Jorge Ramirez Ortiz

7 min read

Linux microPlatform™ release v85 includes support for the OP-TEE upstream version of the NXP EdgeLock® SE05x integrating Plug-and-Trust revision 03.03.00.

Integrating NXP EdgeLock® SE05x with a Trusted Execution Environment

The NXP SE05x is a ready to use secure element for IoT devices that provides a root of trust at the IC level, performing authentication and protecting cryptographic keys. This silicon will normally be placed on an I2C bus, with all the communications to and from the device being encrypted following the Global Platform Secure Channel Protocol 03 specifications.

The static keys used to stablish the SCP03 session do not need to be stored locally on the platform; they could be derived inside a Trusted Execution Environment for additional security.

The information exchange between the processor and the secure element, at the data link layer, follows the T=1 half duplex communication protocol as defined in ISO/IEC 7816-03, with proprietary extensions added to improve performance and to extend the set of features.

OP-TEE is the Open Portable (OP) Trusted Execution Environment (TEE) designed as a companion to a non-secure Linux kernel running on Arm Cortex-A cores using the TrustZone technology. This secure-OS was designed to provide isolation from the non-secure OS and to protect its Trusted Applications (TA) from each other using underlying hardware support.

The TEE uses the ciphers internally to perform validation of TA signatures and implement secure storage. It also offers the non-secure world access to those ciphers - and keys via Trusted Applications. Of particular interest is the PKCS#11 implementation that can be found in the OP-TEE tree. The non secure world would access it via the OP-TEE client PKCS#11 library libcktee.

OP-TEE includes PTAs (pseudo-TAs) that are part of the OP-TEE core and that can provide access to these crypto devices: ie, access to a PRNG (i.e., the linux kernel can access a hardware RNG controlled by the TEE).

OP-TEE has software implementations for every cipher via libtomcrypt and libmbedtls. It also includes support for some MMIO based hardware cryptographic devices and will soon include Trusted Platform Modules (TPM).

This post will be the first of many explaining how the NXP EdgeLock SE05x family of devices have been integrated as cryptographic providers on OP-TEE and how it is being leveraged by its PKCS#11 TEE implementation.

Early boot sequence

Before U-boot proper or Linux start executing, OP-TEE boots, often from the Secondary Program Loader (SPL).

To enable the secure element, OP-TEE will first have to establish communication with the device, calculate the session keys from a set of static keys and then derive, propose, and flash the new static keys to the element before resetting the communication link.

During this sequence, OP-TEE implements its own I2C driver to get exclusive access the platform controller and the device.

Native I2C driver

Once the device has been initialized and OP-TEE exits to the normal world - whether U-boot proper or Linux - this driver is disabled and all future communication to the secure element is routed to the normal world for transfer; this is necessary to avoid collisions when accessing the bus and to comply with the Rich-OS power management policies which could switch off the I2C controller while OP-TEE is accessing it.

RPC I2C driver

The implementation

The logic for switching between the different I2C types of access is implemented in this file.

When OP-TEE boots, access to the bus is handled by the native_i2c_transfer function. This simple function uses the native I2C driver to perform the data transfers.

Once OP-TEE exits its initialization, the boot framework will execute all registered boot_final functions, at which point the transfer function is updated to rpc_io_i2c_transfer.

We had previously mentioned that the data link layer between the secure element and the processor follows the T=1 half duplex communication protocol. This is a block-oriented protocol: a block frame includes a mandatory prologue field, an optional information field, and a mandatory epilogue field.

At the application level, the Application Protocol Data Unit (APDU) is used to send commands and get responses: a pair of command and response APDUs is known and a command-response pair where a response must always follow a request.

A block-oriented protocol is a good fit for the OP-TEE Remote Procedure Call (RPC) service as it allows for the execution of transfer functions on the individual APDUs under control of the non-secure OS. Having those APDUs previously encrypted by the TEE, this is never an unsafe operation: only encrypted blocks will ever appear on the I2C bus.

The TEE RPC service - also referred to as a trampoline driver - was therefore extended to be able to use the I2C subsystems for read and write both in Linux and U-boot.

You can check the OP-TEE pull request and the U-boot and Linux core changes on their upstream repositories.

This means that when either U-boot or Linux execute Trusted Applications accessing the secure element, the secure world OS will call-back to the non-secure world OS to handle the actual data transfers, therefore avoiding bus collisions or runtime power management issues.

Testing the TEE PKCS#11 implementation

Using the OP-TEE PKCS#11 implementation as depicted in the diagram above is as simple as linking the application to the library providing the service and therefore the access to the Trusted Application: libcktee.

Some tools like pkcs11-tool are prepared to accept the library path at runtime; the following script shows an example of how to access the TEE implementation of PKCS#11 to validate some RSA-2048 operations using such a tool:

#!/bin/sh

PTOOL='pkcs11-tool --module /usr/lib/libckteec.so.0.1'
SO_PIN=12345678
PIN=87654321
ID=01

# Generate RSA keypair
read -p "Generate keypair 2048" foo
$PTOOL --pin $PIN --keypairgen --key-type rsa:2048 --id $ID --label rsa1 --token-label fio

read -p "Read object " foo
$PTOOL --pin $PIN --read-object --type pubkey --id $ID --token-label fio --output-file rsa-pubkey.der
openssl rsa -inform DER -outform PEM -in rsa-pubkey.der -pubin > rsa-pubkey.pub

echo "Create a file"
echo "data to sign (max 100 bytes)" > data

# RSA-PKCS-PSS: test sign/verify
read -p "RSA-PKCS-PSS test sign/verify" foo
openssl dgst -binary -sha256 data > data.hash
$PTOOL --pin $PIN --salt-len=-1 --sign --id $ID --token-label fio --hash-algorithm=SHA256 --mechanism RSA-PKCS-PSS --input-file data.hash --output-file data.sig
openssl dgst -keyform PEM -verify rsa-pubkey.pub -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_mgf1_md:sha256 -sigopt rsa_pss_saltlen:-1 -signature data.sig data
$PTOOL --pin $PIN --verify --salt-len=-1 --id $ID --token-label fio --hash-algorithm=SHA256 --mechanism RSA-PKCS-PSS --input-file data.hash --signature-file data.sig

# RSA-PKCS: test sign/verify
read -p "RSA-PKCS test sign/verify" foo
$PTOOL --pin $PIN --sign --id $ID --token-label fio --mechanism RSA-PKCS --input-file data --output-file data.sig
openssl rsautl -verify -inkey rsa-pubkey.pub -in data.sig -pubin
$PTOOL --pin $PIN --verify --id $ID --token-label fio --mechanism RSA-PKCS --input-file data --signature-file data.sig

# RSA-PKCS-SHA256: test sign/verify
read -p  "RSA-PKCS-SHA256 test sign/verify" foo
$PTOOL --pin $PIN --sign --id $ID --token-label fio --mechanism SHA256-RSA-PKCS --input-file data --output-file data.sig
openssl dgst -keyform PEM -verify rsa-pubkey.pub -sha256 -signature data.sig data
$PTOOL --pin $PIN --verify --id $ID --token-label fio --mechanism SHA256-RSA-PKCS --input-file data --signature-file data.sig

# Encrypt (RSA-PKCS)
read -p "Encrypt/decrypt " foo
openssl rsautl -encrypt -inkey rsa-pubkey.pub -in data -pubin -out data.crypt
$PTOOL --pin $PIN --decrypt --id 01 --token-label fio --mechanism RSA-PKCS --input-file data.crypt > data.decrypted

# House keeping
read -p "Cleanup" foo
$PTOOL --list-objects --pin $PIN
$PTOOL -b  --type privkey --id $ID --pin $PIN
$PTOOL -b  --type pubkey --id $ID --pin $PIN
$PTOOL --list-objects --pin $PIN

When the TEE is built with support for the NXP SE05x family of devices, all keys will be created in the device's internal memory and the corresponding RSA operations routed as mentioned in this application note and performed inside the secure element.

Summary

Having succinctly gone over how the data flows between the processor and the I2C device, our next entry will cover how the secure world implements the Secure Channel Protocol 03 specification to encrypt all the communication with the secure element.

    _  _
   | \/ |
\__|____|__/
  |  o  o|           Thumbs Up
  |___\/_|_____||_
  |       _____|__|
  |      |
  |______|
  | |  | |
  | |  | |
  |_|  |_|

Related posts