Skip to content

Conversation

haimaychao
Copy link
Contributor

@haimaychao haimaychao commented Oct 2, 2025

Implement hybrid key exchange support for TLS 1.3 by adding three post-quantum hybrid named groups: X25519MLKEM768, SecP256r1MLKEM768, and SecP384r1MLKEM1024.
Please see JEP 527 for details about this change.


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Integration blocker

 ⚠️ Title mismatch between PR and JBS for issue JDK-8314323

Issue

  • JDK-8314323: Implement JEP 527: TLS 1.3 Hybrid Key Exchange (Enhancement - P2) ⚠️ Title mismatch between PR and JBS.

Contributors

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/27614/head:pull/27614
$ git checkout pull/27614

Update a local copy of the PR:
$ git checkout pull/27614
$ git pull https://git.openjdk.org/jdk.git pull/27614/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 27614

View PR using the GUI difftool:
$ git pr show -t 27614

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/27614.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Oct 2, 2025

👋 Welcome back hchao! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Oct 2, 2025

❗ This change is not yet ready to be integrated.
See the Progress checklist in the description for automated requirements.

@openjdk
Copy link

openjdk bot commented Oct 2, 2025

@haimaychao The following label will be automatically applied to this pull request:

  • security

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command.

@haimaychao
Copy link
Contributor Author

/contributor add @jnimeh

@haimaychao
Copy link
Contributor Author

/contributor add @wangweij

@openjdk
Copy link

openjdk bot commented Oct 2, 2025

@haimaychao
Contributor Jamil Nimeh <[email protected]> successfully added.

@openjdk
Copy link

openjdk bot commented Oct 2, 2025

@haimaychao
Contributor Weijun Wang <[email protected]> successfully added.

@haimaychao haimaychao marked this pull request as ready for review October 2, 2025 23:59
@openjdk openjdk bot added the rfr Pull request is ready for review label Oct 2, 2025
@mlbridge
Copy link

mlbridge bot commented Oct 3, 2025

Webrevs

algParams.init(keAlgParamSpec);
// Skip AlgorithmParameters for KEMs (not supported)
if (namedGroupSpec == NamedGroupSpec.NAMED_GROUP_KEM) {
if (defaultProviderName == null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We assume that if provider is not null then it must be DH without doing any checks to confirm that. It would be cleaner to call getProvider() instead.

Provider p = getProvider();
if (p == null) {
KeyFactory.getInstance(name);
} else {
KeyFactory.getInstance(name, p);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


private static KEM getKEM(String name) throws NoSuchAlgorithmException {
if (name.startsWith("secp") || name.equals("X25519") ||
name.equals("X448")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need X448 here? Also, please provide a comment for this method with functionality description.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

X448 removed, and comment added.

X25519(32, 32,
"XDH", "XDH", NamedParameterSpec.X25519),

X448(56, 56,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need X448 and P521?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need, no. Want, yes. The support for traditional curves that are not part of the first round of hybrid KEMs lays the groundwork for future hybrid KEMs that might use these larger curves. It also gives us the base framework to move these algorithms as named groups to KEM implementations in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks for the explanation! I guess it makes sense if we expect those curves to be used in the future rounds of hybrid KEM.

implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi {
private final PublicKey pkR;
private final PrivateKey skR;
private final AlgorithmParameterSpec spec;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is not being used, it can be removed together with the constructor parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed variable spec.

}
} else if (k instanceof XECKey xkey
&& xkey.getParams() instanceof NamedParameterSpec ns) {
if (ns.getName().equalsIgnoreCase("X25519")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use NamedParameterSpec.X25519?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

ProtocolVersion.PROTOCOLS_TO_13,
PredefinedDHParameterSpecs.ffdheParams.get(8192)),

ML_KEM_512(0x0200, "MLKEM512",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are they needed for this Jep?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We added ML-KEM NamedGroups with null AlgorithmParameterSpec, and they won’t appear as negotiable named groups. They were added to support debug display and recognition of MLKEM named groups when used in the key share, so we can see them in debug and know if they are used. It'd help for interop debugging/testing.

// Finite Field Groups (XDH)
NAMED_GROUP_XDH("XDH", XDHScheme.instance),

NAMED_GROUP_KEM("PQC", KEMScheme.instance),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That Choice of Name needs probably an explaining comment if it is for pure PQC and/ormhybrid?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment added.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can rename "PQC" to "KEM" to be consistent.

} else { // default groups
NamedGroup[] groups = new NamedGroup[] {

// Hybrid key agreements
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like all the infra for X448MLKEM1024 is there, so rather than removing x448 from this patch, why not implement it (it’s more obvious than P511 Variants)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed the infrastructure is there, but I don't see an IETF draft that covers that hybrid variant for TLS, nor do I see an IANA mapping for it here: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8

There needs to be a standard for TLS 1.3 backing these hybrid KEMs before we implement them.

FFDHE_2048,
FFDHE_3072,
FFDHE_4096,
FFDHE_6144,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the choise to knock out ffdhe6144 and 8192 from the default list was done on purpose. I don't think they get much use and they can always be re-enabled via SSLParameters or the system property. We're open to feedback on this if you or others feel like they should remain in place, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change is I think ok, doesn’t make much of a difference for most cases I was just thinking it needed its own commit and ticket reference but if it was intentional fine as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you make a fair point here. It probably deserves its own change, JBS entry, CSR, etc. We'll leave them in for now.

}

private static AlgorithmParameterSpec getSpec(String name) {
if (name.startsWith("secp")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be repeated multiple times including the case sensitive string, maybe have an APS.isGenericEC() helper?


private static int leftPublicLength(String name) {
return switch (name.toLowerCase(Locale.ROOT)) {
case "secp256r1" -> 65;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn’t that Part formte named groups, maybe Look it up instead?


private static byte[] concat(byte[]... inputs) {
ByteArrayOutputStream o = new ByteArrayOutputStream();
Arrays.stream(inputs).forEach(o::writeBytes);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really want a non presized buffer and a stream in the handshake hot path?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use presized buffer.

* @run main/othervm -Djdk.tls.namedGroups="secp384r1"
DisabledCurve DISABLE_NONE PASS
* @run main/othervm -Djdk.tls.namedGroups="secp384r1"
DisabledCurve secp384r1 FAIL
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test case fails whether we specify -Djdk.tls.namedGroups="secp384r1" or not, because the test certificate also uses secp384r1 algorithm in the signature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rfr Pull request is ready for review security [email protected]
Development

Successfully merging this pull request may close these issues.

5 participants