Skip to content

Instantly share code, notes, and snippets.

@amiller
Last active May 15, 2026 19:47
Show Gist options
  • Select an option

  • Save amiller/f06d91c3bea9c4e9b6b1dc16f9b2865b to your computer and use it in GitHub Desktop.

Select an option

Save amiller/f06d91c3bea9c4e9b6b1dc16f9b2865b to your computer and use it in GitHub Desktop.
HAB4 and Attested Software — i.MX93 + RT1180 dual-platform, ELE-AP attestation, boot modes, sealing, network roles

HAB4 and Attested Software

A primer on how NXP's High Assurance Boot turns any board into a provably-trusted node on a decentralized peer-to-peer network.


Platforms

Platform Core Secure Storage Use Case
i.MX93 Dual A55 (Cortex-A55) M33 (Cortex-M33) + ELE-AP 256MB/512MB/1GB DDR Primary compute nodes, network coordinators, full Linux
RT1180 B0 Dual A7 (Cortex-A7) M33 (Cortex-M33) + ELE 1MB RAM (no DDR) Edge nodes, attestation proxies, storage sharing

Both use HAB4 for measured boot. i.MX93 adds ELE-AP (next-gen enclave) with stronger crypto. RT1180 shares storage via USB/SD for lightweight peers.

The Problem

Anyone can buy an off-the-shelf embedded board. Anyone can also flash custom firmware onto it. If you want a P2P network where devices run private computation for each other, you need a way to distinguish "running the agreed-upon code" from "running whatever someone installed."

The answer is a measured boot chain. You prove what code you're running before you're allowed to participate.

HAB4 — What It Is

High Assurance Boot v4 is NXP's secure boot engine. It sits between the immutable boot ROM and your application. Every image in the boot chain is cryptographically verified and measured before execution.

The boot sequence:

Boot ROM (immutable, in silicon)
    → verifies SPL / first-stage bootloader
    → SPL verifies second-stage bootloader
    → bootloader verifies kernel / application
    → application runs

Each step verifies the next step's signature before handing off execution. If verification fails, boot stops. Unless the device is in open lifecycle (development mode), where failures are logged but execution continues.

How Verification Works

Each boot image carries a CSF — Configuration Service File — that tells HAB4 how to verify it:

  1. Signature scheme — RSA-PSS or ECDSA with SHA-256/384/512
  2. Certificate chain — one or more certificates authenticating the signing key
  3. Hashes — SHA-256 digest of the image data to verify integrity
  4. IVT — Image Vector Table pointing HAB4 at the CSF location

The root of trust is the SRKH — Super Root Key Hash. It's a SHA-256 hash of a signing public key, burned into OCOTP fuses. The certificates in the CSF chain back to this root. If they don't, HAB4 rejects the image.

How Measurement Works — The Important Part

HAB4 doesn't just verify — it measures. Each verified image has its SHA-256 hash recorded in a hardware register:

HAB4 reg0 → SPL hash (32 bytes)
HAB4 reg1 → U-Boot / bootloader hash (32 bytes)
HAB4 reg2 → kernel or FIT image hash (32 bytes)
HAB4 reg3 → optional extension / second-stage hash (32 bytes)

These registers are readable by software but writable only by HAB4 hardware. You can't fake them — they reflect what actually booted.

This is the trust signal. Not who signed the image. What the image's content hash is. The measurements are independent of the signing key.

CSF Example

[Header]
HDR.Ver = 4
HDR.Scene = 3
HDR.Hash = 0xXXXXXXXXXXXXXXXX
HDR.Size = 0x00000534

[InstallCSFK]
Csph.Algo = SHA256
Csph.Key = <public key hash>

[AuthenticateCSP]
CSP.Prev = InstallCSFK
CSP.Algo = ECDSA_NP256
CSP.PKey = <signing public key>
CSP.Sign = <signature over certificate>
CSP.Hash = <cert hash>

[VerifyBootDst]
Image.Verifier = AuthenticateCSP
Image.Location = QSPI
Image.Start = 0x08080000
Image.Len = 0x100000

[AuthenticateImage]
Image.Prev = VerifyBootDst
Image.Algo = ECDSA_NP256
Image.Key = <key used to sign the image>
Image.Sign = <image signature>

Generated by NXP signing tools (MCUXpresso or signing-tools).

i.MX93 — Primary Platform

The i.MX93 is the main compute platform. Dual Cortex-A55 cores with full Linux, Cortex-M33 for secure world, and the next-generation ELE-AP (EdgeLock Secure Enclave — Advanced Platform).

Boot Modes

i.MX93 supports three boot modes — the M33 secure processor runs in all of them:

Mode A55 M33 Use Case
LP Boot (Low Power) A55 boots first (fastest) M33 runs concurrently Production — A55 Linux nodes
Single Boot A55 only M33 idle Reduced power, no attestation
Dual Boot A55 + M33 boot together Full secure world Development / debugging

LP Boot is the production default — A55 boots Linux while M33 runs the attestation stack in parallel.

ELE-AP Capabilities

ELE-AP on i.MX93 is the next-gen secure enclave with expanded crypto:

Capability ELE (RT1180) ELE-AP (i.MX93)
RSA 2048 2048
ECDSA NIST P-256 NIST P-256, P-384, P-521, BrainPool
AES 128/256 128/256/512
ECC NIST curves NIST + BrainPool curves
Hash SHA-256, SHA-384, SHA-512 SHA-256, SHA-384, SHA-512
DH No Diffie-Hellman
Key wrapping HKDF, SHA-256 HKDF, SHA-256
Sealing Yes (device attributes) Yes (enhanced attributes)
RNG DRBG DRBG

Attestation Flow

Network → Challenge (32-byte nonce)
    ↓
ELE-AP → Signs nonce + device identity (S3MU)
    ↓
Response → 272 bytes: signature + device ID + measurements
    ↓
Verifier → Validates ELE-AP signature + checks HAB4 measurements

Key Storage and Sealing

ELE-AP provides hardware key storage with 3 key types:

Key Type Storage Export
Permanent Hardware fuses No
Non-Volatile Secure NV memory No
Volatile RAM only No (cleared on reboot)

Sealing binds keys to device attributes (fuses, S3MU, lifecycle state). A sealed key can only be unsealed on the specific device it was sealed on — or a device with matching attributes. Prevents key extraction and migration to untrusted hardware.

i.MX93 Boot Chain

Boot ROM (imx-mkimage) → ATF (A55) → U-Boot SPL → U-Boot → Linux FIT
                                  ↕
                              M33 (ELE-AP)

Building i.MX93 Firmware

# Clone i.MX93 SDK components
git clone https://github.com/nxp-imx/imx-atf.git      # ARM Trust Firmware
git clone https://github.com/nxp-imx/imx-secure-enclave.git  # Secure enclave
git clone https://github.com/nxp-imx/imx-smw.git       # Secure Monitor Wrapper
git clone https://github.com/nxp-imx/imx-mkimage.git   # Boot image tools
git clone https://github.com/nxp-imx/uboot-imx.git     # U-Boot bootloader
git clone https://github.com/nxp-imx/linux-imx.git     # NXP Linux kernel

# Build SPL + U-Boot
make -C uboot-imx imx93_11x11_evk_defconfig
make -C uboot-imx -j$(nproc)

# Build ATF
make -C imx-atf PLAT=imx CROSS_COMPILE=aarch64-linux-gnu- bl31

# Build kernel + DTB
make -C linux-imx imx93_11x11_evk_defconfig
make -C linux-imx Image dtbs -j$(nproc)

# Create FIT image
mkimage -f fit_image.its fit_image.itb

RT1180 — Edge Node Platform

The RT1180 is a lighter platform suitable for edge nodes and attestation proxies. Dual A7 cores, no DDR (1MB internal RAM), but shares storage via USB/SD.

RT1180 Boot Chain

Boot ROM → M33 (ELE) → A7 (Linux/OpenWrt)

RT1180 vs i.MX93

Feature RT1180 B0 i.MX93
Application core Dual Cortex-A7 Dual Cortex-A55
Secure core Cortex-M33 Cortex-M33
Enclave ELE ELE-AP (next-gen)
RAM 1MB internal 256MB/512MB/1GB DDR
Storage SD/USB share eMMC/SD/USB
Use case Edge node, proxy Primary compute

Decentralized Signing

There is no organization. No central authority. So how does the signing key work?

Option A: shared public key — The firmware repo includes a well-known signing key pair. Published in the repo, not secret. Anyone builds firmware, signs with it, flashes their board. The signing key isn't protecting secrets — it's just a HAB4 requirement to get images through verification.

Option B: per-device self-signing — Each device generates its own signing key, burns its own SRKH into fuses, self-signs its firmware. HAB4 still verifies and measures everything identically.

Option C: open lifecycle, no signing required — In OEM Open lifecycle, HAB4 verifies images but continues on failure. You still get measurements of what actually booted. The measurements are the trust signal, not the signature.

In all cases, the network verifier doesn't care who signed. It checks: do the measured hashes match the published reference for this firmware version?

FIT Boot Chain (Kernel + DTB + Initramfs)

The FIT (Flattened Image Tree) format lets you bundle kernel, device tree, and initramfs into a single verified image:

FIT Image (.itb):
├── kernel (Image or zImage)
├── device tree (.dtb)
├── initramfs (cpio.gz)
└── signatures + hashes for each component

HAB4 verifies the entire FIT image as one unit. All components are measured together. This means the kernel, device tree, and initramfs are all cryptographically verified and measured before execution.

Building a FIT Image

# Create FIT image configuration (fit_image.its)
mkimage -f fit_image.its fit_image.itb

# Sign with HAB4-compatible CSF
signing-tools --sign --csf fit.csf --image fit_image.itb \
    --key sign_key.pem --cert sign_cert.pem --out fit_signed.itb

Network Architecture

Role Assignment

Role Platform Function
Compute Node i.MX93 Full Linux, runs inference/workloads
Coordinator i.MX93 Network sync, challenge distribution
Edge Node RT1180 Lightweight attestation, storage proxy
Proxy RT1180 Bridges edge devices to compute nodes

Trust Model

Silicon (immutable Boot ROM)
    ↓
HAB4 (verifies images, records SHA-256 measurements)
    ↓
ELE/ELE-AP (signs attestation with hardware key)
    ↓
Network verifier (checks measurements match published reference)
    ↓
Device eligible to participate

Trust = open-source code + hardware-proven measurements

The firmware is open source. Anyone audits it, builds it, verifies the reference hashes. HAB4 proves the device is running that specific code. ELE proves it's genuine silicon. The network accepts attested devices.

Not "trusted by organization X." Just "running the code we all agreed on, proven by hardware."

The Verifier Application

The verifier is the network component that validates new devices. It runs on the existing attested nodes and handles the challenge-response protocol:

# Pseudocode — runs on verifier nodes
class NetworkVerifier:
    def __init__(self, reference_hashes: Dict[str, str]):
        self.reference = reference_hashes  # Published boot measurements
    
    def verify_new_device(self, device_id: str) -> AttestationResult:
        # 1. Generate challenge nonce
        nonce = os.urandom(32)
        
        # 2. Send challenge to device
        device_response = send_challenge(device_id, nonce)
        
        # 3. Verify ELE/ELE-AP signature
        verify_ele_signature(device_response, nonce)
        
        # 4. Check boot measurements
        measured = device_response.measurements
        assert measured[0] == self.reference['spl']
        assert measured[1] == self.reference['uboot']
        assert measured[2] == self.reference['fit']
        
        # 5. Device is attested → eligible to join
        return AttestationResult.ELIGIBLE

The verifier doesn't use a "shared networkwide private key." Instead, it uses:

  • Reference hashes — Published SHA-256 hashes of the approved firmware
  • ELE/ELE-AP verification — Hardware-proven attestation signatures
  • Challenge-response — Nonce-based freshness to prevent replay attacks

Building and Flashing

Step 1: Clone Firmware Repositories

# Main firmware components
git clone --depth 1 https://github.com/nxp-imx/imx-atf.git
git clone --depth 1 https://github.com/nxp-imx/imx-secure-enclave.git
git clone --depth 1 https://github.com/nxp-imx/imx-smw.git
git clone --depth 1 https://github.com/nxp-imx/imx-mkimage.git
git clone --depth 1 https://github.com/nxp-imx/uboot-imx.git

# Reference attestation code
git clone --depth 1 https://github.com/confidential-containers/guest-components.git
git clone --depth 1 https://github.com/confidential-containers/trustee.git

Step 2: Build Firmware

# SPL
make -C u-boot-imx spl/u-boot-spl.bin
# U-Boot
make -C u-boot-imx u-boot.bin
# Kernel + FIT image
make -C linux-nxp Image dtbs
make -C initramfs initramfs.cpio.gz
mkimage -f fit_image.its fit_image.itb

Step 3: Sign with HAB4

# Sign each stage with your key
signing-tools --sign --csf spl.csf --image spl/u-boot-spl.bin \
    --key sign_key.pem --cert sign_cert.pem --out spl_signed.bin
signing-tools --sign --csf uboot.csf --image u-boot.bin \
    --key sign_key.pem --cert sign_cert.pem --out uboot_signed.bin
signing-tools --sign --csf fit.csf --image fit_image.itb \
    --key sign_key.pem --cert sign_cert.pem --out fit_signed.itb

Step 4: Publish Reference Hashes

# These are the trust anchors — published in the firmware repo
sha256sum spl_signed.bin uboot_signed.bin fit_signed.itb > reference_hashes.txt
# Commit reference_hashes.txt to the repo alongside the source code

Step 5: Device Attestation at Runtime

// Pseudocode — runs on device
hab_status = HAB4_GetStatus();          // must == HAB4_SUCCESS
HAB4_GetMeasurementReg(0, reg0);        // SPL hash
HAB4_GetMeasurementReg(1, reg1);        // U-Boot hash
HAB4_GetMeasurementReg(2, reg2);        # FIT image hash
HAB4_GetMeasurementReg(3, reg3);        # optional extension hash

nonce = receive_challenge_from_network();
ELE_Attest(S3MU, nonce, response);      # 272-byte signed device identity

send_to_network(response, reg0, reg1, reg2, reg3);

Step 6: Verifier Checks

# On the verifier side
attest_resp = receive_from_device()

# 1. Check ELE/ELE-AP signature (prevents replay, proves genuine silicon)
verify_ele_signature(attest_resp, nonce)

# 2. Check measurements against published reference
#    This is the trust decision — nothing else matters
measured = attest_resp.measurements
reference = load_reference_hashes()

assert measured[0] == reference['spl']
assert measured[1] == reference['uboot']
assert measured[2] == reference['fit']  # kernel + DTB + initramfs + Hermes
assert measured[3] == reference['extension']

# Device is attested → eligible to join

The verifier never checks "who signed this." It checks "does what booted match the published code?"

Relevant Source Code

HAB4 Implementation in U-Boot

// U-Boot HAB4 core implementation (hab.c — not ahab.c)
// File: [arch/arm/mach-imx/hab.c](https://github.com/nxp-imx/uboot-imx/blob/lf_v2025.04/arch/arm/mach-imx/hab.c#L232)
// Key functions: hab_rvt_authenticate_image(L232), imx_hab_authenticate_image(L907), authenticate_image(L1024)

// i.MX8 HAB4 container authentication
// File: [arch/arm/mach-imx/imx8/ahab.c](https://github.com/nxp-imx/uboot-imx/blob/lf_v2025.04/arch/arm/mach-imx/imx8/ahab.c#L31)
// Key functions: ahab_auth_cntr_hdr(L31), ahab_auth_release(L48), ahab_verify_cntr_image(L59), authenticate_os_container(L127)

// ELE-specific HAB4 integration
// File: [arch/arm/mach-imx/ele_ahab.c](https://github.com/nxp-imx/uboot-imx/blob/lf_v2025.04/arch/arm/mach-imx/ele_ahab.c#L260)
// Key functions: ahab_auth_cntr_hdr(L260), ahab_auth_release(L282), ahab_verify_cntr_image(L296), ahab_dump(L596), decode_ele_message(L798)

// U-Boot image types for HAB4
// File: [include/image.h](https://github.com/nxp-imx/uboot-imx/blob/lf_v2025.04/include/image.h#L222)
#define IH_TYPE_FIRMWARE_IVT  // Firmware Image with HABv4 IVT (L222)

ELE (EdgeLock Secure Enclave) API

// ELE API header in U-Boot
// File: [arch/arm/include/asm/mach-imx/ele_api.h](https://github.com/nxp-imx/uboot-imx/blob/lf_v2025.04/arch/arm/include/asm/mach-imx/ele_api.h#L9)
// Defines ELE version constants (L9-12), command macros (L20-49), response macros, data structures

// ELE driver implementation
// File: [drivers/misc/imx_ele/ele_api.c](https://github.com/nxp-imx/uboot-imx/blob/lf_v2025.04/drivers/misc/imx_ele/ele_api.c#L34)
// Key functions: ele_release_rdc(L34), ele_auth_oem_ctnr(L79), ele_verify_image(L137), ele_get_info(L445)

ARM Trust Firmware (ATF) Attestation

// DICE attestation in ARM TF (under include/lib/dice/)
// File: [include/lib/dice/dice.h](https://github.com/nxp-imx/imx-atf/blob/lf_v2.12/include/lib/dice/dice.h#L25)
// DICE constants: CDI_SIZE, HASH_SIZE, etc. (L25-30), evidence structures (L85+)

// PSA delegated attestation
// File: [include/lib/psa/delegated_attestation.h](https://github.com/nxp-imx/imx-atf/blob/lf_v2.12/include/lib/psa/delegated_attestation.h#L19)
// RSE delegated attestation commands (L19-20), PSA API functions (L76+)

// Measured boot metadata (under include/drivers/measured_boot/)
// File: [include/drivers/measured_boot/metadata.h](https://github.com/nxp-imx/imx-atf/blob/lf_v2.12/include/drivers/measured_boot/metadata.h#L11)
// Measurement sizes (L11-19), boot stage identifiers (L36-49)

Secure Monitor Wrapper (HAB4 Attestation)

// Attestation key management (under core/keymgr/)
// File: [core/keymgr/keymgr_attest.c](https://github.com/nxp-imx/imx-smw/blob/release%2Fversion_5.x/core/keymgr/keymgr_attest.c#L22)
// Key functions: key_attestation_convert_attributes(L22), key_attestation_convert_args(L48), smw_keymgr_get_attest_chal(L88), smw_keymgr_get_attest_cert(L109)

// HSM API for attestation
// File: [include/hsm/hsm_api.h](https://github.com/nxp-imx/imx-secure-enclave/blob/lf-6.12.49_2.2.0/include/hsm/hsm_api.h#L15)
// HSM module includes: handle, key, utils, crypto, import/export (L15-50)

NXP Secure Enclave SDK

// SHE (Secure Hardware Extension) API
// File: [include/she/she_api.h](https://github.com/nxp-imx/imx-secure-enclave/blob/lf-6.12.49_2.2.0/include/she/she_api.h#L11)
// SHE module includes: session, cipher, RNG, key management (L11-24)

// Device identification (under include/she/internal/)
// File: [include/she/internal/she_get_id.h](https://github.com/nxp-imx/imx-secure-enclave/blob/lf-6.12.49_2.2.0/include/she/internal/she_get_id.h#L21)
// Constants: SHE_CHALLENGE_SIZE (L21), SHE_ID_SIZE (L23), she_get_id() function (L53)

// Attestation test (under test/she/seco/)
// File: [test/she/seco/she_test.c](https://github.com/nxp-imx/imx-secure-enclave/blob/lf-6.12.49_2.2.0/test/she/seco/she_test.c#L29)
// Test entry point: main() (L29), she_test_usage() (L12)

Boot Image Signing Tools

// mkimage for i.MX8/i.MX93
// File: [src/mkimage_imx8.c](https://github.com/nxp-imx/imx-mkimage/blob/lf-6.12.49_2.2.0/src/mkimage_imx8.c#L587)
// Entry point: main() (L587), copy_file() (L104), split_dtb_from_uboot() (L496)

System Controller Firmware

// System Controller firmware recipe
// File: [recipes-bsp/imx-sc-firmware/imx-scfw-porting-kit_1.15.0.bb](https://github.com/nxp-imx/meta-imx-scfw/blob/master/recipes-bsp/imx-sc-firmware/imx-scfw-porting-kit_1.15.0.bb#L3)
// DESCRIPTION (L3), SRC_URI (L10), checksums (L12-15)

TEE Attester Pattern (Reference Implementation)

// TEE evidence generation (under attestation-agent/attester/)
// File: [attestation-agent/attester/src/lib.rs](https://github.com/confidential-containers/guest-components/blob/main/attestation-agent/attester/src/lib.rs#L49)
// BoxedAttester type (L49), Tee → Attester conversion (L51+)

// SE (Secure Enclave) attester (under attestation-agent/attester/src/se/)
// File: [attestation-agent/attester/src/se/mod.rs](https://github.com/confidential-containers/guest-components/blob/main/attestation-agent/attester/src/se/mod.rs#L82)
// SeAttester struct (L82), get_evidence() impl (L86), SeAttestationRequest (L43)

// Verifier interface (under deps/verifier/)
// File: [deps/verifier/src/lib.rs](https://github.com/confidential-containers/trustee/blob/main/deps/verifier/src/lib.rs#L53)
// VerifierConfig struct (L53), to_verifier() function (L69)

Lifecycle States

State Boot Attestation Debug Notes
OEM Open Verify, continue on failure Available JTAG enabled Development/testing
OEM Closed Halt on failure Available JTAG disabled Production
Field Return Halt on failure Limited Disabled End of life

Start in OEM Open during development. Lock to OEM Closed for production. One-way fuse blow.

Research References

Key NXP reference manuals:

  • i.MX93 Application Processor Reference Manual (i.MX93-RM)
  • RT1180 Reference Manual (RT1180-RM)
  • HAB4 User's Guide (AN12603)
  • ELE/ELE-AP SDK documentation

v2.0 — Dual-platform (i.MX93 primary, RT1180 edge). ELE-AP attestation, boot modes, sealing, network roles. Source code references maintained.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment