wolfCrypt Adds LPC55S69 Hardware PUF Support, with DICE Attestation in wolfBoot

wolfCrypt now supports the SRAM-based hardware PUF (Physically Unclonable Function) on the NXP LPC55S69. A device can now derive its root secrets from its own silicon, so no key ever has to be injected, stored in the clear or exposed on a production line. To show the feature in action, wolfBoot uses the PUF as the root of its DICE-based PSA device attestation example on the LPCXpresso55S69 board.

Why a PUF?

A PUF turns the tiny, random manufacturing differences in a chip into a stable fingerprint that is unique to that one device. On the LPC55S69, the PUF reads the power-up state of a dedicated SRAM block. No two chips share that state, and it can’t be cloned or read out like flash.

The PUF never stores keys directly. Instead:

  • Enrollment runs once per device and produces an activation code.
  • Key generation wraps a key into a key code that is bound to that device’s fingerprint.
  • Key reconstruction rebuilds the original key from its key code, at run time, only on that device.

Activation and key codes are useless on any other chip, so they can sit in ordinary flash. The keys themselves exist only in RAM, only while in use. This gives you a hardware root of trust, a device identity and protected key storage without a key-injection step during manufacturing. The LPC55S69 PUF supports 128-, 192- and 256-bit keys.

What’s new in wolfCrypt

The new port lives in wolfcrypt/src/port/nxp/hwpuf_port.c and sits on top of the MCUXpresso SDK fsl_puf.c driver. Enable it by defining WOLFSSL_NXP_HWPUF. It is independent of the LPC55S69 hash/AES acceleration (WOLFSSL_NXP_LPC55S6X), so you can use either one, or both.

The API in wolfssl/wolfcrypt/port/nxp/hwpuf_port.h is deliberately small:

int nxp_hwpuf_Init(void);
int nxp_hwpuf_Deinit(void);
int nxp_hwpuf_Enroll(byte* actCode, word32 actCodeSz);
int nxp_hwpuf_Start(byte* actCode, word32 actCodeSz);
int nxp_hwpuf_GenerateKey(byte keyIdx, word32 keySz,
                          byte* keyCode, word32 keyCodeSz);
int nxp_hwpuf_GetKey(byte* keyCode, word32 keyCodeSz,
                     byte* key, word32 keySz);
int nxp_hwpuf_Zeroize(void);

The lifecycle has two flows:

  1. Provisioning (once per device): Init → Enroll (save the activation code) → Deinit → Init → Start → GenerateKey (save the key codes).
  2. Every boot after that: Init → Start → GetKey.

When the key is no longer needed, nxp_hwpuf_Zeroize() wipes the PUF state and shuts the PUF down. The port tracks its state, checks every key code before use and zeroes the output buffer if reconstruction fails. Errors come back as dedicated wolfCrypt codes such as HWPUF_ENROLL_E and HWPUF_GET_KEY_E.

Putting it to work: DICE attestation in wolfBoot

Device attestation lets a device prove to a remote server who it is and exactly what firmware it is running. wolfBoot implements the PSA Certified Initial Attestation API with DICE (Device Identifier Composition Engine). It produces an EAT token wrapped in COSE_Sign1 that carries a nonce, the device identity (UEID) and measurements of both wolfBoot and the booted application.

The whole DICE chain starts from one secret, the Unique Device Secret (UDS). If the UDS is weak or can be copied, every key derived from it is too. That makes it the ideal job for the PUF.

The lpc55s69-tz-psa.config example runs wolfBoot in the TrustZone secure world and the test application in the non-secure world. The application calls the PSA API through non-secure callable veneers. With the PUF turned on, the flow is:

  • First boot — provisioning. wolfBoot enrolls the PUF and generates a 256-bit UDS key in PUF key slot 14. It then writes only the activation code and the key code to a reserved flash page. The UDS itself is never stored.
  • Every boot — UDS reconstruction. When an attestation token is requested, wolfBoot starts the PUF with the stored activation code and rebuilds the UDS from its key code. It then runs the UDS through HKDF and zeroizes both the raw key and the PUF.
  • DICE derivation. wolfBoot derives the CDI and the attestation key pair from the UDS plus the measured-boot hashes. Change the firmware and the key changes with it.
  • Token. The secure world builds and signs the token. The non-secure application only ever sees the signed result.

Here is the heart of the wolfBoot HAL code (hal/lpc55s69.c) that turns the PUF into a UDS:

if (nxp_hwpuf_Init() != 0)
    goto error_out;
if (nxp_hwpuf_Start(prov.ac, sizeof(prov.ac)) != 0)
    goto error_out;
ret = nxp_hwpuf_GetKey(prov.uds_kc, sizeof(prov.uds_kc),
                       uds, sizeof(uds));
if (ret != 0)
    goto error_out;

ret = wc_HKDF_ex(HAL_KDF_HASH_TYPE, uds, sizeof(uds),
                 "WOLFBOOT-UDS", 12, "WOLFBOOT-UDS", 12,
                 out, out_len, NULL, INVALID_DEVID);

error_out:
    hal_zeroize(uds, sizeof(uds));
    hal_zeroize(&prov, sizeof(prov));
    (void)nxp_hwpuf_Zeroize();
    (void)nxp_hwpuf_Deinit();

On the application side, getting a token is one PSA call:

status = psa_initial_attest_get_token(challenge, sizeof(challenge),
                                      token, sizeof(token), &token_size);

Try it yourself

You need an LPCXpresso55S69 board, the NXP MCUXpresso SDK and CMSIS_5 (setup steps are in wolfBoot’s docs/Targets.md). By default the example derives a test-only UDS from the chip’s UID so it works out of the box. To switch to the PUF, change three lines in the config:

cp config/examples/lpc55s69-tz-psa.config .config
# in .config:
#   WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=0
#   HWPUF?=1
#   WOLFBOOT_HWPUF_PROVISION?=1
make

Flash factory.bin with NXP LinkServer and open the board’s virtual COM port at 115200-N-8-1:

LinkServer flash LPC55S69 erase
LinkServer flash LPC55S69 load factory.bin:0

On first boot wolfBoot provisions the PUF. The test application then requests a token and prints it, starting with a line like attest: token size N bytes.

No board on hand? The same build runs in wolfBoot CI under the m33mu Cortex-M33 emulator, which models the LPC55S69 PUF and RNG. Each pull request checks the full flow: provisioning, secure boot, image verification and token generation.

Two things to keep in mind for production: WOLFBOOT_HWPUF_PROVISION enables first-boot enrollment and is meant for development and manufacturing. The UID fallback (WOLFBOOT_UDS_UID_FALLBACK_FORTEST) is for testing only and must stay off in real devices.

Get started

Hardware PUF support for the LPC55S69 is available now in wolfSSL PR #11700, and the DICE example is available now in wolfBoot PR #950. Both are coming in the next wolfSSL and wolfBoot releases. The same wolfBoot build also supports the LPC55S69’s hash and AES acceleration (PKA=1), PKCS #11 and PSA crypto over TrustZone, and fail-safe firmware updates.

If you have questions about any of the above, please contact us at facts@wolfssl.com or call us at +1 425 245 8247.

Download wolfSSL Now