Skip to content

Add support for hybrid key exchange protocol x25519mlkem768#6107

Merged
vietj merged 21 commits into
eclipse-vertx:masterfrom
anavarr:master
May 20, 2026
Merged

Add support for hybrid key exchange protocol x25519mlkem768#6107
vietj merged 21 commits into
eclipse-vertx:masterfrom
anavarr:master

Conversation

@anavarr

@anavarr anavarr commented May 6, 2026

Copy link
Copy Markdown
Contributor

Motivation:
The rise of quantum computers threatens traditional asymmetric key exchange protocol due to their ability to break private keys.
Post-quantum cryptography must use problems that quantum computers can't solve as quickly. Module-lattice-based problems have been found to resist quantum computers. ML-KEM, for module-lattice-based key encapsulation mechanism, is an instance of a key exchange protocol resistant to quantum computers. Due to its relative short existence, it has been recommended to use it alongside traditional Diffie-Hellman with elliptic curve.
Thus, the new hybrid key exchange protocol x25519mlkem768 uses both Diffie-Hellman with elliptic curve 25519 and ML-KEM. Its has been integrated in OpenSsl starting with version 3.5.

Changes:
This features relies on the netty-tcnative-openssl-dynamic bound to version 3.6 of openssl at runtime, and netty-tcnative-classes at build-time.

  • Add boolean useHybrid field to io.vertx.core.net.SSLOptions and create getters/setters in io.vertx.core.net.SSLOptions as well as every implementation of io.vertx.core.net.TCPSSLOptions.
  • If this value is set to true (default false), the ssl handler will be set to use x25519mlkem768 instead of x25519.
  • If key exchange protocol is set to x25519mlkem768 but JdkSsl is used instead of OpenSsl, or the version of openssl used at runtime does not suppot x25519mlkem768, the user is informed that hybrid key exchange is impossible, then SSL handshake fails

@anavarr

anavarr commented May 6, 2026

Copy link
Copy Markdown
Contributor Author

@vietj

@zekronium

Copy link
Copy Markdown
Contributor

We ported something similar ourselves by bumping tcnatuve, are the extra methods really needed instead of simply setting/adding the curve to sslconfig when creating netclient/httpclient

@vietj vietj added this to the 5.1.0 milestone May 11, 2026
Comment thread vertx-core/src/main/java/io/vertx/core/net/ServerSSLOptions.java Outdated
Comment thread vertx-core/src/main/java/io/vertx/core/net/SSLOptions.java Outdated
Comment thread vertx-core/src/main/java/io/vertx/core/net/SSLOptions.java Outdated
Comment thread vertx-core/src/test/java/io/vertx/it/HybridKeyExchangeTest.java Outdated
Comment thread vertx-core/src/test/java/io/vertx/it/HybridKeyExchangeTest.java Outdated
@anavarr anavarr marked this pull request as draft May 19, 2026 08:16
@anavarr anavarr marked this pull request as ready for review May 19, 2026 14:59
@vietj vietj merged commit b11ff15 into eclipse-vertx:master May 20, 2026
12 of 15 checks passed
@vietj

vietj commented May 20, 2026

Copy link
Copy Markdown
Member

thanks for the great contribution @anavarr

@vietj vietj self-assigned this May 20, 2026
@NilsRenaud

Copy link
Copy Markdown
Contributor

Now that JDK 27 introduced x25519MLKEM768 doesn't Vert.x support it by default when running on JDK 27 ?

I don't really get why we can't "simply" follow this doc and do (either using OpenSSL, JDK27 or BouncyCastle, though the naming of the algorithm may change I guess):

ServerSSLOptions options = new ServerSSLOptions().
  setKeyCertOptions(keyStoreOptions).
  addEnabledCipherSuite("X25519MLKEM768") // if we really want to only permit this algorithm, otherwise will be part of the allowed algorithms

Have I missed something @vietj @anavarr ?

@anavarr

anavarr commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Sorry for answering so late.

I don't really get why we can't "simply" follow this doc and do (either using OpenSSL, JDK27 or BouncyCastle, though the naming of the algorithm may change I guess):

It is a bit more involved. Using OpenSSL is possible through netty-tcnative. Netty-tcnative-classes provides Java classes with native method declarations (JNI). Netty-tcnative-boringssl-static provides the native implementation of those methods and bundles BoringSSL within the JAR. A dynamic variant would instead link against the host's SSL library at runtime.

However, netty-tcnative provides neither a static version of openssl>=3.5, nor a dynamic one working with OpenSSL >= 3.5, hence the need to maintain smallrye-openssl ourselves. This artifact provides a dynamic version of netty-tcnative-openssl that can be used with versions of OpenSSL >= 3.5 (the artifacts provided by netty-tcnative-openssl-dynamic only work against OpenSSL 1.x).

Regarding JDK27, my guess is that we need a PQC solution quickly, and we can't expect Vert.x users to run JDK27 (which will be GA in September) right now?

@zekronium

zekronium commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Even with a smallrye requirement, just like for most features tcnative is a requirement and thats quite obvious with the engineOptions, aren't all the extra code changes for just adding addEnabledCipherSuite("X25519MLKEM768") or SSL.setCurvesList(engine.sslPointer(), "X25519MLKEM768"); quite alot

Imho, why not simply add an option to setCurves in the engineOptions instead

@anavarr

anavarr commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

We actually didn't do much more than adding an option on SSLOptions, as well as getters and setters. We added it there because there might be other SSL engines supporting PQC in the future and we didn't want to re-wire everything everytime an engine adds support.
There are many hooks on SSLOptions subclasses due to fluent API so we had to rewrite most getters/setters for every subclass but that's pretty much it. All those new methods simply set a boolean that eventually decides if we setCurvesList(engine.sslPointer(), "X25519MLKEM768") or not.
Also worth noting: X255619MLKEM768 is not a cipher suite but a key exchange protocol ('named group' in the ssl API).

@zekronium

Copy link
Copy Markdown
Contributor

Maybe we should also simply add a method directly in OpenSSLEngineOptions (and others if allowed) to set curvesList so similar changes in the future are plain configurable instead of keeping the curvesList hidden in internals

@NilsRenaud

Copy link
Copy Markdown
Contributor

FYI I just tried to run a simple Vert.x client/server using JDK 27 EA and they automatically end up using TLSv1.3 with X5519MLKEM768 as named group.

(for the record you can see this by adding -Djavax.net.debug=ssl:handshake when running your test)

Indeed its GA is expected in September but that's not that far away, and setUseHybridKeyExchangeProtocol at the global SSLOption level may be misleading in the future, so I agree with @zekronium, we probably should add this method directly within OpenSSLEngineOptions

@vietj

vietj commented Jun 16, 2026

Copy link
Copy Markdown
Member

we'll keep an eye on that, thanks for the feedback

@zekronium

zekronium commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

If people are aware they even need this, they already know what curve/group to set. I think actually exposing the proper control lets people control many things, not just this.

alot of LOC for one use, where I feel simply curves list setting should of been exposed

Additionally this invents “custom” ways to do things over such a standardised thing as SSL. Everyone knows universally no matter what language or SSL library you use, the language is the same, cipher lists, name groups, signing algorithms. All the setters are practically universal. Now someone goes to vertx and wants to set this from another language, he has to read the docs and find this special toggle instead of vertx sticking to convention.

@NilsRenaud

Copy link
Copy Markdown
Contributor

FYI, I've created an issue on Netty to add nameGroup support in their SslContextBuilder: netty/netty#16999 so vert.x could then have a similar configuration item.

@anavarr

anavarr commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

A new API was proposed here.
The current useHybridKeyExchange is removed and replaced by two new options:

  • key exchange groups
  • PQC enforcement policy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants