GitHub

Fox Red Labs / Benchmarks / Security

Benchmarks

Exchange a key. Send an encrypted message.

Post-quantum key exchange and signatures on the LaunchPad boards: what ran and what it costs.

A Mac and a CC1354P10-1 LaunchPad board agree a key, then the board decrypts a message sent over USB. The key exchange is ML-KEM-768, a post-quantum algorithm. The message is AES-128-GCM. The key check line shows the first 4 bytes of a hash of the agreed key. They match, so both sides hold the same key.

   board> join: key pair ready, offering the public key
   key check  Mac d01a2f61  board d01a2f61  MATCH
   board> R,pqc,join_decrypt,119689,ok,AES-128-GCM, 76 bytes
   board> DECRYPTED (76 bytes, tag ok): Hello from the Mac: this line was encrypted with a key agreed by ML-KEM-768.

Excerpt of the saved result from 2026-10-08 on the CC1354P10-1, 4 of its lines, unedited.

Build this demo Demonstration only. The exchange does not authenticate either endpoint: nothing proves who is on the other end.

Build this demo: the board and what you need

Board: CC1354P10-1 LaunchPad development kit. It is the only board that has run the whole path: 296 kB of RAM and a Cortex-M33 at 48 MHz. One USB cable.

NeedDetail
BoardCC1354P10-1 LaunchPad development kit with a CC1354P10, a Cortex-M33 at 48 MHz. One USB cable to the Mac.
Board toolsCCStudio 21.0.1, TI Arm Clang 5.1.1.LTS and the SimpleLink CC13xx/CC26xx SDK 8.33.00.16 (NoRTOS), for the build and the load.
RepositoryThe cc1354p10/pqc-join folder of the repository.
MacA Mac. The scripts use macOS paths and CCStudio's command-line scripting; the run was on macOS 15.7 (Intel).
Host toolsA C compiler, Python 3 with the cryptography package, and OpenSSL 3.5 or newer first on your PATH. Install the package with pip install cryptography. The Mac's built-in /usr/bin/openssl is LibreSSL and cannot do ML-KEM, so check openssl version. The run used OpenSSL 3.6.4. The host test needs only these, no board.
USB hubA USB hub whose ports uhubctl can power-switch, because this board's debugger connects only just after power-on. Setup or handling. run.sh cuts power to the port for 6 s just before each load, as cc1354p10/FAQ.md describes. On the test setup that was the USB-A port of Apple's USB-C Digital AV Multiport Adapter, as cc1354p10/FAQ.md describes. No manual unplug and replug was done in place of the hub.
Your board's namerun.sh and the CCStudio target file cc1354p10/CC1354P10.ccxml name the test board by its debug-probe serial number. Edit both for yours.
Flash is rewrittenrun.sh backs up the board's flash and boot settings, loads the demo, then restores the image named in RESTORE_IMG and reads flash back. Name an image you want left on the board before you run it.

Run it and what you should see

  1. Run the host test. It needs no board; it builds the board code for the Mac and runs both known-answer sets and the whole exchange against mac_side.py.
    portability/pqc-join/test/run.sh

    You should see: pqc-join host test: OK

  2. Build the board image. It touches no hardware. It took 19.5 s on 2026-10-08 with the SysConfig output already generated, and longer the first time.
    cc1354p10/pqc-join/build.sh -O3

    You should see: a .out file; 272,999 B for the whole -O3 image

  3. Load and run it. run.sh power-cycles the USB port, backs up flash and the boot-settings page, and loads the image. It runs mac_side.py on the UART, then restores the image you name and reads flash back. Replace the UHUB value with your hub and port from uhubctl.
    UHUB=<hub:port from uhubctl> RESTORE_IMG=<path to the .out to leave running> cc1354p10/pqc-join/run.sh -O3

    You should see: the transcript above, then READBACK MATCHES BACKUP (2 minutes 22 seconds on 2026-10-08)

    If it stops: If the board stops at "join: making an ML-KEM-768 key pair" with the CPU in an exception handler, call TRNG_init() before TRNG_open(). Stop with a message if TRNG_open() returns NULL. The debugger showed PC 0x100006f4 and LR 0xfffffff9. This was observed on 2026-10-08 and not saved. The SDK's Board_init() starts only the power and GPIO drivers, so the image has to start the TRNG itself; cc1354p10/pqc-join/README.md, the TRNG section, has the fix.

Full write-up, with every step timed and the files listed: LP-CC1354P10-1 PQC join notes. The shared code is in portability/pqc-join.

The whole run, as saved

   board> I,results,kat pass
   board> join: making an ML-KEM-768 key pair
   board> R,pqc,join_trng_64_bytes,967756,ok,64 random bytes for the key pair
   board> R,pqc,join_keygen,1002199,ok,ML-KEM-768 key pair from the random bytes
   board> join: key pair ready, offering the public key
   board> R,pqc,join_decaps_kdf,1430503,ok,decapsulate, HKDF-SHA256 to the AES key, key check
   key check  Mac d01a2f61  board d01a2f61  MATCH
   board> R,pqc,join_decrypt,119689,ok,AES-128-GCM, 76 bytes
   board> DECRYPTED (76 bytes, tag ok): Hello from the Mac: this line was encrypted with a key agreed by ML-KEM-768.

Scroll sideways inside the box to read the long lines.

Source: cc1354p10/pqc-join/results/load4-join.txt. Lines 54 to 62 of the saved result file, unedited. Board lines start with board>. The key check line is the Mac's. The hex frames are not in this file; the exchange as JSON is in cc1354p10/pqc-join/results/load4-join.json.

What this shows

  • The two sides derived the same AES-128 key: the key check is d01a2f61 on the Mac and on the board, and the line reads MATCH.
  • The board decrypted one message and its tag verified under the agreed key. Neither side is authenticated, so anyone in the middle of the exchange would hold that key too. This does not say who sent it.
  • NIST known-answer vectors for ML-KEM-768 and ML-DSA-44 passed on the board before the exchange. See the "kat pass" line in the same file.

What it leaves open

  • No protection against power-analysis or fault attacks is claimed by the libraries or by the demo code.
  • It is a demonstration of the math and its cost, not a hardened product. Do not use the keys for anything real, and do not reuse them.
  • It ran once over one wired transport, with no radio link. The join has no signature step, so neither side is authenticated. It has not been reviewed or tested for side channels.
  • The known-answer vectors check the arithmetic against NIST's fixed inputs. They do not show the code is safe to ship.

What this means for your project

  1. Key agreement fits in about 20 kB of stack on a part with room to spare. On this 296 kB board, ML-KEM-768 peaked at 14.7 kB of stack for key generation, 20 kB for encapsulation and 18.7 kB for decapsulation. That is mlkem-native in portable C at -O3. ML-DSA-44 signing peaked at 50.1 kB. A 32 kB board should fit key agreement but was not tested. Signing there needs mldsa-native's low-RAM mode.
  2. The wire set the pace. The board's own work for the whole join was about 73 ms at 48 MHz. The wire took about 420 ms at 115,200 baud (computed, not measured). That time is long because the host and board exchange the keys, ciphertext and message as hex text, which doubles the bytes on the wire. A radio would add its own cost.
  3. A product needs signatures and a review. This join has no signature step, so neither side is authenticated. The code has had no side-channel or fault testing, review or certification. A product would sign or pin the keys, test for those attacks and have the design reviewed.
More: flash, wire size and integration
  • Flash. Flash at -O3 is 80.5 kB for ML-KEM-768 and 106.7 kB for ML-DSA-44. Built for size (-Os, built, not run) it is 9.4 kB and 13.3 kB.
  • Wire size. 2.2 kB crosses the wire (1.2 kB public key plus 1.1 kB ciphertext) against 64 B for X25519 (32 B each way). Over a radio with small packets that is many more packets: about 10 Bluetooth LE packets of 244 B against 2 (computed, not measured).
  • Integration. Initialize the TRNG driver yourself; the SDK's Board_init() does not. The libraries (mlkem-native 2.0.0, mldsa-native 2.0.0) are unmodified portable C and keep their working buffers on the stack by default. ML-DSA signing time varies from message to message (77 to 361 ms over 16 messages, mean 154 ms) because it retries until a check passes.

Which boards can do it

Post-quantum cryptography is key exchange and signatures built to survive a large quantum computer. Such a computer could break today's RSA and elliptic curves. NIST, the U.S. standards body, standardized ML-KEM for key exchange and ML-DSA for signatures in 2024. TI publishes ML-KEM-768 key generation, encapsulation and decapsulation at 27, 31 and 36 ms on the MSPM0G3519 at 80 MHz and 11, 12 and 14 ms on the MSPM33C321A at 160 MHz. Those times use its MSP Crypto Library.

Key agreement (ML-KEM-768)

  • Works CC1354P10-1: the demonstration above, about 20 kB of stack.
  • Not for this chip MSPM0 C1104: one 1.2 kB key exceeds its 1 kB of RAM.

Signing (ML-DSA-44)

  • Works CC1354P10-1: ML-DSA-44 signing measured with 50 kB of stack, in an image with a 64 kB stack.

Measured by us

1 kB = 1,024 bytes. Cycles from the CPU's own counter, converted to milliseconds at the clock shown; peak stack from painting the stack before each call. Rows come from data/results/.

BoardOperationImplementationResultPeak stackSource
CC1354P10-1ML-KEM-768 key generationmlkem-native, TI Arm Clang, -O3, code in flash20.8 ms at 48 MHz14.7 kBcc1354p10/pqc-join/README.md
CC1354P10-1ML-KEM-768 encapsulationmlkem-native, TI Arm Clang, -O3, code in flash24.0 ms at 48 MHz20 kBcc1354p10/pqc-join/README.md
CC1354P10-1ML-KEM-768 decapsulationmlkem-native, TI Arm Clang, -O3, code in flash29.1 ms at 48 MHz18.7 kBcc1354p10/pqc-join/README.md
CC1354P10-1Code in flash, ML-KEM-768 at -O3mlkem-native, TI Arm Clang, -O380.5 kBcc1354p10/pqc-join/README.md
CC1354P10-1Code in flash, ML-KEM-768 at -Os, built not runmlkem-native, TI Arm Clang, -Os9.4 kBcc1354p10/pqc-join/README.md
CC1354P10-1ML-DSA-44 key generationmldsa-native, TI Arm Clang, -O3, code in flash49.5 ms at 48 MHz32 kBcc1354p10/pqc-join/README.md
CC1354P10-1ML-DSA-44 signmldsa-native, TI Arm Clang, -O3, code in flash154.3 ms at 48 MHz, mean50.1 kBcc1354p10/pqc-join/README.md
CC1354P10-1ML-DSA-44 verifymldsa-native, TI Arm Clang, -O3, code in flash50.5 ms at 48 MHz30.2 kBcc1354p10/pqc-join/README.md
CC1354P10-1Code in flash, ML-DSA-44 at -O3mldsa-native, TI Arm Clang, -O3106.7 kBcc1354p10/pqc-join/README.md
CC1354P10-1Code in flash, ML-DSA-44 at -Os, built not runmldsa-native, TI Arm Clang, -Os13.3 kBcc1354p10/pqc-join/README.md
CC2755P10Code in flash, ML-KEM-768 at -O3mlkem-native, TI Arm Clang, -O380.5 kB
CC2755P10Code in flash, ML-DSA-44 at -O3mldsa-native, TI Arm Clang, -O3106.7 kB
CC2755P10Code in flash, ML-KEM-768 at -Os, built not runmlkem-native, TI Arm Clang, -Os9.4 kB
CC2755P10Code in flash, ML-DSA-44 at -Os, built not runmldsa-native, TI Arm Clang, -Os13.3 kB
CC2755P10ML-KEM-768 key generationmlkem-native, TI Arm Clang, -O3, code in flash10.0 ms at 96 MHz13.7 kB
CC2755P10ML-KEM-768 encapsulationmlkem-native, TI Arm Clang, -O3, code in flash11.6 ms at 96 MHz16.8 kB
CC2755P10ML-KEM-768 decapsulationmlkem-native, TI Arm Clang, -O3, code in flash14.1 ms at 96 MHz17.7 kB
CC2755P10ML-DSA-44 key generationmldsa-native, TI Arm Clang, -O3, code in flash23.9 ms at 96 MHz31.3 kB
CC2755P10ML-DSA-44 signmldsa-native, TI Arm Clang, -O3, code in flash49.7 ms at 96 MHz, mean48.3 kB
CC2755P10ML-DSA-44 verifymldsa-native, TI Arm Clang, -O3, code in flash24.3 ms at 96 MHz29.2 kB
CC2755P10ML-KEM-768 key generationmlkem-native, TI Arm Clang, -Os, code in flash10.3 ms at 96 MHz13.7 kB
CC2755P10ML-KEM-768 encapsulationmlkem-native, TI Arm Clang, -Os, code in flash11.9 ms at 96 MHz16.8 kB
CC2755P10ML-KEM-768 decapsulationmlkem-native, TI Arm Clang, -Os, code in flash14.6 ms at 96 MHz17.7 kB
CC2755P10ML-DSA-44 key generationmldsa-native, TI Arm Clang, -Os, code in flash23.4 ms at 96 MHz31.3 kB
CC2755P10ML-DSA-44 signmldsa-native, TI Arm Clang, -Os, code in flash47.3 ms at 96 MHz, mean48.3 kB
CC2755P10ML-DSA-44 verifymldsa-native, TI Arm Clang, -Os, code in flash23.7 ms at 96 MHz29.2 kB
CC2755P10ECDSA P-256 signmicro-ecc, plain C, TI Arm Clang, -O2115.1 ms at 96 MHz
CC2755P10ECDSA P-256 verifymicro-ecc, plain C, TI Arm Clang, -O2131.4 ms at 96 MHz
CC2755P10ECDSA P-256 signmicro-ecc, assembly, TI Arm Clang, -O281.2 ms at 96 MHz
CC2755P10ECDSA P-256 verifymicro-ecc, assembly, TI Arm Clang, -O294.1 ms at 96 MHz
CC2755P10ECDSA P-256 signTI library, driver, hsm, TI Arm Clang, -O224.3 ms at 96 MHz
CC2755P10ECDSA P-256 verifyTI library, driver, hsm, TI Arm Clang, -O241.7 ms at 96 MHz
MSPM0 G3519ML-KEM-768 key generationmlkem-native, TI Arm Clang, -O3, code in flash24.6 ms at 80 MHz14.6 kBportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ML-KEM-768 encapsulationmlkem-native, TI Arm Clang, -O3, code in flash29.1 ms at 80 MHz20 kBportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ML-KEM-768 decapsulationmlkem-native, TI Arm Clang, -O3, code in flash35.6 ms at 80 MHz18.8 kBportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ML-DSA-44 key generationmldsa-native, TI Arm Clang, -O3, code in flash82.2 ms at 80 MHz14.7 kBportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ML-DSA-44 signmldsa-native, TI Arm Clang, -O3, code in flash317.4 ms at 80 MHz17.2 kBportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ML-DSA-44 verifymldsa-native, TI Arm Clang, -O3, code in flash95.5 ms at 80 MHz11.4 kBportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ML-DSA-44 signmldsa-native, TI Arm Clang, -O3, code in flash, 16 messages515.7 ms at 80 MHz, mean17.2 kBportability/boards/mspm0g3519/PORT.md
MSPM0 G3519Code in flash, ML-KEM-768 at -O3mlkem-native, TI Arm Clang, -O388.5 kBportability/boards/mspm0g3519/PORT.md
MSPM0 G3519Code in flash, ML-KEM-768 at -Os, built not runmlkem-native, TI Arm Clang, -Os11.2 kBportability/boards/mspm0g3519/PORT.md
MSPM0 G3519Code in flash, ML-DSA-44 at -O3mldsa-native, TI Arm Clang, -O3192.2 kBportability/boards/mspm0g3519/PORT.md
MSPM0 G3519Code in flash, ML-DSA-44 at -Os, built not runmldsa-native, TI Arm Clang, -Os16.2 kBportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ECDSA P-256 signTI library, example, software, TI Arm Clang, -O2417.3 ms at 80 MHzportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ECDSA P-256 verifyTI library, example, software, TI Arm Clang, -O2749.8 ms at 80 MHzportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ECDSA P-256 signmicro-ecc, assembly, TI Arm Clang, -O2208.1 ms at 80 MHzportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ECDSA P-256 verifymicro-ecc, assembly, TI Arm Clang, -O2233.1 ms at 80 MHzportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ECDSA P-256 signmicro-ecc, plain C, TI Arm Clang, -O2477.0 ms at 80 MHzportability/boards/mspm0g3519/PORT.md
MSPM0 G3519ECDSA P-256 verifymicro-ecc, plain C, TI Arm Clang, -O2528.8 ms at 80 MHzportability/boards/mspm0g3519/PORT.md
MSPM0 G3519Code in flash, ML-DSA-44 at -O3mldsa-native, low-RAM, TI Arm Clang, -O3115.8 kBcc1354p10/pqc-join/README.md
MSPM0 G3519Code in flash, ML-DSA-44 at -Os, built not runmldsa-native, low-RAM, TI Arm Clang, -Os15.1 kBcc1354p10/pqc-join/README.md
MSPM0 G3519ML-DSA-44 key generationmldsa-native, low-RAM, TI Arm Clang, -O3, code in flash84.0 ms at 80 MHz14.7 kBportability/pqc-join/lowram_build.sh
MSPM0 G3519ML-DSA-44 signmldsa-native, low-RAM, TI Arm Clang, -O3, code in flash308.8 ms at 80 MHz, mean17.2 kBportability/pqc-join/lowram_build.sh
MSPM0 G3519ML-DSA-44 verifymldsa-native, low-RAM, TI Arm Clang, -O3, code in flash97.4 ms at 80 MHz11.4 kBportability/pqc-join/lowram_build.sh
MSPM0 G5187ECDSA P-256 signTI library, TI Arm Clang, -O2, code in flash417.3 ms at 80 MHz
MSPM0 G5187ECDSA P-256 verifyTI library, TI Arm Clang, -O2, code in flash749.9 ms at 80 MHz
MSPM0 G5187ECDSA P-256 signmicro-ecc, assembly, TI Arm Clang, -O2, code in flash208.1 ms at 80 MHz
MSPM0 G5187ECDSA P-256 verifymicro-ecc, assembly, TI Arm Clang, -O2, code in flash233.1 ms at 80 MHz
MSPM0 G5187ECDSA P-256 signmicro-ecc, plain C, TI Arm Clang, -O2, code in flash482.8 ms at 80 MHz
MSPM0 G5187ECDSA P-256 verifymicro-ecc, plain C, TI Arm Clang, -O2, code in flash535.3 ms at 80 MHz
MSPM0 G5187ML-KEM-768 key generationmlkem-native, TI Arm Clang, -Os, code in flash24.9 ms at 80 MHz13.8 kB
MSPM0 G5187ML-KEM-768 encapsulationmlkem-native, TI Arm Clang, -Os, code in flash29.8 ms at 80 MHz16.8 kB
MSPM0 G5187ML-KEM-768 decapsulationmlkem-native, TI Arm Clang, -Os, code in flash36.4 ms at 80 MHz17.7 kB
MSPM0 G5187Code in flash, ML-KEM-768 at -Os, built not runmlkem-native, TI Arm Clang, -Os11.2 kB
MSPM0 G5187Code in flash, ML-KEM-768 at -O3mlkem-native, TI Arm Clang, -O388.5 kB
MSPM0 G5187Code in flash, ML-DSA-44 at -Os, built not runmldsa-native, TI Arm Clang, -Os16.2 kB
MSPM0 G5187Code in flash, ML-DSA-44 at -O3mldsa-native, low-RAM, TI Arm Clang, -O3115.8 kBcc1354p10/pqc-join/README.md
MSPM0 G5187Code in flash, ML-DSA-44 at -Os, built not runmldsa-native, low-RAM, TI Arm Clang, -Os15.1 kBcc1354p10/pqc-join/README.md
MSPM0 G5187ML-DSA-44 key generationmldsa-native, low-RAM, TI Arm Clang, -Os, code in flash83.6 ms at 80 MHz12.2 kB
MSPM0 G5187ML-DSA-44 verifymldsa-native, low-RAM, TI Arm Clang, -Os, code in flash99.3 ms at 80 MHz10.9 kB
MSPM33 C321AML-KEM-768 key generationTI library, example, TI Arm Clang, -O2, code in flash46.3 ms at 32 MHz13.7 kBmspm33c321a/tools/build_crypto.sh
MSPM33 C321AML-KEM-768 encapsulationTI library, example, TI Arm Clang, -O2, code in flash53.7 ms at 32 MHz16.8 kBmspm33c321a/tools/build_crypto.sh
MSPM33 C321AML-KEM-768 decapsulationTI library, example, TI Arm Clang, -O2, code in flash62.1 ms at 32 MHz17.7 kBmspm33c321a/tools/build_crypto.sh
MSPM33 C321AML-DSA-44 key generationTI library, example, TI Arm Clang, -O2, code in flash85.4 ms at 32 MHz14.6 kBmspm33c321a/tools/build_crypto.sh
MSPM33 C321AML-DSA-44 signTI library, example, TI Arm Clang, -O2, code in flash724.7 ms at 32 MHz16.9 kBmspm33c321a/tools/build_crypto.sh
MSPM33 C321AML-DSA-44 verifyTI library, example, TI Arm Clang, -O2, code in flash108.5 ms at 32 MHz11.4 kBmspm33c321a/tools/build_crypto.sh
MSPM33 C321AECDSA P-256 signTI library, example, TI Arm Clang, -O2, code in flash162.9 ms at 32 MHzmspm33c321a/tools/build_crypto.sh
MSPM33 C321AECDSA P-256 verifyTI library, example, TI Arm Clang, -O2, code in flash328.2 ms at 32 MHzmspm33c321a/tools/build_crypto.sh
MSPM33 C321ACode in flash, ML-KEM-768 at -O3mlkem-native, TI Arm Clang, -O380.5 kB
MSPM33 C321ACode in flash, ML-KEM-768 at -Os, built not runmlkem-native, TI Arm Clang, -Os9.4 kB
MSPM33 C321ACode in flash, ML-DSA-44 at -O3mldsa-native, TI Arm Clang, -O3106.7 kB
MSPM33 C321ACode in flash, ML-DSA-44 at -Os, built not runmldsa-native, TI Arm Clang, -Os13.3 kB
MSPM33 C321AML-KEM-768 key generationmlkem-native, TI Arm Clang, -O3, code in flash8.1 ms at 160 MHz13.7 kB
MSPM33 C321AML-KEM-768 encapsulationmlkem-native, TI Arm Clang, -O3, code in flash9.2 ms at 160 MHz16.8 kB
MSPM33 C321AML-KEM-768 decapsulationmlkem-native, TI Arm Clang, -O3, code in flash11.3 ms at 160 MHz17.7 kB
MSPM33 C321AML-DSA-44 key generationmldsa-native, TI Arm Clang, -O3, code in flash19.1 ms at 160 MHz14.6 kB
MSPM33 C321AML-DSA-44 verifymldsa-native, TI Arm Clang, -O3, code in flash19.5 ms at 160 MHz11.4 kB
MSPM33 C321AML-KEM-768 key generationmlkem-native, TI Arm Clang, -Os, code in flash8.3 ms at 160 MHz13.7 kB
MSPM33 C321AML-KEM-768 encapsulationmlkem-native, TI Arm Clang, -Os, code in flash9.4 ms at 160 MHz16.8 kB
MSPM33 C321AML-KEM-768 decapsulationmlkem-native, TI Arm Clang, -Os, code in flash11.5 ms at 160 MHz17.7 kB
MSPM33 C321AML-DSA-44 key generationmldsa-native, TI Arm Clang, -Os, code in flash18.9 ms at 160 MHz14.6 kB
MSPM33 C321AML-DSA-44 verifymldsa-native, TI Arm Clang, -Os, code in flash19.0 ms at 160 MHz11.4 kB
MSPM33 C321AML-DSA-44 signmldsa-native, TI Arm Clang, -O3, code in flash58.1 ms at 160 MHz, mean16.9 kB
MSPM33 C321AML-DSA-44 signmldsa-native, TI Arm Clang, -Os, code in flash55.4 ms at 160 MHz, mean16.9 kB
MSPM33 C321ACode in flash, ML-DSA-44 at -O3mldsa-native, low-RAM, TI Arm Clang, -O388.1 kBcc1354p10/pqc-join/README.md
MSPM33 C321ACode in flash, ML-DSA-44 at -Os, built not runmldsa-native, low-RAM, TI Arm Clang, -Os12.7 kBcc1354p10/pqc-join/README.md
MSPM33 C321AML-DSA-44 key generationmldsa-native, low-RAM, TI Arm Clang, -O3, code in flash19.4 ms at 160 MHz14.6 kB
MSPM33 C321AML-DSA-44 signmldsa-native, low-RAM, TI Arm Clang, -O3, code in flash54.4 ms at 160 MHz, mean16.9 kB
MSPM33 C321AML-DSA-44 verifymldsa-native, low-RAM, TI Arm Clang, -O3, code in flash19.6 ms at 160 MHz11.4 kB
NUCLEO-U083RCCode in flash, ML-KEM-768 at -O3mlkem-native, GCC 14, -O320.5 kB
NUCLEO-U083RCCode in flash, ML-KEM-768 at -Os, built not runmlkem-native, GCC 14, -Os9.9 kB
NUCLEO-U083RCCode in flash, ML-DSA-44 at -O3mldsa-native, GCC 14, -O332.3 kB
NUCLEO-U083RCCode in flash, ML-DSA-44 at -Os, built not runmldsa-native, GCC 14, -Os13.8 kB
NUCLEO-U083RCCode in flash, ML-DSA-44 at -O3mldsa-native, low-RAM, GCC 14, -O329.4 kBportability/pqc-join/boards/nucleo-u083rc/build.sh -O3 dsa-lowram
NUCLEO-U083RCCode in flash, ML-DSA-44 at -Os, built not runmldsa-native, low-RAM, GCC 14, -Os13.1 kBportability/pqc-join/boards/nucleo-u083rc/build.sh -Os dsa-lowram
SK-TDA4VMML-KEM-768 key generationmlkem-native, GCC, -O3, a720.1 ms at 2000 MHz
SK-TDA4VMML-KEM-768 encapsulationmlkem-native, GCC, -O3, a720.1 ms at 2000 MHz
SK-TDA4VMML-KEM-768 decapsulationmlkem-native, GCC, -O3, a720.1 ms at 2000 MHz
SK-TDA4VMML-DSA-44 key generationmldsa-native, GCC, -O3, a720.2 ms at 2000 MHz
SK-TDA4VMML-DSA-44 signmldsa-native, GCC, -O3, a720.6 ms at 2000 MHz
SK-TDA4VMML-DSA-44 verifymldsa-native, GCC, -O3, a720.2 ms at 2000 MHz
The exchange, step by step

ML-KEM-768 key agreement between the board and a Mac, then one line the Mac encrypted with AES-128-GCM under the agreed key, decrypted and authenticated on the board.

Transport. USB serial through the board's built-in XDS110 debugger (its backchannel UART), 115,200 baud, 8N1. Every byte is sent as two hex characters, so 2.3 kB of payload in all (the 2.2 kB of key material plus the encrypted message) took 4,864 characters on the wire. Frame format: @PK, @CT, @KEY, @MSG, @DONE lines, each with a CRC-32 (portability/pqc-join/core/pqc_join.h). The Mac side is tools/pqc_join/mac_side.py with OpenSSL 3.5 or newer (the Mac's built-in /usr/bin/openssl is LibreSSL and cannot do ML-KEM).

  1. The board makes an ML-KEM-768 key pair from 64 bytes of its hardware random-number generator (20 ms for the bytes, 21 ms for the key pair); the 2.3 kB private key stays on the chip
  2. The board sends its 1.2 kB public key (2,389 characters as hex, about 207 ms at 115,200 baud, computed)
  3. The Mac wraps a fresh secret with that key (58.6 ms, including starting OpenSSL) and sends back the 1.1 kB ciphertext
  4. The board unwraps it and turns the secret into an AES-128 key with HKDF-SHA-256 (29.8 ms on the board); both sides show the first 4 bytes of a hash of that key, and they match
  5. The Mac sends one 76-byte line encrypted with AES-128-GCM; the board decrypts it in software (2.5 ms), checks the tag and prints it
Board by board: estimated and measured

One row per board, one cell per operation. RAM needs differ by code. About 14 kB is the peak stack measured for PQClean on the RP2040 (arXiv 2603.19340). About 19 kB is an estimate for mlkem-native: its published 14 kB plus the 2.3 kB, 1.2 kB and 1.1 kB keys and ciphertext. Cells without a measured row are estimated from cycle counts published for other cores. Each cell cites its source.

Board
ML-KEM-768 encapsulationpost-quantum key exchange
ML-DSA-44 signpost-quantum signature
ECDSA P-256 signthe classical signature it replaces
LP-CC1354P10-1 (Cortex-M33, 48 MHz, 296 kB RAM, board in hand)
ML-KEM-768 encapsulationWorksmlkem-native-ticlang-o3-flash: 1.15M cycles. 24.0 ms at 48 MHz, median of 3 loads, NIST known-answer tests pass first; mlkem-native 2.0.0, TI Arm Clang -O3, code in flash.
ML-DSA-44 signWorksmldsa-native, portable C: 7.41M cycles. 154 ms mean at 48 MHz over 16 messages (77 to 361 ms, because signing retries a random number of times). Verify 50.5 ms, key generation 49.5 ms. 50.1 kB of stack to sign. NIST known-answer tests pass first. mldsa-native 2.0.0, TI Arm Clang -O3, code in flash.
ECDSA P-256 sign
LP-EM-CC2340R5 (Cortex-M0+, 48 MHz, 36 kB RAM, board in hand)
ML-KEM-768 encapsulationNot for this chipmlkem-native beside TI's Bluetooth example: expected to have room only without the Bluetooth stack (from RAM figures, not a build): about 19 kB needed (an estimate, same basis as the LP-MSPM0G3507 figure). 5.1 kB of RAM left with TI's basic BLE example (data/results/cc2340r5.csv).
ML-DSA-44 sign
ECDSA P-256 sign
LP-EM-CC2755P10 (Cortex-M33, 96 MHz, 162 kB RAM, board in hand)
ML-KEM-768 encapsulationWorksmlkem-native, portable C: 1.11M cycles. 11.6 ms at 96 MHz; NIST's known-answer test passed on the board first.
ML-DSA-44 signWorksmldsa-native, portable C: 4.77M cycles. 49.7 ms at 96 MHz; NIST's known-answer test passed on the board first.
ECDSA P-256 signWorksmicro-ecc-c-ticlang-o2: 11.1M cycles. 115.1 ms at 96 MHz; NIST's known-answer test passed on the board first. micro-ecc-asm-ticlang-o2: 7.79M cycles. 81.2 ms at 96 MHz; NIST's known-answer test passed on the board first. ti-driver-hsm-ticlang-o2: 2.33M cycles. 24.3 ms at 96 MHz; NIST's known-answer test passed on the board first.
LP-MSPM0C1104 (Cortex-M0+, 24 MHz, 1 kB RAM, board in hand)
ML-KEM-768 encapsulationNot for this chipNo implementation found fits: 1 kB of RAM. One 1.2 kB encapsulation key exceeds it, and about 14 kB of working RAM is needed (PQClean, arXiv 2603.19340: 14.2 kB peak stack).
ML-DSA-44 signNot for this chipNo implementation found fits: 1 kB of RAM.
ECDSA P-256 sign
LP-MSPM0G3519 (Cortex-M0+, 80 MHz, 128 kB RAM, board in hand)
ML-KEM-768 encapsulationWorksmlkem-native, portable C: 2.33M cycles. 29.1 ms at 80 MHz; NIST's known-answer test passed on the board first.
ML-DSA-44 signWorksmldsa-native, portable C: 25.4M cycles. 317.4 ms at 80 MHz; NIST's known-answer test passed on the board first.
ECDSA P-256 signWorksTI's library, in software: 33.4M cycles. 417.3 ms at 80 MHz; NIST's known-answer test passed on the board first. micro-ecc-asm-ticlang-o2: 16.6M cycles. 208.1 ms at 80 MHz; NIST's known-answer test passed on the board first. micro-ecc-c-ticlang-o2: 38.2M cycles. 477.0 ms at 80 MHz; NIST's known-answer test passed on the board first.
LP-MSPM0G5187 (Cortex-M0+ with NPU, 80 MHz, 32 kB RAM, board in hand)
ML-KEM-768 encapsulationWorksmlkem-native, portable C: 2.38M cycles. 29.8 ms at 80 MHz; NIST's known-answer test passed on the board first.
ML-DSA-44 sign
ECDSA P-256 signWorksti-ticlang-o2-flash: 33.4M cycles. 417.3 ms at 80 MHz; NIST's known-answer test passed on the board first. uecc-asm-ticlang-o2-flash: 16.6M cycles. 208.1 ms at 80 MHz; NIST's known-answer test passed on the board first. uecc-c-ticlang-o2-flash: 38.6M cycles. 482.8 ms at 80 MHz; NIST's known-answer test passed on the board first.
LP-MSPM0L1306 (Cortex-M0+, 32 MHz, 4 kB RAM, board in hand)
ML-KEM-768 encapsulationNot for this chipExpected: no implementation found fits (from published RAM needs, not built here): 4 kB of RAM. The smallest portable ML-KEM-768 found needs about 14 kB (PQClean, arXiv 2603.19340: 14.2 kB peak stack). Even pqm4's Cortex-M4-only assembly low-stack build plus one ciphertext is about 3.9 kB (an estimate).
ML-DSA-44 signNot for this chipExpected: no implementation found fits (from published RAM needs, not built here): 4 kB of RAM; the signature alone is 2.4 kB.
ECDSA P-256 sign
LP-MSPM33C321A (Cortex-M33, 160 MHz, 256 kB RAM, board in hand)
ML-KEM-768 encapsulationWorksTI's MSP Crypto Library example: 1.72M cycles. 53.7 ms at 32 MHz; NIST's known-answer test passed on the board first. mlkem-native, portable C: 1.48M cycles. 9.2 ms at 160 MHz; NIST's known-answer test passed on the board first.
ML-DSA-44 signWorksTI's MSP Crypto Library example: 23.2M cycles. 724.7 ms at 32 MHz; NIST's known-answer test passed on the board first. mldsa-native, portable C: 9.3M cycles. 58.1 ms at 160 MHz; NIST's known-answer test passed on the board first.
ECDSA P-256 signWorksTI's MSP Crypto Library example: 5.21M cycles. 162.9 ms at 32 MHz; NIST's known-answer test passed on the board first.
SK-TDA4VM (2x Cortex-A72 + C7x/MMA + 2x C66x + 6x R5F, 2000 MHz, 4 GB RAM, board in hand)
ML-KEM-768 encapsulationWorksmlkem-native-gcc-o3-a72: 160k cycles. 0.1 ms at 2000 MHz; NIST's known-answer test passed on the board first.
ML-DSA-44 signWorksmldsa-native-gcc-o3-a72: 1.18M cycles. 0.6 ms at 2000 MHz; NIST's known-answer test passed on the board first.
ECDSA P-256 sign

Not for this chip the chip does not have the RAM for itWorks measured on a board; the detail is in the table above. Every other cell is estimated.

What the hardware blocks buy

Chips with an AES block run it far faster than software: the MSPM33 C321A runs AES 41.7 to 41.9 times faster at 1 kB. A block's win is largest on large data and shrinks on a single 16-byte block. The CRC block gains a few times, because the CPU still feeds it word by word. Each figure is hardware cycles against a software library on the same chip, from data/results/. A row range means the rows differ by key-setup handling or code variant.

BoardAES on 1 kB, hardware over softwareCRC on 1 kB, hardware over software
AM13E23029.8 to 29.9 times1.89 times
CC1354P10-145.8 to 51.1 timesno measured row
CC2340R5128.2 to 131.5 timesno measured row
ESP32-C6-DevKitC-1-N80.83 timesno measured row
FRDM-MCXA153no measured row7.06 times
MSPM0 C1104no measured row3 times
MSPM0 G350720.3 times13.5 times
MSPM0 G351945.8 times8.09 times
MSPM0 G518745.5 times8.12 times
MSPM0 L1306no measured row10.9 times
MSPM33 C321A41.7 to 41.9 times2.75 times
NUCLEO-U083RC19.6 to 93.9 times8.34 times
Raspberry Pi Pico 2 Hno measured row5.63 to 5.71 times
Published by others, not measured by us

In TI's tables a post-quantum signature check (ML-DSA-44 verify, 98 ms) is faster on the MSPM0G3519's Cortex-M0+ than the classical one it replaces done in software (ECDSA P-256 verify, 716 ms). The cost is size: keys and ciphertexts of 800 to 4.5 kB instead of 32 to 64, and tens of kilobytes of RAM.

TI's published figures: post-quantum against classical, millisecondsLower is better. Published by TI in the MSP Crypto Library 0.33.00.07 user guide (MSPM0 SDK 2.11.00.07), not measured by us. Milliseconds are TI's kilocycles divided by the clock. Compare the two verify bars with each other: both check a signature. The key-exchange bar is a different job.
ML-KEM-768, the whole exchange (keygen + encaps + decaps)ML-DSA-44 verifyECDSA P-256 verify
MSPM0G3519Cortex-M0+, 80 MHz; ECDSA in software
94.2 ms
97.8 ms
716 ms
MSPM33C321ACortex-M33, 160 MHz; ECDSA with the PKA
37.1 ms
22.9 ms
131 ms
Published figures for comparable cores

Published figures for these operations on comparable cores, as each source states it. Different code, compilers and clocks, so compare cycles within one source before comparing across sources.

Published byChipClockCodeOperationResultSource
TIMSPM0G3519 (Cortex-M0+)80 MHzMSP Crypto Library 0.33.00.07ML-KEM-768 keygen / encaps / decaps2.16M / 2.51M / 2.86M cycles (27 / 31 / 36 ms)MSPM0 SDK 2.11.00.07, MSP Crypto Library 0.33.00.07 user guide, Performance and Throughput
TIMSPM0G3519 (Cortex-M0+)80 MHzMSP Crypto Library 0.33.00.07ML-DSA-44 keygen / sign (mean of 16) / verify6.69M / 33.9M / 7.83M cycles (84 / 423 / 98 ms)MSPM0 SDK 2.11.00.07, MSP Crypto Library 0.33.00.07 user guide, Performance and Throughput
TIMSPM0G3519 (Cortex-M0+)80 MHzMSP Crypto Library 0.33.00.07, softwareECDSA P-256 sign / verify31.4M / 57.3M cycles (392 / 716 ms)MSPM0 SDK 2.11.00.07, MSP Crypto Library 0.33.00.07 user guide, Performance and Throughput
TIMSPM33C321A (Cortex-M33)160 MHzMSP Crypto Library 0.33.00.07ML-KEM-768 keygen / encaps / decaps1.76M / 1.96M / 2.22M cycles (11 / 12 / 14 ms)MSPM0 SDK 2.11.00.07, MSP Crypto Library 0.33.00.07 user guide, Performance and Throughput
TIMSPM33C321A (Cortex-M33)160 MHzMSP Crypto Library 0.33.00.07ML-DSA-44 keygen / sign (mean of 16) / verify3.58M / 21.3M / 3.66M cycles (22 / 133 / 23 ms)MSPM0 SDK 2.11.00.07, MSP Crypto Library 0.33.00.07 user guide, Performance and Throughput
TIMSPM33C321A (Cortex-M33)160 MHzMSP Crypto Library 0.33.00.07, PKAECDSA P-256 sign / verify10.4M / 21M cycles (65 / 131 ms)MSPM0 SDK 2.11.00.07, MSP Crypto Library 0.33.00.07 user guide, Performance and Throughput
pqm4 projectSTMicroelectronics STM32L4R5 (Cortex-M4)24 MHzreference C (clean)ML-KEM-768 keygen / encaps / decaps989k / 1.14M / 1.39M cyclesgithub.com
pqm4 projectSTMicroelectronics STM32L4R5 (Cortex-M4)24 MHzCortex-M4 assembly (m4fspeed)ML-KEM-768 keygen / encaps / decaps642k / 659k / 708k cyclesgithub.com
Chhetri, arXiv 2603.19340Raspberry Pi RP2040 (Cortex-M0+)133 MHzPQClean reference C, GCC -OsML-KEM-768 keygen / encaps / decaps2.13M / 2.47M / 2.93M cycles (16.0 / 18.6 / 22.0 ms)arxiv.org
Chhetri, arXiv 2603.19340Raspberry Pi RP2040 (Cortex-M0+)133 MHzPQClean reference C, GCC -OsML-DSA-44 keygen / sign (mean) / verify39.8 / 158.9 / 44.0 ms (21.1 million cycles to sign; sign time varies 66 to 73 percent between runs)arxiv.org
wolfSSLSTMicroelectronics STM32F439 (Cortex-M4)168 MHzwolfCrypt with Thumb-2 assemblyML-KEM-768 keygen / encaps / decaps6.5 / 6.6 / 8.3 mswolfssl.com
Karcz et al., PLOS One 2026Espressif ESP32-C6 (RISC-V)160 MHzliboqs on ESP-IDF 5.5.1ML-KEM-768 keygen / encaps / decaps171 / 171 / 205 msjournals.plos.org
Why a chip buyer cares now
The dates that set the deadlines

From the standards bodies and governments themselves. NIST is the U.S. standards body; FIPS numbers are its standards; CNSA 2.0 is the U.S. National Security Agency's list of required algorithms. A device sold in 2027 may still be in the field after 2035.

  • 2024-04-11: European Commission Recommendation on a coordinated roadmap for the move to post-quantum cryptography. digital-strategy.ec.europa.eu
  • 2024-08-13: NIST publishes the final FIPS 203 (ML-KEM), FIPS 204 (ML-DSA) and FIPS 205 (SLH-DSA), ready for immediate use. nist.gov
  • 2024-11-12: NIST IR 8547 draft: 112-bit ECDSA, ECDH and RSA deprecated after 2030; all quantum-vulnerable public-key algorithms, P-256 included, disallowed after 2035 (still a draft). csrc.nist.gov
  • 2024-12: NSA CNSA 2.0 FAQ version 2.1: ML-KEM-1024 and ML-DSA-87 for U.S. national security systems. media.defense.gov
  • 2025-06-23: EU member states' roadmap: start by the end of 2026, high-risk uses by the end of 2030 with quantum-safe firmware upgrades on by default, everything by the end of 2035. digital-strategy.ec.europa.eu
  • 2027: NSA: from 2027 all new commercial national security systems must support quantum-resistant algorithms; legacy systems that cannot are phased out by 2030. nsa.gov
  • 2030 to 2033: CNSA 2.0: firmware and software signing exclusively post-quantum by 2030; constrained devices support and prefer by 2030, exclusively by 2033. media.defense.gov
The two algorithms, and the sizes they bring
Key, ciphertext and signature sizes

ML-KEM replaces the key exchange, ML-DSA the signature. The last two rows are the classical algorithms they replace, for scale. "NIST category" is NIST's strength scale from 1 to 5: higher is stronger, and bigger.

AlgorithmWhat it isPublic keyCiphertext or signatureSource
ML-KEM-512Post-quantum key exchange, NIST category 1 (the lowest of five strength levels)800768nvlpubs.nist.gov
ML-KEM-768Post-quantum key exchange, NIST category 3; NIST's recommended default1.2 kB1.1 kBnvlpubs.nist.gov
ML-KEM-1024Post-quantum key exchange, NIST category 5; the size CNSA 2.0 requires1.5 kB1.5 kBnvlpubs.nist.gov
ML-DSA-44Post-quantum signature, NIST category 21.3 kB2.4 kBnvlpubs.nist.gov
ML-DSA-65Post-quantum signature, NIST category 31.9 kB3.2 kBnvlpubs.nist.gov
ML-DSA-87Post-quantum signature, NIST category 5; the size CNSA 2.0 requires2.5 kB4.5 kBnvlpubs.nist.gov
X25519Classical key exchange it replaces (the other side's public key is the reply)3232MSPM0 SDK 2.11.00.07 source/ti/crypto/include/curve25519/curve25519.h (X25519_KEY_LENGTH)
ECDSA P-256Classical signature it replaces (public key as x and y coordinates)6464MSPM0 SDK 2.11.00.07 source/ti/crypto/include/ec/ec.h (32-byte coordinates and r, s)
Questions about this section
Why does the board stop at "join: making an ML-KEM-768 key pair"?
Call TRNG_init() before TRNG_open(), and stop with a message if TRNG_open() returns NULL. An image that skips TRNG_init() stops here; the SDK's Board_init() starts only the power and GPIO drivers. The TRNG is the chip's hardware random-number generator, which the key pair needs. The debugger showed the CPU in an exception handler, PC 0x100006f4 and LR 0xfffffff9 (observed 2026-10-08, not saved). cc1354p10/pqc-join/README.md, the TRNG section.
Can someone in the middle read the message?
Someone who only listens cannot. They see the public key and the ciphertext, and ML-KEM-768 is built so that does not give away the key. Someone who can change bytes on the wire can. They answer the board with their own key and the Mac with theirs, and each side agrees a key with them. Do not ship it as is: the demonstration has no signature step. A product signs or pins the keys, for example with ML-DSA-44, whose signing is measured above.
What does the key check line mean, and what would MISMATCH mean?
Each side prints the first 4 bytes of a hash of the key it holds (tools/pqc_join/mac_side.py). MATCH means the two are equal, so both sides almost certainly hold the same key, and the decryption tag passing confirms it. MISMATCH would mean they derived different keys and the board's decryption would fail its tag check. Every saved run printed MATCH; a MISMATCH has not been seen here.
Why does the demo need a USB hub the Mac can switch?
This board's debugger connects only in the first seconds after power-on, so run.sh switches the port off for 6 s and on again before each load. Install uhubctl (brew install uhubctl) and run it alone to list hubs; one marked ppps can switch its ports. Here that was the USB-A port of Apple's USB-C Digital AV Multiport Adapter. No manual unplug and replug was done. cc1354p10/FAQ.md.
Are any of these numbers measured by Fox Red Labs?
Some are. Measured by us: 111 results on 7 boards (CC1354P10-1, CC2755P10, MSPM0 G3519, MSPM0 G5187, MSPM33 C321A, NUCLEO-U083RC, SK-TDA4VM), in the Measured by us table above, with 47 peak-stack measurements shown beside them. The other times are TI's published figures or estimates. Every cell marked Not run is TI's published figure or an estimated range, and the "published by others" table is exactly that.
Why can the MSPM0 L1306 and MSPM0 C1104 not do post-quantum key exchange?
RAM. The smallest portable ML-KEM-768 found needs about 14 kB of working RAM (PQClean on the RP2040, arXiv 2603.19340); the MSPM0 L1306 has 4 kB and the MSPM0 C1104 has 1 kB, less than one 1.2 kB public key. The answer is no for the MSPM0 C1104 (its RAM is below the size of one public key). For the MSPM0 L1306 the answer is expected to be no: it comes from published RAM needs, not from a build.
Why does the CC2340R5 fit ML-KEM alone but not beside Bluetooth?
ML-KEM-768 with mlkem-native needs about 19 kB of working RAM (an estimate). TI's basic Bluetooth example leaves 5.1 kB of the chip's 36 kB. So room is expected only without the Bluetooth stack. That is one TI example configuration; a trimmed Bluetooth build or a lower-RAM ML-KEM was not built.
Is post-quantum slower than what it replaces?
In TI's own tables, not for checking signatures: ML-DSA-44 verify is faster than ECDSA P-256 verify on both chips TI lists. Signing is slower. Every key, ciphertext and signature is 20 to 72 times bigger, which costs more on a radio link.
Why do ML-DSA signing times differ so much between TI's cores and runs?
Signing repeats an internal try until a check passes, so its time varies from run to run. The RP2040 paper saw 66 to 73 percent spread, and TI averages only 16 signs. TI's own ML-DSA-44 sign mean is 40.4M cycles at 32 MHz but 33.9M cycles at 80 MHz on the same chip (TI's MSP Crypto Library 0.33.00.07 user guide, MSPM0 SDK 2.11.00.07, performance tables). The difference is probably run-to-run noise (suspected, not repeated). Verification time is fixed. TI's runs were not repeated here.
Why does the MSPM0 G3507 fit ML-DSA signing only in low-RAM mode?
The default mode of mldsa-native needs 43.4 kB for signing, more than the chip's 32 kB. Its low-RAM mode needs 9.6 kB, or about 14.4 kB with the 2.5 kB private key and 2.4 kB signature added, and trades speed for memory by an amount nobody has published. This is an estimate from published figures, not a build.
Why does this page give both 14 kB and 19 kB for ML-KEM-768?
They are different code. About 14 kB is the peak stack measured for PQClean on the RP2040 (arXiv 2603.19340). About 19 kB is an estimate for mlkem-native: its published 14 kB for decapsulation plus the keys and ciphertext (2.3 kB, 1.2 kB and 1.1 kB).
What does "No estimate" mean?
There is no estimate or test for that board and operation.
How it was measured: kinds of evidence and how to read the tags

Measured by us: 111 results on 7 boards (CC1354P10-1, CC2755P10, MSPM0 G3519, MSPM0 G5187, MSPM33 C321A, NUCLEO-U083RC, SK-TDA4VM), in the Measured by us table above, with 47 peak-stack measurements shown beside them. The other times are TI's published figures or estimates. Researched 2026-10-09; each figure cites its source. Terms: vocabulary.

Four kinds of evidence on this page

  • Demonstration, ran end to end: The exchange above, on one board, once, on 2026-10-08.
  • Known-answer check, NIST vectors passed: ML-KEM-768 and ML-DSA-44 outputs match NIST's fixed inputs byte for byte, on the board, before timing.
  • Measurement, cycles and stack: Times from the CPU cycle counter and stack from painting it, in the Measured by us table above.
  • Assurance, none claimed: No side-channel testing, no certification (such as FIPS 140-3), no review. Nothing here says the project is secure.

A cell marked "Chip limit" means the chip cannot hold the code in its RAM or flash. Only rows marked as measured are measurements made here; none of them is a security claim.

Words in this section

Words used below, once: Cortex-M0+ is a small Arm core with no FPU (floating-point unit); Cortex-M33 is a faster Arm core; C28x is TI's control-processor core; PKA is hardware that speeds up elliptic-curve math. Encapsulation: the sender wraps a fresh secret with the receiver's public key, which gives a ciphertext (the wrapped secret); decapsulation unwraps it. kcycles means thousands of clock cycles. pqm4 is an open-source collection of post-quantum code for Cortex-M4 chips (m4fspeed is its speed-tuned assembly build); liboqs is an open-source post-quantum library; uint8_t is C's 8-bit integer type. 1 kB = 1,024 bytes.

ML-KEM (Kyber)
NIST's post-quantum key exchange (FIPS 203). One side publishes a public key; the other uses it to wrap a fresh shared secret (encapsulation) and sends back a ciphertext; the first side unwraps it (decapsulation). The number (512, 768, 1024) is the strength level.
ML-DSA (Dilithium)
NIST's post-quantum signature (FIPS 204), used to prove firmware or a message came from the right key. 44, 65 and 87 are its strength levels.
ECDSA P-256, X25519
Today's elliptic-curve signature and key exchange; a large enough quantum computer could break both.
TRNG
True random-number generator: the chip's hardware source of random bytes, which the key pair needs.
HKDF-SHA-256
A standard way to turn the shared secret from the exchange into an AES key of the right length.
PKA
Public-key accelerator: hardware on the MSPM33C321A that speeds up the elliptic-curve math.
kcycles
Thousands of clock cycles. Divide by the clock in MHz to get milliseconds.
mlkem-native, mldsa-native
Portable open-source C versions of ML-KEM and ML-DSA that run on any of these cores; TI's library covers only the MSPM0G3519 and MSPM33C321A.