Skip to content

Commit 2dcf59e

Browse files
committed
Add support for hybrid key exchange protocol x25519mlkem768 for making TLS sessions quantum safe.
This features relies on the netty-tcnative-openssl-dynamic bound to version 3.6 of openssl. - 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 channel is closed
1 parent 158cf20 commit 2dcf59e

21 files changed

+226
-22
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@
204204
<artifactId>log4j-core</artifactId>
205205
<scope>test</scope>
206206
</dependency>
207+
<dependency>
208+
<groupId>io.netty</groupId>
209+
<artifactId>netty-tcnative-classes</artifactId>
210+
<version>2.0.76.Final</version>
211+
</dependency>
207212
<dependency>
208213
<groupId>io.netty</groupId>
209214
<artifactId>netty-tcnative-boringssl-static</artifactId>

src/main/asciidoc/http.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ To handle `h2` requests, TLS must be enabled along with {@link io.vertx.core.htt
4141
{@link examples.HTTP2Examples#example0}
4242
----
4343

44+
The rise of quantum computers will make key exchange protocols such as x25519 obsolete as they will be able to "crack" secret keys quickly.
45+
Vert.x proposes a quantum-safe key exchange protocol, x25519MLKEM768 (official recommendation of NIST) to ensure sessions over TLS are safe against quantum computers.
46+
Hybrid key exchange must be enabled along with {@link io.vertx.core.http.HttpServerOptions#setUseHybrid(boolean)} and only works using OpenSsl ({$@link io.vertx.core.http.HttpServerOptions#setSslEngineOptions(SSLEngineOptions)})
47+
4448
ALPN is a TLS extension that negotiates the protocol before the client and the server start to exchange data.
4549

4650
Clients that don't support ALPN will still be able to do a _classic_ SSL handshake.

src/main/generated/io/vertx/core/eventbus/EventBusOptionsConverter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,11 @@ static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, EventBu
279279
obj.setUseAlpn((Boolean)member.getValue());
280280
}
281281
break;
282+
case "useHybrid":
283+
if (member.getValue() instanceof Boolean) {
284+
obj.setUseHybrid((Boolean)member.getValue());
285+
}
286+
break;
282287
case "writeIdleTimeout":
283288
if (member.getValue() instanceof Number) {
284289
obj.setWriteIdleTimeout(((Number)member.getValue()).intValue());
@@ -388,6 +393,7 @@ static void toJson(EventBusOptions obj, java.util.Map<String, Object> json) {
388393
json.put("trustStoreOptions", obj.getTrustStoreOptions().toJson());
389394
}
390395
json.put("useAlpn", obj.isUseAlpn());
396+
json.put("useHybrid", obj.isUseHybrid());
391397
json.put("writeIdleTimeout", obj.getWriteIdleTimeout());
392398
}
393399
}

src/main/generated/io/vertx/core/net/SSLOptionsConverter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, SSLOpti
6969
obj.setUseAlpn((Boolean)member.getValue());
7070
}
7171
break;
72+
case "useHybrid":
73+
if (member.getValue() instanceof Boolean) {
74+
obj.setUseHybrid((Boolean)member.getValue());
75+
}
76+
break;
7277
}
7378
}
7479
}
@@ -103,5 +108,6 @@ static void toJson(SSLOptions obj, java.util.Map<String, Object> json) {
103108
json.put("sslHandshakeTimeoutUnit", obj.getSslHandshakeTimeoutUnit().name());
104109
}
105110
json.put("useAlpn", obj.isUseAlpn());
111+
json.put("useHybrid", obj.isUseHybrid());
106112
}
107113
}

src/main/generated/io/vertx/core/net/TCPSSLOptionsConverter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, TCPSSLO
179179
obj.setUseAlpn((Boolean)member.getValue());
180180
}
181181
break;
182+
case "useHybrid":
183+
if (member.getValue() instanceof Boolean) {
184+
obj.setUseHybrid((Boolean)member.getValue());
185+
}
186+
break;
182187
case "writeIdleTimeout":
183188
if (member.getValue() instanceof Number) {
184189
obj.setWriteIdleTimeout(((Number)member.getValue()).intValue());
@@ -258,6 +263,7 @@ static void toJson(TCPSSLOptions obj, java.util.Map<String, Object> json) {
258263
json.put("trustStoreOptions", obj.getTrustStoreOptions().toJson());
259264
}
260265
json.put("useAlpn", obj.isUseAlpn());
266+
json.put("useHybrid", obj.isUseHybrid());
261267
json.put("writeIdleTimeout", obj.getWriteIdleTimeout());
262268
}
263269
}

src/main/java/io/vertx/core/eventbus/EventBusOptions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,11 @@ public EventBusOptions setUseAlpn(boolean useAlpn) {
475475
return (EventBusOptions) super.setUseAlpn(useAlpn);
476476
}
477477

478+
@Override
479+
public EventBusOptions setUseHybrid(boolean useHybrid) {
480+
return (EventBusOptions) super.setUseHybrid(useHybrid);
481+
}
482+
478483
@Override
479484
public EventBusOptions setSslEngineOptions(SSLEngineOptions sslEngineOptions) {
480485
return (EventBusOptions) super.setSslEngineOptions(sslEngineOptions);

src/main/java/io/vertx/core/http/HttpClientOptions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,11 @@ public HttpClientOptions setUseAlpn(boolean useAlpn) {
11541154
return (HttpClientOptions) super.setUseAlpn(useAlpn);
11551155
}
11561156

1157+
@Override
1158+
public HttpClientOptions setUseHybrid(boolean useHybrid) {
1159+
return (HttpClientOptions) super.setUseHybrid(useHybrid);
1160+
}
1161+
11571162
@Override
11581163
public HttpClientOptions setSslEngineOptions(SSLEngineOptions sslEngineOptions) {
11591164
return (HttpClientOptions) super.setSslEngineOptions(sslEngineOptions);

src/main/java/io/vertx/core/http/HttpServerOptions.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,18 @@ public HttpServerOptions setUseAlpn(boolean useAlpn) {
421421
return this;
422422
}
423423

424+
/*
425+
Use X25519MLKEM768 instead of X25519 for key exchange.
426+
X25519MLKEM768 is a hybrid protocol exchange ensuring that encryption will resist quantum computers
427+
*/
428+
@Override
429+
public HttpServerOptions setUseHybrid(boolean useHybrid) {
430+
super.setUseHybrid(useHybrid);
431+
return this;
432+
}
433+
434+
435+
424436
@Override
425437
public HttpServerOptions setKeyCertOptions(KeyCertOptions options) {
426438
super.setKeyCertOptions(options);

src/main/java/io/vertx/core/http/WebSocketClientOptions.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,11 @@ public WebSocketClientOptions setUseAlpn(boolean useAlpn) {
552552
return (WebSocketClientOptions)super.setUseAlpn(useAlpn);
553553
}
554554

555+
@Override
556+
public WebSocketClientOptions setUseHybrid(boolean useHybrid) {
557+
return (WebSocketClientOptions)super.setUseHybrid(useHybrid);
558+
}
559+
555560
@Override
556561
public WebSocketClientOptions setSslEngineOptions(SSLEngineOptions sslEngineOptions) {
557562
return (WebSocketClientOptions)super.setSslEngineOptions(sslEngineOptions);

src/main/java/io/vertx/core/http/impl/HttpServerWorker.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@
3333
import io.vertx.core.impl.VertxInternal;
3434
import io.vertx.core.net.impl.*;
3535
import io.vertx.core.spi.metrics.HttpServerMetrics;
36+
import org.slf4j.Logger;
37+
import org.slf4j.LoggerFactory;
3638

3739
import java.nio.charset.StandardCharsets;
3840
import java.util.List;
41+
import java.util.concurrent.ExecutionException;
3942
import java.util.function.BiConsumer;
4043
import java.util.function.Function;
4144
import java.util.function.Supplier;
@@ -61,6 +64,7 @@ public class HttpServerWorker implements BiConsumer<Channel, SslChannelProvider>
6164
private final CompressionOptions[] compressionOptions;
6265
private final Function<String, String> encodingDetector;
6366
private final GlobalTrafficShapingHandler trafficShapingHandler;
67+
private static final Logger log = LoggerFactory.getLogger(SslChannelProvider.class);
6468

6569
public HttpServerWorker(ContextInternal context,
6670
Supplier<ContextInternal> streamContextSupplier,
@@ -115,18 +119,28 @@ public void accept(Channel ch, SslChannelProvider sslChannelProvider) {
115119
if (idle != null) {
116120
ch.pipeline().remove(idle);
117121
}
118-
configurePipeline(future.getNow(), sslChannelProvider);
122+
try {
123+
configurePipeline(future.getNow(), sslChannelProvider);
124+
} catch (Exception e) {
125+
log.error(e.getMessage()+ ", now closing the channel");
126+
ch.close();
127+
}
119128
} else {
120129
//No need to close the channel.HAProxyMessageDecoder already did
121130
handleException(future.cause());
122131
}
123132
});
124133
} else {
125-
configurePipeline(ch, sslChannelProvider);
134+
try {
135+
configurePipeline(ch, sslChannelProvider);
136+
} catch (Exception e) {
137+
log.error(e.getMessage()+ ", now closing the channel");
138+
ch.close();
139+
}
126140
}
127141
}
128142

129-
private void configurePipeline(Channel ch, SslChannelProvider sslChannelProvider) {
143+
private void configurePipeline(Channel ch, SslChannelProvider sslChannelProvider) throws Exception {
130144
ChannelPipeline pipeline = ch.pipeline();
131145
if (options.isSsl()) {
132146
pipeline.addLast("ssl", sslChannelProvider.createServerHandler(HttpUtils.socketAddressToHostAndPort(ch.remoteAddress())));

0 commit comments

Comments
 (0)