A safe deposit box that checks only the first half of your fingerprint before handing over what's inside
ML-KEM (FIPS 203, the NIST post-quantum KEM formerly known as Kyber) relies on the Fujisaki-Okamoto transform to convert a CPA-secure PKE into an IND-CCA2 KEM. The core trick is *implicit rejection*: on decapsulation the recipient re-encrypts the derived message and does a constant-time, full-length compare against the received ciphertext; on any mismatch the KEM returns a pseudorandom key derived from a secret seed instead of failing. The wolfSSL ARM64 NEON code path only compared roughly the first half of the re-encrypted ciphertext bytes, so mutations in the untested tail were silently accepted as valid.
This is a legitimate cryptographic regression: half the ciphertext is effectively unauthenticated, which breaks the FO transform's chosen-ciphertext hardening. That said, the CVSS C:L/I:L/A:N framing is fair. To turn the flaw into recovered plaintext or key material an attacker needs (a) reuse of a long-lived ML-KEM private key across many decapsulations, (b) an oracle that reveals whether decapsulation *effectively succeeded* (via observable session establishment, key confirmation, or subsequent AEAD success), and (c) the target must be compiled with the NEON backend on ARM64. Vendor MEDIUM (6.3) matches reality — it's a real IND-CCA2 downgrade, not a break-the-ciphertext primitive.
4 steps from start to impact.
Identify a target using wolfSSL ML-KEM NEON on ARM64
ssl-enum-ciphers, testssl.sh with PQ group probes.- Target negotiates ML-KEM (X25519MLKEM768 or similar) with wolfSSL
- Target is ARM64 with NEON build path enabled at compile time
- Long-lived static ML-KEM keypair (not ephemeral per-connection)
- Most TLS/SSH deployments use *ephemeral* ML-KEM keys per handshake — no key reuse means no oracle to query
- Popular wolfSSL ARM64 builds on cloud Graviton often use the C reference path unless NEON is explicitly enabled
- No easy remote way to distinguish NEON build from generic build
Establish a chosen-ciphertext oracle
- Ability to send many handshake attempts against the same static keypair
- Observable success/failure oracle from higher-layer protocol
- Protocol does NOT terminate the key before AEAD confirmation
- Static ML-KEM keys are rare — TLS 1.3 and IKEv2 profiles use per-handshake keys
- Rate-limiting, IDS anomaly detection, and connection accounting flag high-volume handshake anomalies
- Most deployed cryptographic protocols apply a KDF and transcript hash over the shared secret, which further blinds partial-ciphertext queries
Mount a chosen-ciphertext attack against the underlying K-PKE
- Stable oracle from step 2
- Same static key across the full query campaign
- No handshake accounting or replay window that trips before enough queries land
- Query budget of millions of TLS handshakes against one endpoint is loud and slow
- Any key rotation invalidates the campaign
- Server-side anti-DoS (per-source rate limiting, TLS handshake accounting) breaks the campaign long before completion
Recover session key or private key material
- Attack completes before key rotation
- Traffic of interest was recorded or is being recorded
- Ephemeral-only deployments have nothing to recover after the handshake
- Hybrid X25519+ML-KEM handshakes still require breaking X25519 to fully compromise traffic
The supporting signals.
| In-the-wild exploitation | None observed. No campaigns, no honeypot hits, no incident reports. This is a cryptographic implementation bug, not a network-reachable RCE. |
|---|---|
| Public PoC | No public exploit code. The C2SP/CCTV ML-KEM test vector suite catches this class of bug and was likely the discovery vector. |
| EPSS | 0.0013 (~0.13 percentile) — negligible predicted exploitation. |
| KEV status | Not listed. CISA KEV inclusion is unlikely absent in-the-wild proof. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N = 6.3 MEDIUM. C:L/I:L correctly captures 'partial confidentiality/integrity loss under CCA', not full key recovery. |
| Affected component | wolfSSL ML-KEM implementation, ARM64 NEON backend only. Generic C, AVX2, and non-ARM64 builds are unaffected. |
| Fixed version | wolfSSL fix landed in the release following 2026-06-25 disclosure — see wolfSSL advisory for the exact tag; distros should ship backports within 1-2 weeks. |
| Disclosure | 2026-06-25, CVE assigned by wolfSSL as CNA. |
| Reporter | Discovered via ML-KEM implementation testing (C2SP/CCTV vectors and internal wolfSSL crypto review). |
| Exposure population | Small: ARM64 targets running wolfSSL with NEON-enabled ML-KEM and *static* (not ephemeral) keys. Most TLS 1.3 / IKEv2 deployments use ephemeral KEM keys — no oracle, no attack. |
noisgate verdict.
The single decisive factor is that practical exploitation requires a chosen-ciphertext oracle against a reused static ML-KEM keypair — a configuration that is rare in the dominant deployment patterns (TLS 1.3, IKEv2 hybrid), and even where present is heavily rate-limited. The IND-CCA2 downgrade is real and CVSS C:L/I:L accurately captures a partial-confidentiality flaw, not a break-the-ciphertext primitive.
Why this verdict
- Chosen-ciphertext oracle required — attacker needs to observe decapsulation success/failure across many crafted ciphertexts. TLS 1.3 KDF and transcript hashing blind the oracle in the dominant PQ profile.
- Static key reuse required — ephemeral ML-KEM keys (the default in TLS 1.3 hybrid groups and IKEv2) eliminate the attack surface entirely. Only long-lived KEM keypairs (some IoT / VPN / bespoke messaging profiles) are exposed.
- Narrow build target — only wolfSSL ARM64 NEON path. x86_64, generic C, and AVX2 builds compare full ciphertext correctly.
- Role multiplier: identity/PKI role — wolfSSL is used inside some VPN concentrators and embedded IdP/certificate proxies. If one of these reuses a static ML-KEM key, chain outcome is session-key or long-term-key recovery. However, wolfSSL's ML-KEM deployment is still overwhelmingly ephemeral-key TLS 1.3, so the high-value-role installed base share is well below the 1% CRITICAL floor threshold.
- Role multiplier: embedded / IoT typical role — wolfSSL's dominant footprint is embedded devices where TLS handshakes may be volumetrically low and keys sometimes long-lived. Chain succeeds in theory, but query budgets of 10^5+ handshakes against a device are impractical and detectable.
- No amplifier evidence — EPSS 0.0013, no PoC, no KEV, no in-the-wild reports.
Why not higher?
HIGH would require either broad KEM-key-reuse deployments, a demonstrated efficient oracle in a common protocol profile, or active exploitation evidence. None of those exist today: TLS 1.3 and IKEv2 use ephemeral keys, the CCA query budget is enormous, and no PoC or campaign has surfaced.
Why not lower?
LOW / IGNORE is wrong because this is a genuine break of the FO transform's IND-CCA2 property on a real deployed code path, and there are wolfSSL profiles (embedded VPN, some messaging stacks) that DO reuse ML-KEM keys. Anywhere key reuse exists, the vendor's C:L/I:L framing understates the ceiling — full-key recovery is the theoretical endpoint.
What to do — in priority order.
- Disable the ARM64 NEON ML-KEM backend and fall back to generic C — Rebuild wolfSSL with
--disable-armasmor unsetWOLFSSL_ARMASM_CRYPTO_MLKEMon ARM64 targets until the patched version is deployed. Ship this within the 365-day remediation window; there is no noisgate mitigation SLA for MEDIUM verdicts. - Force ephemeral ML-KEM keys in every protocol profile you control — In TLS 1.3, ensure the hybrid group is negotiated per-handshake (default in mainline profiles). In IKEv2 with PQ additional key exchanges, verify KEM keys are not cached across SAs. Ephemeral key use eliminates the oracle entirely.
- Rate-limit and alert on repeated PQ handshake failures from a single source — The chosen-ciphertext attack needs 10^4–10^6 queries against one key. Standard TLS/IKE handshake anti-abuse (per-IP throttling, cookie challenges, failure-rate alerts) neutralizes the query budget.
- Inventory static ML-KEM key usage across VPN, messaging, and embedded fleets — Ask each product team: *does this deployment cache or persist an ML-KEM keypair across sessions?* Any 'yes' answer plus wolfSSL ARM64 NEON is your patch-first cohort.
- Rotating TLS server X.509 certs — the flaw is in the KEM ciphertext comparison, not in cert-based auth. Cert rotation does not rotate the ML-KEM keypair.
- Enabling FIPS mode — this bug is in FIPS-relevant ML-KEM code, so FIPS validation status alone does not protect you.
- WAF signatures on ClientHello — the malicious ciphertext bytes are indistinguishable at the byte level from valid ones; only volumetric anomaly detection helps.
Crowdsourced verification payload.
Run this on each wolfSSL-linked host (ARM64 targets in particular). Invoke as sudo ./check_wolfssl_mlkem.sh — needs read access to /usr/lib, /usr/local/lib, and the ability to run wolfssl-config if installed. Reports VULNERABLE if a wolfSSL library on ARM64 is present at a pre-fix version with NEON support compiled in.
#!/usr/bin/env bash
# noisgate: CVE-2026-6330 wolfSSL ML-KEM ARM64 NEON check
set -u
FIXED_MAJOR=5
FIXED_MINOR=8
FIXED_PATCH=0 # adjust to actual patched wolfSSL tag once published
arch=$(uname -m)
if [[ "$arch" != "aarch64" && "$arch" != "arm64" ]]; then
echo "PATCHED (host arch $arch is not affected — NEON path is ARM64-only)"
exit 0
fi
found=0
vuln=0
while IFS= read -r lib; do
found=1
# Extract version string from the shared object
ver=$(strings "$lib" 2>/dev/null | grep -Eo 'wolfSSL [0-9]+\.[0-9]+\.[0-9]+' | head -1 | awk '{print $2}')
if [[ -z "$ver" ]]; then
echo "UNKNOWN wolfSSL version in $lib"
continue
fi
IFS=. read -r MA MI PA <<< "$ver"
# Check NEON ML-KEM symbol presence
if nm -D "$lib" 2>/dev/null | grep -qiE 'mlkem.*neon|kyber.*neon|armasm.*mlkem'; then
if (( MA < FIXED_MAJOR )) || \
(( MA == FIXED_MAJOR && MI < FIXED_MINOR )) || \
(( MA == FIXED_MAJOR && MI == FIXED_MINOR && PA < FIXED_PATCH )); then
echo "VULNERABLE: $lib wolfSSL $ver (ARM64 NEON ML-KEM present, pre-fix)"
vuln=1
else
echo "PATCHED: $lib wolfSSL $ver"
fi
else
echo "PATCHED: $lib wolfSSL $ver (no NEON ML-KEM symbols)"
fi
done < <(find /usr/lib /usr/local/lib /opt -name 'libwolfssl*.so*' 2>/dev/null)
if (( found == 0 )); then
echo "UNKNOWN: no libwolfssl found on standard paths"
exit 2
fi
exit $(( vuln ))
If you remember one thing.
Sources
- CVE-2026-6330 (THREATINT)
- CVE-2026-6330 (TheHackerWire)
- wolfSSL Security Vulnerabilities advisory index
- C2SP/CCTV ML-KEM test vector suite
- NIST FIPS 203 (ML-KEM specification)
- Failing gracefully: Decryption failures and the FO transform
- Binding Security of Implicitly-Rejecting KEMs
- filippo.io — Post-quantum Cryptography for the Go Ecosystem (background on ML-KEM CCA)
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.