Post-Quantum Secure Boot on the NXP i.MX95 Cortex-M7

We ported wolfBoot to the Cortex-M7 on NXP’s i.MX95, running on a Toradex SMARC iMX95 module, and measured ML-DSA-87 verified boot with the core at 800 MHz. Verification and boot completed in 5.25 ms, and the ML-DSA-87 build of wolfBoot came out 940 bytes smaller than the ECDSA P-256 build of the same target.

Algorithm wolfBoot .text verify + boot at 800 MHz
ECDSA P-256 22,548 B 1,741,288 cycles 2.18 ms
ML-DSA-87 21,608 B 4,201,179 cycles 5.25 ms


ML-DSA-87 is the NIST security level 5 parameter set, the strongest of the three, and verifying it takes 2.4x the cycles of the ECDSA P-256 build.

The ML-DSA-87 bootloader is 940 bytes smaller than the ECDSA one. ML-DSA verification is SHAKE plus polynomial arithmetic, so it never pulls in the big-integer math P-256 verification needs. For a verify-only workload, which is exactly what a bootloader is, the post-quantum build is the smaller of the two. Verification ran entirely inside the default 256 KB DTCM with no spill to DDR.

The extra size moves into wolfBoot’s image header, which carries a 4,627-byte ML-DSA-87 signature and a 2,592-byte public key against 64 bytes each for ECDSA P-256.

Booting Zephyr through the same verification

A bare-metal payload proves the boot chain, but the useful question is whether a real operating system rides it. Zephyr 4.4.0 boots on the M7 under wolfBoot with ML-DSA-87 verification, using the upstream imx95_evk M7 target with a single devicetree overlay to relocate it into wolfBoot’s boot partition.

Because the two payloads differ by more than fifty times in size, that gives a second data point on what verification actually scales with:

payload payload size verify + boot at 800 MHz
bare-metal test app 1,004 B 4,201,179 cycles 5.25 ms
Zephyr 54,080 B 5,916,540 cycles 7.40 ms

54x the payload costs 1.41x the verify. The lattice signature check is a fixed cost paid once, and only the SHA-256 pass over the image grows. Post-quantum verified boot of a genuine RTOS image lands at 7.4 ms.

wolfCrypt bare-metal on the M7

The complete wolfCrypt self-test suite passes on that core, and the benchmark gives a picture of what the real-time domain can do at 800 MHz:

algorithm throughput
SHA-256 29.2 MiB/s
AES-128-CBC (encrypt) 29.0 MiB/s
AES-128-GCM (encrypt) 13.9 MiB/s
SHA3-256 11.6 MiB/s
ECDSA P-256 sign 1.14 ms
ECDSA P-256 verify 2.11 ms

Note: these results are with the Cortex-M7’s instruction and data caches enabled. Without the instruction/data cache is slower (for example SHA-256 measures 1.1 MiB/s.

Building it

The target is in wolfBoot PR #863 (branch), with the full memory map and console protocol in docs/Targets.md:

cp config/examples/imx95-m7.config .config
make SIGN=ML_DSA ML_DSA_LEVEL=5 IMAGE_SIGNATURE_SIZE=4627 DEBUG_UART=1

Next on this part

We are bringing wolfBoot to the Cortex-A55 cluster to replace U-Boot, with ML-DSA-87 verifying the Linux FIT image, so that both the real-time and application domains boot from the same post-quantum root of trust.

wolfBoot is dual-licensed under GPLv3 or a commercial license. Questions about an i.MX95, SMARC or Zephyr integration? Contact facts@wolfssl.com or support@wolfssl.com.

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