Add ML-KEM (FIPS 203) and ML-DSA (FIPS 204) support#361
Add ML-KEM (FIPS 203) and ML-DSA (FIPS 204) support#361cconlon wants to merge 2 commits intowolfSSL:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds TLS 1.3 post-quantum support to wolfssljni/wolfJSSE by introducing ML-KEM named groups for key exchange and ML-DSA certificate authentication, surfaced via standard JSSE configuration paths and backed by JNI/native feature detection and key-share helpers.
Changes:
- Added ML-KEM named-group constants/parsing + TLS 1.3 key_share pre-generation (
useKeyShare) and configuration viaSSLParameters.setNamedGroups()/jdk.tls.namedGroups/wolfjsse.enabledSupportedCurves. - Added ML-DSA certificate authentication support paths (incl. PKCS#8 handling and supported signature algorithm advertising).
- Added extensive integration/unit tests plus example scripts and generated PQC test artifacts.
Reviewed changes
Copilot reviewed 53 out of 80 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/test/com/wolfssl/test/WolfSSLTest.java | Adds unit tests for PQC named-group constants, parsing, and feature-detect JNI calls. |
| src/test/com/wolfssl/test/WolfSSLSessionTest.java | Adds tests for useKeyShare() behavior and useSupportedCurves() aggregate-failure semantics. |
| src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java | Adjusts test harness to handle larger PQC handshake flights and adds opt-in extra debug. |
| src/test/com/wolfssl/provider/jsse/test/WolfSSLPQCTestUtil.java | New PEM-based KeyManager/TrustManager helpers to run ML-DSA tests on older JDKs. |
| src/test/com/wolfssl/provider/jsse/test/WolfSSLPQCKeyExchangeTest.java | New end-to-end SSLEngine ML-KEM named-group negotiation tests. |
| src/test/com/wolfssl/provider/jsse/test/WolfSSLPQCAuthenticationTest.java | New end-to-end ML-DSA authentication tests + signature scheme formatting test. |
| src/test/com/wolfssl/provider/jsse/test/WolfSSLPQCAuthKeyStoreTest.java | New JDK 24+ keystore-based ML-DSA authentication tests (JKS/PKCS12). |
| src/test/com/wolfssl/provider/jsse/test/WolfSSLJSSETestSuite.java | Adds PQC test classes to the main JSSE test suite. |
| src/test/com/wolfssl/provider/jsse/test/WolfSSLCNSA2Test.java | New composite CNSA 2.0 compliance integration tests (kex/auth/cipher/protocol). |
| src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java | Adds ML-DSA signature-scheme mapping and jdk.tls.namedGroups parsing helper. |
| src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java | Reflective get/set for SSLParameters.setNamedGroups() (JDK 20+) and propagation. |
| src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java | Copies named-groups across parameter cloning (reflection-based for Java 8 baseline). |
| src/java/com/wolfssl/provider/jsse/WolfSSLInternalVerifyCb.java | Fixes authType detection for ML-DSA vs DSA substring collision; minor formatting. |
| src/java/com/wolfssl/provider/jsse/WolfSSLImplementSSLSession.java | Advertises ML-DSA signature algorithms in local supported signature alg list. |
| src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java | Applies supported_groups config sources, filters PQC groups without TLS 1.3, and pre-generates PQC key shares. |
| src/java/com/wolfssl/WolfSSLSession.java | Adds useKeyShare() JNI binding and improves useSupportedCurves() error aggregation. |
| src/java/com/wolfssl/WolfSSL.java | Adds ML-KEM named-group constants, PQC feature-detect JNI APIs, public named-group parsing, and PQC-group classification helper. |
| native/com_wolfssl_WolfSSLSession.h | Declares JNI binding for WolfSSLSession.useKeyShare. |
| native/com_wolfssl_WolfSSLSession.c | Implements JNI useKeyShare via wolfSSL_UseKeyShare() with TLS 1.3 gating. |
| native/com_wolfssl_WolfSSLCertificate.c | Maps native ML-DSA signature types to JSSE-visible algorithm strings. |
| native/com_wolfssl_WolfSSL.h | Declares JNI PQC feature-detect APIs and ML-KEM named-group constants. |
| native/com_wolfssl_WolfSSL.c | Implements JNI PQC feature-detect APIs (PQCEnabled, MLKEMEnabled, MLDSAEnabled, MLKEMOldIdsEnabled). |
| examples/server.sh | Documents PQC usage patterns for the non-JSSE server wrapper script. |
| examples/provider/update-keystore-pqc.sh | New script to generate ML-DSA JKS/PKCS12 keystores with JDK 24+ keytool. |
| examples/provider/ServerJSSE.sh | Documents PQC usage patterns for JSSE server wrapper script. |
| examples/provider/ServerJSSE.java | Adds -pqc named-group restriction support for JSSE server example. |
| examples/provider/ClientJSSE.sh | Documents PQC usage patterns for JSSE client wrapper script. |
| examples/provider/ClientJSSE.java | Adds -pqc named-group restriction support for JSSE client example. |
| examples/client.sh | Documents PQC usage patterns for the non-JSSE client wrapper script. |
| examples/certs/pqc/server-mldsa87-priv.pem | Adds generated ML-DSA-87 server private key test artifact. |
| examples/certs/pqc/server-mldsa65-priv.pem | Adds generated ML-DSA-65 server private key test artifact. |
| examples/certs/pqc/server-mldsa44.pem | Adds generated ML-DSA-44 server certificate test artifact. |
| examples/certs/pqc/server-mldsa44-priv.pem | Adds generated ML-DSA-44 server private key test artifact. |
| examples/certs/pqc/root-mldsa87-priv.pem | Adds generated ML-DSA-87 root private key test artifact. |
| examples/certs/pqc/root-mldsa65-priv.pem | Adds generated ML-DSA-65 root private key test artifact. |
| examples/certs/pqc/root-mldsa44.pem | Adds generated ML-DSA-44 root certificate test artifact. |
| examples/certs/pqc/root-mldsa44-priv.pem | Adds generated ML-DSA-44 root private key test artifact. |
| examples/certs/pqc/client-mldsa87-priv.pem | Adds generated ML-DSA-87 client private key test artifact. |
| examples/certs/pqc/client-mldsa65-priv.pem | Adds generated ML-DSA-65 client private key test artifact. |
| examples/certs/pqc/client-mldsa44.pem | Adds generated ML-DSA-44 client certificate test artifact. |
| examples/certs/pqc/client-mldsa44-priv.pem | Adds generated ML-DSA-44 client private key test artifact. |
| examples/certs/gen-mldsa-certs.sh | New OpenSSL-based generator for ML-DSA cert chains used by tests/examples. |
| examples/Server.java | Adds -pqc support and key_share pre-generation in the non-JSSE server example. |
| examples/README.md | Documents how to run PQC TLS 1.3 examples and requirements/limitations. |
| examples/Client.java | Adds -pqc support and key_share pre-generation in the non-JSSE client example. |
| README.md | Updates property docs for PQC named groups, signature schemes, and jdk.tls.namedGroups. |
Comments suppressed due to low confidence (11)
src/java/com/wolfssl/WolfSSL.java:1
- With
getNamedGroupFromStringnowpublic, it should be resilient to common inputs. As written,switch (curveName)will throw NPE on null and will not handle leading/trailing whitespace, which is easy to introduce via properties (e.g.,jdk.tls.namedGroups). Consider adding a null/trim normalization at the top (and optionally case normalization where safe) and returningWOLFSSL_NAMED_GROUP_INVALIDfor null/unparseable values instead of throwing.
native/com_wolfssl_WolfSSLSession.c:1 - JNI
useKeySharecasts the Javaint grouptoword16without validating range/sign. Negative values or values > 65535 will wrap and can select an unintended group ID, producing incorrect behavior. Mandatory: validategroupin JNI (and/or in the Java wrapper) to ensure it is within[0, 65535]and returnBAD_FUNC_ARG(or throwIllegalArgumentExceptionon the Java side) when invalid.
examples/provider/update-keystore-pqc.sh:1 - The script uses predictable
/tmppaths derived from$$for probe keystores and the scratch working directory. This is vulnerable to symlink/hardlink attacks and can also collide in some environments. Prefermktemp/mktemp -dfor both the probe file andwork, and add atrapto ensure cleanup on early exit.
examples/provider/update-keystore-pqc.sh:1 - The script uses predictable
/tmppaths derived from$$for probe keystores and the scratch working directory. This is vulnerable to symlink/hardlink attacks and can also collide in some environments. Prefermktemp/mktemp -dfor both the probe file andwork, and add atrapto ensure cleanup on early exit.
examples/provider/update-keystore-pqc.sh:1 - The script uses predictable
/tmppaths derived from$$for probe keystores and the scratch working directory. This is vulnerable to symlink/hardlink attacks and can also collide in some environments. Prefermktemp/mktemp -dfor both the probe file andwork, and add atrapto ensure cleanup on early exit.
src/test/com/wolfssl/provider/jsse/test/WolfSSLPQCTestUtil.java:1 - This utility does a lot of manual I/O boilerplate (explicit
close(), manual read loop, charset by name). For more robust and concise test code, consider switching to try-with-resources andFiles.readAllBytes(...), and preferStandardCharsets.UTF_8. Also, the current read loop doesn’t verify that the file was fully read (off == all.length), which can silently produce truncated PEM parsing if a short read occurs.
src/test/com/wolfssl/provider/jsse/test/WolfSSLPQCTestUtil.java:1 - This utility does a lot of manual I/O boilerplate (explicit
close(), manual read loop, charset by name). For more robust and concise test code, consider switching to try-with-resources andFiles.readAllBytes(...), and preferStandardCharsets.UTF_8. Also, the current read loop doesn’t verify that the file was fully read (off == all.length), which can silently produce truncated PEM parsing if a short read occurs.
src/test/com/wolfssl/provider/jsse/test/WolfSSLPQCTestUtil.java:1 - This utility does a lot of manual I/O boilerplate (explicit
close(), manual read loop, charset by name). For more robust and concise test code, consider switching to try-with-resources andFiles.readAllBytes(...), and preferStandardCharsets.UTF_8. Also, the current read loop doesn’t verify that the file was fully read (off == all.length), which can silently produce truncated PEM parsing if a short read occurs.
src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java:1 jdk.tls.namedGroupsparsing only removes the exact substring\", \"and doesn’t trim individual tokens. Inputs like\"X25519MLKEM768, secp256r1\"(double spaces) or\"X25519MLKEM768,\tsecp256r1\"will leave whitespace and likely cause group lookup failures downstream. Consider trimming each token (or splitting on a regex like\\s*,\\s*) to make property parsing robust.
src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java:1jdk.tls.namedGroupsparsing only removes the exact substring\", \"and doesn’t trim individual tokens. Inputs like\"X25519MLKEM768, secp256r1\"(double spaces) or\"X25519MLKEM768,\tsecp256r1\"will leave whitespace and likely cause group lookup failures downstream. Consider trimming each token (or splitting on a regex like\\s*,\\s*) to make property parsing robust.
src/test/com/wolfssl/provider/jsse/test/WolfSSLPQCAuthenticationTest.java:1- The comment below the test references “JEP 527” elsewhere in this block (ML-DSA is JEP 497 per this PR description). Please correct the JEP reference to avoid confusing future maintainers about which JDK change introduced ML-DSA.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 53 out of 80 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #361
Scan targets checked: wolfssljni-bugs, wolfssljni-src
Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
…A (FIPS 204) support and tests
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 54 out of 81 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (5)
examples/certs/pqc/server-mldsa87-priv.pem:1
- The PR adds multiple ML-DSA private keys to the repository under examples/certs/pqc/. Even if intended for testing, committing private key material is a security risk (accidental reuse, downstream consumption, and secret-scanning policy violations) and also increases repo blast radius. Prefer removing the *-priv.pem files from version control and generating them at test/CI time (e.g., run gen-mldsa-certs.sh and update-keystore-pqc.sh in CI before PQC tests), keeping only public certs (or generating everything on-demand and skipping when absent).
src/java/com/wolfssl/provider/jsse/WolfSSLUtil.java:1 jdk.tls.namedGroupsparsing is fragile: it only removes the exact sequence ", " and will leave other whitespace (leading/trailing spaces, tabs, multiple spaces) in tokens. That can cause group names like "secp256r1" to become " secp256r1" and fail resolution downstream. Consider splitting on a whitespace-tolerant delimiter (e.g., trim each token or split on "\\s*,\\s*") and dropping empty tokens.
src/java/com/wolfssl/WolfSSLSession.java:1- This introduces a nested
synchronized (sslLock)inside an outersynchronized (sslLock). Java locks are re-entrant so it works, but the inner block is redundant and obscures intent. Removing the inner synchronization (or restructuring so there is only one lock acquisition per call) will simplify the control flow and avoid confusion during future changes.
src/test/com/wolfssl/provider/jsse/test/WolfSSLPQCTestUtil.java:1 - Unused import in this new test utility (
ByteArrayInputStream) should be removed to keep the file clean and avoid checkstyle/IDE warnings.
src/test/com/wolfssl/provider/jsse/test/WolfSSLPQCTestUtil.java:1 - This PEM reader assumes
f.length()fits in an int and that reading fills the buffer fully; if a short read occurs, the remaining bytes stay as zeros and can corrupt the Base64 payload. It would be more robust to read using a streaming approach (e.g.,Files.readAllBytes/ByteArrayOutputStream) and to useStandardCharsets.UTF_8instead of the string charset name.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
This PR adds TLS 1.3 post-quantum support to wolfssljni / wolfJSSE with ML-KEM (FIPS 203) named groups for key exchange, and ML-DSA (FIPS 204) X.509 certificate authentication. Available through standard JSSE APIs (
SSLContext,SSLEngine,SSLSocket,KeyManagerFactory,TrustManagerFactory).Feature additions
ML-KEM key exchange (FIPS 203) for TLS 1.3 - selectable through the standard JSSE configuration mechanisms, in this precedence order:
SSLParameters.setNamedGroups()(JDK 20+, JEP 452) - per-engine, takes precedence when set.jdk.tls.namedGroupsSystem property - JVM-wide. (JSSE convention is client-side only; wolfJSSE follows that.)wolfjsse.enabledSupportedCurvesSecurity/system property - wolfJSSE-specific fallback that applies to both client and server, useful when running on a JDK older than 20 or when configuring server-side selection without per-engine code.Supported named groups:
ML-KEM-512,ML-KEM-768,ML-KEM-1024. Require native--enable-tls-mlkem-standalone.SECP256R1MLKEM768(0x11EB),X25519MLKEM768(0x11EC),SECP384R1MLKEM1024(0x11ED, CNSA 2.0 level).SECP256R1MLKEM512,SECP384R1MLKEM768,SECP521R1MLKEM1024,X25519MLKEM512,X448MLKEM768.ML-DSA cert authentication (FIPS 204) - mutual auth across all three parameter sets:
ML-DSA-44(Category 2)ML-DSA-65(Category 3)ML-DSA-87(Category 5, CNSA 2.0 mandated)Loaded through standard JCE keystores (JKS + PKCS12, JDK 24+ via JEP 497). Works on Java 8 too via a PEM-based KeyManager helper, since older JDKs have no ML-DSA
KeyFactory. NewloadKeyAndCertChainbranch inWolfSSLEngineHelperpasses the full PKCS#8 wrapper through for ML-DSA so native dispatches to the right parameter set.Examples
The bundled
ServerJSSE/ClientJSSEexamples add a-pqc <named-group>flag and accept ML-DSA keystores via the existing-c(entity cert/key) and-A(trusted CA) flags.examples/certs/gen-mldsa-certs.sh- produces ML-DSA-{44,65,87} root + server + client cert chains.examples/provider/update-keystore-pqc.sh- producesserver-,client-, andca-mldsa<N>.{jks,p12}keystores (JDK 24+ keytool) for all three parameter sets. Server and client entity certs share one root per level so a singleca-mldsa<N>truststore validates both sides.Native wolfSSL requirements
--enable-mlkem --enable-mldsa. Add--enable-tls-mlkem-standalonefor the pureML-KEM-512/768/1024groups (PQ/T hybrids work without it). For full coverage of every named group above, also include--enable-curve25519(forX25519MLKEM*) and--enable-curve448(forX448MLKEM768).d2i_PUBKEY/d2i_PrivateKey). ML-KEM key exchange works on earlier wolfssl versions (ie: 5.9.1).