-
Notifications
You must be signed in to change notification settings - Fork 1
Post Quantum ML KEM
SPDM 1.4 (DMTF DSP0274 1.4.0) adds ML-KEM (FIPS 203) as a post-quantum key-exchange method. wolfSPDM implements the requester side, advertised alongside the classical ECDHE P-384 group so the responder selects one.
ML-KEM in SPDM 1.4 is standalone, not hybrid — DSP0274 §23.5 states "key encapsulation (ML-KEM) for session establishment. No support for hybrid algorithms." When ML-KEM is negotiated it replaces the DHE group, and its decapsulated 32-byte shared secret feeds the existing key schedule in place of the ECDH secret. Combine it with Post-Quantum ML-DSA signing for a fully post-quantum SPDM handshake.
- The requester generates an ephemeral ML-KEM key pair and sends the
encapsulation key
ekas theKEY_EXCHANGEExchangeData(replacing the 96-byte ECDHE X‖Y point). - The responder encapsulates, returning the ciphertext
cas theKEY_EXCHANGE_RSPExchangeData(alongside its signature and HMAC). - The requester decapsulates
cwith its decapsulation keydkto recover the 32-byte shared secretK′, which drives the key schedule (§12.2). TH1/TH2 and all downstream derivation are unchanged from the ECDHE path.
Per FIPS 203 implicit rejection, a K′ ≠ K mismatch surfaces only as a FINISH
integrity-check failure and is handled like any other session-message failure.
In NEGOTIATE_ALGORITHMS, wolfSPDM advertises a KEMAlg AlgStruct
(AlgType = 0x07, DSP0274 1.4 Table 24) with the supported ML-KEM sets, dual-stack
alongside the DHE group. The responder selects exactly one key-exchange
method — a DHE group or a KEM, never both (no hybrid). wolfSPDM enforces that
mutual exclusivity when parsing ALGORITHMS.
| KEMAlg bit | Algorithm |
ek (request) |
ciphertext c (response) |
shared secret |
|---|---|---|---|---|
0x01 |
ML-KEM-512 | 800 B | 768 B | 32 B |
0x02 |
ML-KEM-768 | 1184 B | 1088 B | 32 B |
0x04 |
ML-KEM-1024 | 1568 B | 1568 B | 32 B |
By default wolfSPDM advertises ECDHE and all three ML-KEM sets. To force a PQC-only key exchange (e.g. for testing), pin it at runtime:
/* Advertise only ML-KEM-768 (advDhe = 0). The responder must use it or fail. */
wolfSPDM_SetKeyExchangePref(ctx, 0, SPDM_KEM_ALGO_ML_KEM_768);A KEM-only preference below SPDM 1.4 fails (WOLFSPDM_E_ALGO_MISMATCH) rather
than silently downgrading to DHE.
ML-KEM follows the linked wolfSSL automatically: it is enabled when wolfSSL
reports WOLFSSL_HAVE_MLKEM and provides the wc_MlKemKey API (build wolfSSL
with --enable-mlkem). The capability is detected at configure time.
# wolfSSL with ML-KEM (and ML-DSA for a fully post-quantum handshake)
./configure --enable-ecc --enable-sha384 --enable-aesgcm --enable-hkdf \
--enable-sp --enable-mlkem --enable-mldsa --prefix=$HOME/wolfssl-install
make && make install
# wolfSPDM (ML-KEM auto-enabled; --enable-mlkem asserts it, --disable-mlkem off)
./configure --with-wolfssl=$HOME/wolfssl-install
make && make checkThe configure summary prints ML-KEM: enabled|disabled. wolfSPDM uses the
wc_MlKemKey_* API only (Init, MakeKey, EncodePublicKey, Decapsulate,
the size getters, Free); the legacy wc_KyberKey_* aliases are not used.
The demo selects a key exchange with --kex:
./examples/spdm_demo --emu --ver 1.4 --kex mlkem768 # ML-KEM key exchange
./examples/spdm_demo --emu --ver 1.4 --kex ecdhe # classical ECDHEML-KEM only changes the key-exchange ExchangeData; everything downstream is
unchanged. Two size effects:
-
Larger KEY_EXCHANGE request. The
ek(up to 1568 B for ML-KEM-1024) makes the request ~870–1640 B, vs ~158 B for ECDHE. This still fits common responders (spdm-emu advertises 4608 B), but wolfSPDM implements onlyCHUNK_GET(response reassembly), notCHUNK_SEND(request fragmentation). If the request exceeds the responder's advertisedDataTransferSize,wolfSPDM_KeyExchangefails fast (WOLFSPDM_E_BUFFER_SMALL) rather than emit a non-conformant oversized message — see Message Chunking. -
Static context. The ephemeral ML-KEM key lives in the context (a union with
the classical
ecc_key; only one is ever live), so ML-KEM-only builds use a largerWOLFSPDM_CTX_STATIC_SIZEthan the classical profile (see Configuration and Macros). A fully post-quantum (ML-KEM + ML-DSA) build uses the ML-DSA budget, which already covers it.
When ML-KEM and ML-DSA are combined, an ML-DSA-87 signed response plus the ML-KEM ciphertext can exceed the DataTransferSize, so the responder chunks it and wolfSPDM reassembles via CHUNK_GET — the full-PQ path exercises ML-KEM, ML-DSA, and chunking together.
- DMTF DSP0274 1.4.0 — §10.17.2 (ML-KEM scheme), §10.17.3 (message formats, Table 77), §12.2 (KEM K/K′ computation), Table 24 (KEMAlg), §23.5 (no hybrid)
- NIST FIPS 203 — ML-KEM
- wolfSSL
wc_mlkem.h—wc_MlKemKey_*API