tyler

2026-05-2416 minsecurityembeddedsecure-boothardwarecrypto

Embedded Security In Practice: Secure Boot on FRDM-MCXN947

This post implements ECDSA P-384 secure boot on the FRDM-MCXN947: the boot ROM now refuses any image without a valid signature. Live captures prove TS2 closed.

The M1 Threat Analysis and Risk Assessment (TARA) flagged TS2 at Risk 3. In the default DEVELOP state the boot Read-Only Memory (ROM) runs whatever is in flash: an attacker with debug or In-System Programming (ISP) access writes firmware, the part runs it. The damage scenario (D2) is firmware overwritten to plant persistence or defeat body functions, rated Moderate for safety and Major for operational impact. The ROM checks nothing before running it.

M2 closes that path. Secure boot makes the ROM a gatekeeper: it runs an image if and only if that image carries a valid Elliptic Curve Digital Signature Algorithm (ECDSA) P-384 signature against a root of trust provisioned into the device. An attacker who can rewrite flash still cannot get code to execute without the private key behind that root. This post implements that control on the FRDM-MCXN947 (Secure Provisioning SDK (SPSDK) 3.10.0, board in DEVELOP, One-Time Programmable (OTP) blank throughout), proves it with two live board captures, and scores the residual risk.

Table 1: the risk before the control
IDThreat scenarioImpact (S/F/O/P)FeasibilityRisk (1-5)
TS2Firmware overwritten, part runs it (D2)Moderate / Negligible / Major / NegligibleMedium3
TS5Fault injection to bypass secure bootModerate / Negligible / Major / NegligibleLow2

TS2 is the direct target: secure boot removes the “write flash, it runs” path. TS5 (fault injection timed against the boot ROM’s signature check) is the residual attack against the control we are about to add. It is already at Risk 2 in M1’s TARA because of Low feasibility, and M2 does not change that. Glitch resistance is a Leading-tier anti-tamper problem, the second conversation, scoped for later; no foundational control in this series closes it.

The series roadmap

M2 is the second of eight milestones, each taking one control from the Embedded Security In Practice blog post and applying it to this board. The table situates this post; every milestone traces back to a threat from the M1 TARA.

The series milestone roadmap
MilestoneWhat it covers
M0Baseline: a working hello_world on the board, before any security
M1Target, threat model, and lifecycle plan. No fuses burned
M2 (this post)Secure boot: only signed firmware runs
M3Authenticated debug: keep debug, behind a credential
M4Hardware root of trust and key storage
M5TrustZone-M isolation between secure and non-secure code
M6Flash encryption: a raw read returns ciphertext
M7Secure firmware update: signed, fail-safe, anti-rollback
M8The basics: TRNG, disabled ISP and DFU, no defaults

The security control: Firmware Signing

Signed firmware is the mitigation. Every image the device runs carries a cryptographic signature, and the boot ROM runs it only after that signature checks out. Flashing still works the same way, so an attacker with debug or bus access can overwrite the firmware. That overwritten firmware will not run: without the signing key, an attacker cannot produce a signature the ROM accepts. Closing TS2 comes down to two questions: who is allowed to sign, and how the device knows to trust them.

Everything depends on who the ROM trusts. The answer is the Root of Trust Key Hash (ROTKH): a 48-byte digest stored in Customer Manufacturing Programmable Area (CMPA) at 0x0100_4060. Each of the four P-384 root public keys is hashed with Secure Hash Algorithm, 384-bit (SHA-384), and those four hashes are concatenated and hashed again into the ROTKH. The ROM trusts the hash, not the keys directly. That indirection enables revocation: a compromised root can be retired while the device keeps booting on the others, without changing the trust anchor baked into CMPA.

The four Root of Trust Keys (RoTK0 through RoTK3) are the OEM’s offline root Certificate Authorities (CA). In a real product these live in a Hardware Security Module (HSM), powered on only to certify signing keys or handle exceptional revocation events. In this walkthrough the keys are PEM files on disk; the chain design is the point here, not production key custody. Offline means not connected to the build pipeline: a root key sits at the top of the trust hierarchy, and keeping it cold is the whole point of the design. The four slots serve distinct roles. RoTK0 and RoTK1 are boot-image CAs: two slots so one root can be retired while the device still boots on the other. RoTK2 is the debug authentication CA, idle until M3. RoTK3 is the firmware-update CA, idle until M7. One shared root-of-trust key table backs all three concerns; the RoTK_USAGE field at 0x0100_4054 assigns per-key roles.

Day-to-day firmware signing uses the Image Signing Key (ISK), not the roots. The ISK is the warm key: it lives in the release pipeline, signs each firmware build, and can be revoked and re-issued under a root if compromised. That is the standard Public Key Infrastructure (PKI) split. Compromising the ISK is painful but recoverable. Compromising a root is the catastrophe the offline posture exists to prevent.

Building a signed image goes like this. Generate a cert block containing the ISK public key, an ISK certificate (signed by RoTK0 with ECDSA P-384), and the ROTKH. Wrap the application binary as a signed_xip Master Boot Image (MBI): app binary plus cert block plus a 96-byte ECDSA P-384 signature computed by the ISK private key. On every boot the ROM walks that chain backward: read ROTKH from CMPA, hash the four root pubkeys from the cert block and confirm the result matches, verify the ISK cert against RoTK0, verify the image signature against the ISK. All three checks must pass. Any failure drops the ROM to its recovery interface instead of running the image.

I provisioned the ROTKH and set SEC_BOOT_EN to ECDSA_SIGNED, writing two things into CMPA. The ROTKH (48 bytes, high-word-first at 0x0100_4060) is the trust anchor. The SEC_BOOT_EN field at 0x0100_4050 is set to ECDSA_SIGNED (value 0x3), which tells the ROM to accept only ECDSA-signed images. The ENF_CNSA = LIMITED_0B01 field pins the algorithm suite to the Commercial National Security Algorithm (CNSA) level: P-384, SHA-384, and Advanced Encryption Standard 256-bit (AES-256), enforced across secure boot, debug authentication, and firmware update. Setting it in M2 means M3 and M7 start with the algorithm choice already made.

flowchart TB subgraph OFFLINE[Root keys - offline] RK0[RoTK0 - boot CA, ISK signer] RK1[RoTK1 - boot CA] RK2[RoTK2 - debug CA, M3] RK3[RoTK3 - update CA, M7] end ROTKH[ROTKH: SHA-384 of the four per-key hashes\n48 bytes, CMPA 0x0100_4060] ISK[ISK certificate\nsigned by RoTK0] IMG[signed_xip MBI\ncert block + P-384 ECDSA signature] ROM[Boot ROM\nreads ROTKH from CMPA] OFFLINE -->|hash the four per-key hashes| ROTKH RK0 -->|signs| ISK ISK -->|signs| IMG ROM -->|verifies signature against| ROTKH
The trust chain for M2. Each of the four P-384 roots is hashed, and those four digests hash together into the 48-byte ROTKH stored in CMPA. RoTK0 certifies the ISK, which signs images day-to-day; the roots stay offline. RoTK2 and RoTK3 sit idle until M3 and M7. The ROM’s job on every boot: verify the image signature against the ROTKH, run it if it matches, refuse and drop to recovery if it does not.

All tools: SPSDK 3.10.0, family mcxn947.

Key generation (four P-384 roots plus the ISK):

for i in 0 1 2 3; do
  nxpcrypto key generate -k secp384r1 -e pem -o provisioning/keys/rotk${i}_prv.pem
done
nxpcrypto key generate -k secp384r1 -e pem -o provisioning/keys/isk_prv.pem

Cert block and ROTKH readout. Note: nxpimage prints the value labeled RKTH (Root Key Table Hash, SPSDK’s output label). The silicon field on this device is ROTKH, consistent with M1’s naming note. Same value, different label.

nxpimage -v cert-block export -c provisioning/configs/m2/cert_block.yaml
# prints: RKTH: 163e8d7a...799b61

MBI build (edit the template to set inputImageFile, certBlock, signer = isk_prv.pem):

nxpimage mbi export -c provisioning/configs/m2/mcxn947_xip_signed.yaml

CMPA: cross-check byte order, write, and verify. The cmp step catches ROTKH byte-order mismatches before writing:

# cross-check ROTKH byte order: derive CMPA two ways, compare
pfr export -c provisioning/configs/m2/cmpa.yaml -o build/m2/cmpa.bin
pfr export -c provisioning/configs/m2/cmpa.yaml -e build/m2/cert_block.bin \
           -o build/m2/cmpa_check.bin
cmp build/m2/cmpa.bin build/m2/cmpa_check.bin   # identical => ROTKH order correct

# enter ISP via debug mailbox, flash the signed image, write CMPA, read back
nxpdebugmbox -i mcu-link cmd -f mcxn947 ispmode -m 0
PORT=/dev/cu.usbmodemJABXJL3CFXOL03             # substitute your MCU-Link VCOM port
blhost -p $PORT flash-erase-region 0x0 0x8000
blhost -p $PORT write-memory 0x0 build/m2/mbi_signed.bin
pfr write -f mcxn947 -p $PORT -t cmpa -c provisioning/configs/m2/cmpa.yaml
pfr read  -f mcxn947 -p $PORT -t cmpa -o build/m2/cmpa_after.bin   # confirm SEC_BOOT_EN=ECDSA_SIGNED

Load and test the control

With CMPA holding SEC_BOOT_EN=ECDSA_SIGNED, the ROTKH, and ENF_CNSA=LIMITED_0B01, two cases show the control working.

Signed firmware runs

On reset (triggered via the debug probe for deterministic capture timing), the ROM authenticated the signed_xip image against the CMPA ROTKH and ran it:

--- boot banner (signed image authenticated against CMPA ROTKH) ---
MCUX SDK version: 2026.06.00
hello world.

--- echo test (proves the app is executing, not just printing once) ---
sent: secure-boot-OK
recv: secure-boot-OK

A single banner print could conceivably come from the ROM before it refuses an image. An echo loop proves the application is actually running. Legitimate, signed firmware gets through.

Tampered firmware is refused

I flipped one byte inside the signed image at offset 0x2340 from 0x00 to 0xFF. cmp -l confirmed exactly one byte differs from the valid signed image. That image went into flash. On reset:

--- listen for the boot banner (expect none) ---
banner bytes captured: 0      # no banner, the app never ran

--- echo test ---
sent: ALIVE?
recv: 5a a7 00 03 01 50 00 00 fb 40   # mboot framing (0x5A) from the ROM, not the app

No banner. No echo. The Universal Asynchronous Receiver/Transmitter (UART) returned mboot protocol bytes from the ROM bootloader, not the application. USB Human Interface Device (USB-HID) 1fc9:014f enumerated. The ROM dropped to its native recovery interface rather than executing the image.

This is the TS2 attack path stopped at the ROM: an attacker who rewrites flash cannot get code to execute without the ISK’s private key. One flipped byte breaks the ECDSA signature check, and the ROM refuses the image. The drop-to-recovery rather than halt-and-die behavior is the right default for dev silicon. Halt-and-stay-dead would punish provisioning mistakes (wrong ROTKH byte order, malformed cert block) just as harshly as it would stop an attacker who has no signing key. Whether a shipped Body Control Module (BCM) should offer any recovery channel at all is an Original Equipment Manufacturer (OEM) policy call, not a silicon default.

The risk after the control

Secure boot shuts the direct TS2 path: an attacker who writes to flash cannot get code to run without the signing key. But M2 as implemented here is not the final state. Three residuals remain.

The CMPA configuration is reversible while OTP stays blank. An attacker with open debug access, which M3 has not yet closed, can call pfr erase-cmpa and wipe the secure-boot configuration. The OTP burn is what makes the policy permanent, and that burn is deliberately deferred until the full control stack is proven.

The update path is a separate residual. A signed but malicious firmware update that passes the signature check because the ISK was compromised, or because the update channel has no rollback protection, is the problem M7 closes.

Fault injection (TS5) against the boot ROM’s signature check is the hardware-level residual. M2 does nothing for it.

The control for both rows: the boot ROM verifies an ECDSA P-384 signature against the provisioned ROTKH before running any image.

Table 2: the risk after the control
IDResidual attack pathFeasibilityRisk (1-5)Closes fully at
TS2Disable reversible config via open debug; no permanence until OTP burnLow2M3 (debug lock) + final OTP burn
TS5Voltage/EM glitch past the ROM signature checkLow2Leading-tier anti-tamper (second conversation)

TS2 drops from Risk 3 (Medium feasibility, no control) to Risk 2 (Low feasibility, control in place but reversible). The feasibility falls because the direct flash-overwrite path is closed; disabling the config now requires open debug access plus knowledge of the provisioning tool chain. M3 and the OTP burn close the residual completely. I score the residual at Risk 2. That is a judgment in M1’s framework, not a board measurement, but it reflects the real state: the direct overwrite path is closed, the reversible-config and open-debug paths are not.

A risk score reflects the current state, not the intended end state. Secure boot is enforced but the config is still reversible and the part is still in DEVELOP, so TS2 stays at Risk 2, not Risk 1, until M3 and the OTP burn close the residual.

Why nothing is fused yet

All of this ran reversibly in CMPA with OTP blank. That is not a gap; it is the designed condition in DEVELOP state. Write the secure-boot config into CMPA, get real ROM enforcement from it, recover with pfr erase-cmpa if something is wrong. On this board, I proved the chain end to end with a recoverable setting before committing anything one-way. Nothing is fused yet because nothing needs to be fused yet. The burn happens once, deliberately, as a final step after all controls are proven.

What’s next

M3 closes the open debug door that is the remaining TS2 residual. After the CMPA write, pyocd still reached the AHB Access Port (AHB-AP) in application mode; the CMPA CC_SOCU recompute that pfr write triggers did not lock debug. M3 starts from known-open debug, with RoTK2 already assigned the debug CA role in the shared root-of-trust key table.

The OTP burn (ROTKH words 0x0b through 0x16, low-word-first per Security Reference Manual (SRM) Rev 5 Table 186, the reverse of CMPA’s high-to-low order) is the final hardening step. It waits until all controls M2 through M8 are implemented and verified. When that point arrives, the ROTKH and secure-boot-config fuses go in once, with the context of a complete working stack behind them.

Further reading

  • M1: FRDM-MCXN947 TARA and lifecycle plan: the TARA table M2 closes, the lifecycle state machine, and the full M2-M8 roadmap.
  • Embedded Security In Practice (Part 1): why secure boot, debug lockdown, and key storage each exist, vendor-neutral.
  • NXP MCX Nx4x Security Reference Manual, Rev 5 (2025-05-19): primary source for all CMPA register definitions and OTP fuse map used here.
  • SPSDK 3.10.0 documentation (nxpimage, pfr, nxpdebugmbox): tool reference for all commands above.

Acronyms

Acronyms used in this post
AcronymTermWhat it is
AES-256Advanced Encryption Standard, 256-bitSymmetric cipher; the CNSA suite level enforced by ENF_CNSA
AHB-APAHB Access PortDebugger memory-access port on the MCX
BCMBody Control ModuleThe automotive ECU role this board plays in the series (from M1)
CACertificate AuthorityKey that signs and delegates trust to subordinate keys
CC_SOCU(not expanded)CMPA debug-access configuration field (detailed in M3)
CMPACustomer Manufacturing Programmable AreaFlash region holding secure-boot config; rewritable in DEVELOP
CNSACommercial National Security Algorithm suiteNSA-defined algorithm suite: P-384, SHA-384, AES-256
DFUDevice Firmware UpdateUSB download mode disabled in M8
ECDSAElliptic Curve Digital Signature AlgorithmSigning algorithm used for image authentication
ENF_CNSACNSA-enforcement fieldCMPA field that pins the algorithm suite to CNSA level
HSMHardware Security ModuleTamper-resistant device that guards private keys; where the root CAs live
ISKImage Signing KeyIntermediate key that signs images day-to-day; certified by a RoTK
ISPIn-System ProgrammingROM bootloader mode for flashing and device configuration
LC_STATElifecycle-state fieldCMPA/OTP field encoding the device lifecycle phase
MBIMaster Boot ImageNXP firmware container format verified by the boot ROM
OEMOriginal Equipment ManufacturerDevice maker responsible for provisioning and recovery policy
OTPOne-Time ProgrammableFuse bank; writes are permanent
PKIPublic Key InfrastructureThe root-CA / signing-key hierarchy that authenticates firmware
RKTHRoot Key Table HashSPSDK output label for the ROTKH value
ROMRead-Only MemoryBoot ROM that verifies images on every reset
RoTKRoot of Trust KeyOne of four P-384 keys forming the root of trust
RoTK_USAGEkey-role fieldAssigns boot, debug, and update CA roles to each RoTK slot
ROTKHRoot of Trust Key Hash48-byte value in CMPA: each RoTK public key is hashed with SHA-384, and those four hashes are concatenated and hashed again to produce it
SB3.1Secure Binary 3.1NXP firmware-update container format
SEC_BOOT_ENsecure-boot-enable fieldCMPA field controlling which image types the ROM will boot
SHA-384Secure Hash Algorithm, 384-bitHash algorithm used for the ROTKH and image digests
SPSDKSecure Provisioning SDKNXP tool suite: nxpimage, pfr, nxpdebugmbox, nxpcrypto
SRMSecurity Reference ManualNXP MCX Nx4x Security Reference Manual
TARAThreat Analysis and Risk AssessmentISO/SAE 21434 security analysis method
TRNGTrue Random Number GeneratorHardware entropy source, seeded in M8
UARTUniversal Asynchronous Receiver/TransmitterSerial interface used for boot banner and echo test
USB-HIDUSB Human Interface DeviceUSB device class the ROM enumerates in recovery mode
XIPExecute In PlaceImage type that runs directly from flash; NXP type name: signed_xip
← OlderUsing the Automotive Threat Matrix to threat model a telematics control unit