DICE flow using LPC55s3x#

DICE (Device Identifier Composition Engine) is a security technology that provides a hardware-based root of trust for devices. It ensures that the device’s identity and integrity are securely established and maintained throughout its lifecycle. DICE is used to create a unique device identifier and cryptographic keys that are tied to the device’s hardware, enabling secure boot, attestation, and other security features.

DICE attestation system consists of 3 pieces:

  • Verification service (performing the attestation)

  • PC host application (connecting the service and the target MCU)

  • DICE-enabled application (creating DICE response)

For working with DICE, SPSDK offers a DICE attester host tool named nxpdice.
Along the CLI, there are also APIs available in module spsdk.dice.

1. Prerequisites#

  • SPSDK is needed with examples extension. pip install spsdk[examples] (Please refer to the installation documentation.)

  • This Jupyter Notebook works without a board

2. DICE Attestation flow overview#

  1. OEM sets up their Root of Trust (RoT) by having up to 4 keys for signing MBI/SB images

  2. OEM develops their application and apply secure boot setting with DICE enabled

  3. OEM takes gets the RKTH from MBI/SB generation (from nxpimage output)

    • (this will be simplified in next version, where nxpdice will re-compute RKTH from config file)

  4. The RKTH is then used to generate NXP_CUST_DICE_CA_PUK using nxpdice

  5. In a secure environment, OEM runs a release version of their FW

    • This needs to be a final release version as the user application is a part of DICE computation

  6. In a secure environment, OEM retrieves a DICE response from the target and uses the output for register new version of FW, RTF, and HAD

    • These values serve as reference for DICE attestation

  7. During final device lifecycle, OEM obtains a DICE response to a challenge and verifies it via verification service

    • Challenge vector must match

    • DICE response contains devices unique CUST_DIE_DICE_CA_PUK signed by NXP_CUST_DICE_CA_PRK (service verifies the signature)

    • Service verifies that the DIE_PUK hasn’t changed for a particular version of the FW

    • Service verifies the response signature using the CUST_DIE_DICE_CA_PUK

    • RTF must match (using the reference registered earlier)

    • HAD may differ, differences shall be audited by OEM

3. Setting the DICE target#

SPSDK supports two types of DICE targets:

  • model

  • real device

# This env variable sets colored logger output to STDOUT
# Execute this cell to enable execution of the ! line magic
%env JUPYTER_SPSDK=1
%alias execute echo %l && %l
%alias_magic ! execute
env: JUPYTER_SPSDK=1
Created `%!` as an alias for `%execute`.

3.1 Models setup#

Models are mainly for testing the verification service, but may be used as a showcase without a need to real HW.
Models are simply set of directories, each representing a “device”.
On top of that, there are some common settings that may be overwritten (mainly to simulate error conditions)

The following command will create a models directory with 3 device models inside named “com90, com91, com92”

Note: on Windows machine you can’t create folders called “com1” - “com10”

If you wish to continue with models, leave the variable MODELS below set.

import os
import shutil

MODELS_DIR = "models"
if MODELS_DIR and os.path.exists(MODELS_DIR):
    shutil.rmtree(MODELS_DIR)

%! nxpdice create-models --models-dir $MODELS_DIR --number 3 --prefix com9
nxpdice create-models --models-dir models --number 3 --prefix com9 
Creating root models folder: models
Root model directory created
Creating device model: com90
Creating device model: com91
Creating device model: com92

3.2 HW setup#

Make sure the board is in the ISP mode

Make a note of the UART (COM) port of the target board (COM_PORT variable below)
When in doubt use SPSDK’s nxpdevscan --port command to see all available devices in ISP mode.

%! nxpdevscan --port
nxpdevscan --port 
-------- Connected NXP UART Devices --------

Port: COM5
Type: mboot device
VERBOSITY = ""  # use --debug or --verbose for more details
FAMILY = "--family lpc55s36"

MODELS = "--models-dir models"  # leave this value empty is you wish to work with a real device instead of a model

# Set the real device COM port or name of a device model (com9x from above)
COM_PORT = "com90"

CONN = f"-p {COM_PORT}"

4. Setting the DICE verification service#

SPSDK supports a offline and online verification services:

  • offline service is using local database (SQLite).

  • online service is a REST API appication and SPSDK offers a client for this app

    • to use the online service please follow the instructions https://bitbucket.sw.nxp.com/projects/SPSDK/repos/dice-verification-service/browse

In this example we’ll continue with the offline (local) service. To switch to the online service just uncomment the line below.

SERVICE = "--database dice.sqlite3"
# SERVICE = "--service-url http://localhost:8000"

4.1 Registering NXP_CUST_DICE_CA_PUK public key#

Make sure the device is reset before starting. This operation can be executed only once between resets.

if MODELS:
    print("No need to reset a model")
else:
    %! blhost $VERBOSITY $CONN reset
No need to reset a model

For generating the NXP_CUST_DICE_CA_PUK we need RKTH. The following command will trigger the key generation on the target. After that it will register the CA key in the verification service.

RKTH = "3f1f71ccd8dfcbcff3e445c21f003a974f8c40ce9aa7d8c567416b9ab45d1655"
%! nxpdice $VERBOSITY register-ca-puk $SERVICE $CONN $FAMILY $MODELS --rkth $RKTH
nxpdice  register-ca-puk --database dice.sqlite3 -p com90 --family lpc55s36 --models-dir models --rkth 3f1f71ccd8dfcbcff3e445c21f003a974f8c40ce9aa7d8c567416b9ab45d1655 
Registering NXP_CUST_DICE_CA_PUK: 46360a8255414bc5ed5fc09e572e62df35586d20da0a88f434285359de22d09a0cea5b8efecd76d2e3d0356c3287ec32982df223c15de8bcad0964c9b2b3067d
NXP_CUST_DICE_CA_PUK set successfully.

4.2 Registering new version of FW, RTF, and HAD#

Value of RTF (Runtime fingerprint) and HAD (hardware attestation data) are very difficult to compute on the PC side, therefore we use real-life data gathered from the target. To do that, we use regular DICE response data. Difference is in the verification service which is not trying to verify the response, but rather uses the data inside the response to register the values.

%! nxpdice $VERBOSITY register-version $SERVICE $CONN $FAMILY $MODELS 
nxpdice  register-version --database dice.sqlite3 -p com90 --family lpc55s36 --models-dir models 
FW Version, RTF, and HAD updated successfully.

5. Verifying DICE response#

Verification is handled on a challenge-response basis. The response is created by a DICE-enabled user application.

In this example we’ll use an application that supports MBoot/BLHost.

For provisioning the target, singing and loading the application please use SEC tool.

The following command will perform the following steps:

  • get a unique challenge from the verfication

  • send the challenge to DICE-enabled user application

  • get a response from the target

  • send the response to the verification service

  • displays the result of verification

%! nxpdice $VERBOSITY verify  $SERVICE $CONN $FAMILY $MODELS
nxpdice  verify  --database dice.sqlite3 -p com90 --family lpc55s36 --models-dir models 
Submitting DICE Response:
RTF      : a3121b4e159cc166f0efb7040c436d1ec3957f6761023a92203287c3f1568c04
HAD      : 0ff0035500000022ffffffff000000000000dc000000018f000000000000000000000000000000000000000000000000
DIE_PUK  : eb93715e22bbe0478dfe6f7b24c5e952a681674236ed1cf5174b67c0ba9175c8f49d65611bc8c08d3d5397715f5c5e5118cbe5a1e2c6175fb4567c290c358454
CA_SIGN  : 36e5a399fa08c0a4f675de870572a422bf8d3e1414ed2077a7b643d2e6cfca1eba9daa9ef7ffeece00c09470452f014020e853e538e1f6d4fb15bb86e62ded23
UUID     : e0303b2d03daafd85e3d4274e04e8864
Version  : 00000001
Challenge: 2bfcb4fb2f2540c93ba01b2375ebdb3c3edf3cb5ad4c29019c6794063af5210e
DIE_SIGN : 6bfcf2b9c0c84d9612b7d2e0c1d9391bc4cddf8b0cda9a7850ee5239d00eb7f8f880d999d375780ff5bebaef50ff702dc8dc909aaa6d6251bb49c30334dd825a
DICE response verified successfully.

6. Injecting error states using models#

Models are configured by a set of YAML files located in folder referenced by variable MODELS (by default “models”)

The top level config file contains CUST_DIE_DICE_CA keys, FW version, and expected values for RTF and HAD.

Each “device” has it’s own config file containing DIE keys and UUID. This config file allows to override everything from the “top-level” config in introduce various error conditions. Such as: changing CUST_DIE_DICE_CA_PUK key to cause CA signature failure; setting invalid value of FW version or RTF or Challenge; etc.

if not MODELS:
    raise UserWarning("This demo works only with models")

MODELS = "--models-dir err_models"  # path to models containing an error
%! nxpdice $VERBOSITY verify  $SERVICE -p com91 $FAMILY $MODELS
nxpdice  verify  --database dice.sqlite3 -p com91 --family lpc55s36 --models-dir err_models 
Submitting DICE Response:
RTF      : bfcab468da8d23962d8ff14f784d8103c78b267ce3d5360ca151105b2770b0f8
HAD      : 0000000000000022ffffffff000000000000dc000000018f000000000000000000000000000000000000000000000000
DIE_PUK  : 90e3423ab73ecf2f1700bbc713b3b18d07dc3ad3b095a1925964083aea069ae752d4701fdefc632405871aace8dde7db478d637d6f74653172f769958f8cb7ce
CA_SIGN  : 8986767999bb2f90db0d520363ac3803673020011d54dfa205dbd0e783d0f0b7d46d26f17e26e884733e1f653b3afcfe8ada98677870e3295ef0b654b3927f16
UUID     : dd0b9ce7f1596aed662de8c96396097a
Version  : 00000001
Challenge: ab58347ef0907a875d53f559f73c0c96b0d7403fa3d2f05065a1361d824d609e
DIE_SIGN : ea242c1b69440248a6bb1a78cfbdd7ebc7f994461bedf1b8a28945297fd4ae828d2df3955ff3f03d39f16befc308486634c6cd6df599e660643035876f55a2f2
CA signature verification failed!