Hi,

Thanks for taking the time to review the APIs and send us your feedback.

On 7/17/26 4:02 PM, Arpan Sharma wrote:
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.
FYI, this doesn't completely address your concern, but In JDK 28, we added a JFR event that captures the negotiated named group [1] and is also logged at the DEBUG level.
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.)
You probably already know this, but you can set only PQC or hybrid named groups if you don't want classical groups to be 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:
Were you setting (requesting) *only* a pure ML-KEM group? Or did you also set X25519? If the latter, this is correct behavior per the TLS 1.3 RFC. The server chose one of the groups the client supports. It does not have to fail if the client's most preferred group is not selected. It does not have to fail if it doesn't support one of the groups.
   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.
I think it would be useful to add an API to get a provider's supported groups, ala SSLSocket.getSupportedCipherSuites. This would allow an application to check what is supported and set the ones it wants to use before starting a handshake. Throwing an exception on an invalid protocol is clearly different than an invalid or unsupported group. Different providers may support different groups, or even custom ones. Allowing an application to be flexible and not tied to one provider is a plus.
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?
I'm not sure what you mean by security-policy parameter. As mentioned before, if you want to guarantee pure ML-KEM is used, you should just specify that and nothing else.
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?
No, see above. I would be ok with adding an API to get the supported groups though (see above).
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.
Yes, I think it would be useful to add an API to SSLSession or ExtendedSSLSession that returns the group that was negotiated.

I have filed an RFE to add these two APIs [2].
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

Sorry, but I cannot review code that is outside of openjdk.

--Sean

[1] https://bugs.openjdk.org/browse/JDK-8301626

[2] https://bugs.openjdk.org/browse/JDK-8388519

Reply via email to