Hello, I've been measuring post-quantum TLS key exchange on the JVM and ran into a behaviour I'd like to raise as a specification question rather than an implementation bug, since the current behaviour conforms to its documentation. I've also
Hello,
I've been measuring post-quantum TLS key exchange on the JVM and ran into a
behaviour I'd like to raise as a specification question rather than an
implementation bug, since the current behaviour conforms to its documentation.
I've also filed it via bugreport.java.com (reference 38bc5adc-fe85-492c-a579-
ce0444a8e58e).
THE BEHAVIOUR
An application that calls SSLParameters.setNamedGroups() with a named group the
provider does not support has that group silently dropped. The TLS handshake
proceeds on a classical group, no exception surfaces, nothing is logged at
default levels, and no public API reveals which group was actually negotiated.
The only signal is a DEBUG line under -Djavax.net.debug.
This is documented -- the setNamedGroups() spec (added by JDK-8291950 in JDK 20)
states that providers "should ignore unknown named group scheme names" -- so I'm
raising the contract, not the implementation.
WHY IT MATTERS NOW
Under CNSA 2.0 and NIST IR 8547 timelines, the natural Java migration step is
exactly this call: setNamedGroups() with a post-quantum group first in
preference
order. When that group is unsupported on the running JDK, the
application deploys
believing it has post-quantum key exchange while every connection silently falls
back to classical ECDH -- the precise harvest-now-decrypt-later exposure the
migration was meant to close. Because no public API exposes the negotiated named
group, this is undetectable at runtime by the application, by health checks, or
by compliance tooling. (Verified: after a handshake that negotiated x25519,
SSLParameters.getNamedGroups() still returns the requested "MLKEM768,x25519" --
it reports what was set, not what was used.)
JEP 527 DOES NOT RESOLVE IT
I tested JDK 27-ea+30-2302 alongside 26.0.1+8-34. JDK 27 correctly negotiates
X25519MLKEM768 -- JEP 527 works as intended. But the silent-drop behaviour
persists for every group outside its scope. Requesting the pure-ML-KEM group
MLKEM768 on 27-ea: it is recognised as a defined-but-disabled group and still
silently discarded, the handshake completing on x25519 with no error:
JDK 26.0.1: SupportedGroupsExtension.java:181
"Ignore unspecified named group: X25519MLKEM768"
JDK 27-ea: SupportedGroupsExtension.java:200
"Ignore inactive or disabled named group: MLKEM768"
A control case with a nonexistent group name ("NoSuchGroup12345") is dropped
identically on both JDKs, so the behaviour is generic rather than PQC-specific.
AN INCONSISTENCY WORTH NOTING
The setNamedGroups() spec already throws IllegalArgumentException when a named
group entry is a duplicate, null, or blank -- but only "ignores" one that is
unknown/unsupported. And a comparable security-policy setter fails loudly:
sslSocket.setEnabledProtocols(new String[]{"TLSv9.9"})
-> IllegalArgumentException: Unsupported protocol: TLSv9.9 (both JDKs)
So the API already distinguishes invalid input (throw) from valid input, and a
sibling setter rejects unsupported values outright; only an
unsupported-but-well-
formed named group is silently accepted and then dropped.
QUESTIONS FOR THE LIST
1. Is the "providers should ignore unknown named group scheme names" contract
still considered correct, now that named groups are a security-policy
parameter (post-JEP-527) rather than a performance hint?
2. Would a CSR revising it be welcome -- either throwing
IllegalArgumentException
for an unsupported group (consistent with setEnabledProtocols, and with the
existing duplicate/null/blank handling), or at minimum emitting a platform-
logger warning when a requested group is dropped?
3. Is there interest in exposing the negotiated named group through a public API
(e.g. on ExtendedSSLSession), so applications can verify their key-exchange
posture? Today there is no way to do so short of parsing debug output or
reflecting into sun.security.ssl internals.
REPRODUCER AND DATA
A single-file, dependency-free reproducer runs on a stock JDK in one command
(no server, no network); the full probe harness, pinned environment with build
strings, verbatim javax.net.debug traces, and the complete results matrix across
JDK 26 GA and 27-ea are here:
https://github.com/Arpan0995/jdk-tls-namedgroup-silent-drop
Happy to do further testing if it would help.
Regards,
Arpan Sharma