diff --git a/pom.xml b/pom.xml index acad23eabee..b2443e7e414 100644 --- a/pom.xml +++ b/pom.xml @@ -204,6 +204,11 @@ log4j-core test + + io.netty + netty-tcnative-classes + 2.0.75.Final-SNAPSHOT + io.netty netty-tcnative-boringssl-static diff --git a/src/main/asciidoc/http.adoc b/src/main/asciidoc/http.adoc index 16b2d1bd91b..9a64219a2f9 100644 --- a/src/main/asciidoc/http.adoc +++ b/src/main/asciidoc/http.adoc @@ -41,6 +41,10 @@ To handle `h2` requests, TLS must be enabled along with {@link io.vertx.core.htt {@link examples.HTTP2Examples#example0} ---- +The rise of quantum computers will make key exchange protocols such as x25519 obsolete as they will be able to "crack" secret keys quickly. +Vert.x proposes a quantum-safe key exchange protocol, x25519MLKEM768 (official recommendation of NIST) to ensure sessions over TLS are safe against quantum computers. +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)}) + ALPN is a TLS extension that negotiates the protocol before the client and the server start to exchange data. Clients that don't support ALPN will still be able to do a _classic_ SSL handshake. diff --git a/src/main/generated/io/vertx/core/eventbus/EventBusOptionsConverter.java b/src/main/generated/io/vertx/core/eventbus/EventBusOptionsConverter.java index c750e1938bd..79e6fa8dfe5 100644 --- a/src/main/generated/io/vertx/core/eventbus/EventBusOptionsConverter.java +++ b/src/main/generated/io/vertx/core/eventbus/EventBusOptionsConverter.java @@ -279,6 +279,11 @@ static void fromJson(Iterable> json, EventBu obj.setUseAlpn((Boolean)member.getValue()); } break; + case "useHybrid": + if (member.getValue() instanceof Boolean) { + obj.setUseHybrid((Boolean)member.getValue()); + } + break; case "writeIdleTimeout": if (member.getValue() instanceof Number) { obj.setWriteIdleTimeout(((Number)member.getValue()).intValue()); @@ -388,6 +393,7 @@ static void toJson(EventBusOptions obj, java.util.Map json) { json.put("trustStoreOptions", obj.getTrustStoreOptions().toJson()); } json.put("useAlpn", obj.isUseAlpn()); + json.put("useHybrid", obj.isUseHybrid()); json.put("writeIdleTimeout", obj.getWriteIdleTimeout()); } } diff --git a/src/main/generated/io/vertx/core/net/SSLOptionsConverter.java b/src/main/generated/io/vertx/core/net/SSLOptionsConverter.java index ed5e57e40c9..76de6b53b5e 100644 --- a/src/main/generated/io/vertx/core/net/SSLOptionsConverter.java +++ b/src/main/generated/io/vertx/core/net/SSLOptionsConverter.java @@ -69,6 +69,11 @@ static void fromJson(Iterable> json, SSLOpti obj.setUseAlpn((Boolean)member.getValue()); } break; + case "useHybrid": + if (member.getValue() instanceof Boolean) { + obj.setUseHybrid((Boolean)member.getValue()); + } + break; } } } @@ -103,5 +108,6 @@ static void toJson(SSLOptions obj, java.util.Map json) { json.put("sslHandshakeTimeoutUnit", obj.getSslHandshakeTimeoutUnit().name()); } json.put("useAlpn", obj.isUseAlpn()); + json.put("useHybrid", obj.isUseHybrid()); } } diff --git a/src/main/generated/io/vertx/core/net/TCPSSLOptionsConverter.java b/src/main/generated/io/vertx/core/net/TCPSSLOptionsConverter.java index 82392e43426..77b551b0262 100644 --- a/src/main/generated/io/vertx/core/net/TCPSSLOptionsConverter.java +++ b/src/main/generated/io/vertx/core/net/TCPSSLOptionsConverter.java @@ -179,6 +179,11 @@ static void fromJson(Iterable> json, TCPSSLO obj.setUseAlpn((Boolean)member.getValue()); } break; + case "useHybrid": + if (member.getValue() instanceof Boolean) { + obj.setUseHybrid((Boolean)member.getValue()); + } + break; case "writeIdleTimeout": if (member.getValue() instanceof Number) { obj.setWriteIdleTimeout(((Number)member.getValue()).intValue()); @@ -258,6 +263,7 @@ static void toJson(TCPSSLOptions obj, java.util.Map json) { json.put("trustStoreOptions", obj.getTrustStoreOptions().toJson()); } json.put("useAlpn", obj.isUseAlpn()); + json.put("useHybrid", obj.isUseHybrid()); json.put("writeIdleTimeout", obj.getWriteIdleTimeout()); } } diff --git a/src/main/java/io/vertx/core/eventbus/EventBusOptions.java b/src/main/java/io/vertx/core/eventbus/EventBusOptions.java index 955260e319d..85b2c6d7604 100644 --- a/src/main/java/io/vertx/core/eventbus/EventBusOptions.java +++ b/src/main/java/io/vertx/core/eventbus/EventBusOptions.java @@ -475,6 +475,11 @@ public EventBusOptions setUseAlpn(boolean useAlpn) { return (EventBusOptions) super.setUseAlpn(useAlpn); } + @Override + public EventBusOptions setUseHybrid(boolean useHybrid) { + return (EventBusOptions) super.setUseHybrid(useHybrid); + } + @Override public EventBusOptions setSslEngineOptions(SSLEngineOptions sslEngineOptions) { return (EventBusOptions) super.setSslEngineOptions(sslEngineOptions); diff --git a/src/main/java/io/vertx/core/http/HttpClientOptions.java b/src/main/java/io/vertx/core/http/HttpClientOptions.java index d5516ee040e..9cf2f3ed4dd 100755 --- a/src/main/java/io/vertx/core/http/HttpClientOptions.java +++ b/src/main/java/io/vertx/core/http/HttpClientOptions.java @@ -1154,6 +1154,11 @@ public HttpClientOptions setUseAlpn(boolean useAlpn) { return (HttpClientOptions) super.setUseAlpn(useAlpn); } + @Override + public HttpClientOptions setUseHybrid(boolean useHybrid) { + return (HttpClientOptions) super.setUseHybrid(useHybrid); + } + @Override public HttpClientOptions setSslEngineOptions(SSLEngineOptions sslEngineOptions) { return (HttpClientOptions) super.setSslEngineOptions(sslEngineOptions); diff --git a/src/main/java/io/vertx/core/http/HttpServerOptions.java b/src/main/java/io/vertx/core/http/HttpServerOptions.java index 345c804f862..917b57c3506 100755 --- a/src/main/java/io/vertx/core/http/HttpServerOptions.java +++ b/src/main/java/io/vertx/core/http/HttpServerOptions.java @@ -421,6 +421,18 @@ public HttpServerOptions setUseAlpn(boolean useAlpn) { return this; } + /* + Use X25519MLKEM768 instead of X25519 for key exchange. + X25519MLKEM768 is a hybrid protocol exchange ensuring that encryption will resist quantum computers + */ + @Override + public HttpServerOptions setUseHybrid(boolean useHybrid) { + super.setUseHybrid(useHybrid); + return this; + } + + + @Override public HttpServerOptions setKeyCertOptions(KeyCertOptions options) { super.setKeyCertOptions(options); diff --git a/src/main/java/io/vertx/core/http/WebSocketClientOptions.java b/src/main/java/io/vertx/core/http/WebSocketClientOptions.java index 2fa6757755d..abb0f2c89a9 100644 --- a/src/main/java/io/vertx/core/http/WebSocketClientOptions.java +++ b/src/main/java/io/vertx/core/http/WebSocketClientOptions.java @@ -552,6 +552,11 @@ public WebSocketClientOptions setUseAlpn(boolean useAlpn) { return (WebSocketClientOptions)super.setUseAlpn(useAlpn); } + @Override + public WebSocketClientOptions setUseHybrid(boolean useHybrid) { + return (WebSocketClientOptions)super.setUseHybrid(useHybrid); + } + @Override public WebSocketClientOptions setSslEngineOptions(SSLEngineOptions sslEngineOptions) { return (WebSocketClientOptions)super.setSslEngineOptions(sslEngineOptions); diff --git a/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java b/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java index f783d9a333e..e1b2d34f044 100644 --- a/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java +++ b/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java @@ -33,9 +33,12 @@ import io.vertx.core.impl.VertxInternal; import io.vertx.core.net.impl.*; import io.vertx.core.spi.metrics.HttpServerMetrics; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; @@ -61,6 +64,7 @@ public class HttpServerWorker implements BiConsumer private final CompressionOptions[] compressionOptions; private final Function encodingDetector; private final GlobalTrafficShapingHandler trafficShapingHandler; + private static final Logger log = LoggerFactory.getLogger(SslChannelProvider.class); public HttpServerWorker(ContextInternal context, Supplier streamContextSupplier, @@ -115,18 +119,28 @@ public void accept(Channel ch, SslChannelProvider sslChannelProvider) { if (idle != null) { ch.pipeline().remove(idle); } - configurePipeline(future.getNow(), sslChannelProvider); + try { + configurePipeline(future.getNow(), sslChannelProvider); + } catch (Exception e) { + log.error(e.getMessage()+ ", now closing the channel"); + ch.close(); + } } else { //No need to close the channel.HAProxyMessageDecoder already did handleException(future.cause()); } }); } else { - configurePipeline(ch, sslChannelProvider); + try { + configurePipeline(ch, sslChannelProvider); + } catch (Exception e) { + log.error(e.getMessage()+ ", now closing the channel"); + ch.close(); + } } } - private void configurePipeline(Channel ch, SslChannelProvider sslChannelProvider) { + private void configurePipeline(Channel ch, SslChannelProvider sslChannelProvider) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (options.isSsl()) { pipeline.addLast("ssl", sslChannelProvider.createServerHandler(HttpUtils.socketAddressToHostAndPort(ch.remoteAddress()))); diff --git a/src/main/java/io/vertx/core/net/ClientOptionsBase.java b/src/main/java/io/vertx/core/net/ClientOptionsBase.java index 2fb8b876042..703e34c6bd6 100755 --- a/src/main/java/io/vertx/core/net/ClientOptionsBase.java +++ b/src/main/java/io/vertx/core/net/ClientOptionsBase.java @@ -340,6 +340,11 @@ public ClientOptionsBase setUseAlpn(boolean useAlpn) { return (ClientOptionsBase) super.setUseAlpn(useAlpn); } + @Override + public ClientOptionsBase setUseHybrid(boolean useHyrbrid) { + return (ClientOptionsBase) super.setUseHybrid(useHyrbrid); + } + @Override public ClientOptionsBase setSslEngineOptions(SSLEngineOptions sslEngineOptions) { return (ClientOptionsBase) super.setSslEngineOptions(sslEngineOptions); diff --git a/src/main/java/io/vertx/core/net/NetClientOptions.java b/src/main/java/io/vertx/core/net/NetClientOptions.java index 5c9fcd56120..b605b73311d 100755 --- a/src/main/java/io/vertx/core/net/NetClientOptions.java +++ b/src/main/java/io/vertx/core/net/NetClientOptions.java @@ -263,6 +263,11 @@ public NetClientOptions setUseAlpn(boolean useAlpn) { return (NetClientOptions) super.setUseAlpn(useAlpn); } + @Override + public NetClientOptions setUseHybrid(boolean useHybrid) { + return (NetClientOptions) super.setUseHybrid(useHybrid); + } + @Override public NetClientOptions setSslEngineOptions(SSLEngineOptions sslEngineOptions) { return (NetClientOptions) super.setSslEngineOptions(sslEngineOptions); diff --git a/src/main/java/io/vertx/core/net/NetServerOptions.java b/src/main/java/io/vertx/core/net/NetServerOptions.java index 125b079d4a6..3a1eb907e6c 100755 --- a/src/main/java/io/vertx/core/net/NetServerOptions.java +++ b/src/main/java/io/vertx/core/net/NetServerOptions.java @@ -221,8 +221,8 @@ public NetServerOptions setSsl(boolean ssl) { } @Override - public NetServerOptions setUseAlpn(boolean useAlpn) { - super.setUseAlpn(useAlpn); + public NetServerOptions setUseHybrid(boolean useHybrid) { + super.setUseHybrid(useHybrid); return this; } diff --git a/src/main/java/io/vertx/core/net/SSLOptions.java b/src/main/java/io/vertx/core/net/SSLOptions.java index ed3f0bc0f69..f01f6e6da85 100644 --- a/src/main/java/io/vertx/core/net/SSLOptions.java +++ b/src/main/java/io/vertx/core/net/SSLOptions.java @@ -37,7 +37,11 @@ public class SSLOptions { /** * Default use alpn = false */ - public static final boolean DEFAULT_USE_ALPN = false; + public static final boolean DEFAULT_USE_ALPN = false; /** + + * Default use hybrid = false + */ + public static final boolean DEFAULT_USE_HYBRID = false; /** * The default value of SSL handshake timeout = 10 @@ -66,6 +70,7 @@ public class SSLOptions { private ArrayList crlPaths; private ArrayList crlValues; private boolean useAlpn; + private boolean useHybrid; private Set enabledSecureTransportProtocols; /** @@ -99,6 +104,7 @@ public SSLOptions(SSLOptions other) { this.crlPaths = new ArrayList<>(other.getCrlPaths()); this.crlValues = new ArrayList<>(other.getCrlValues()); this.useAlpn = other.useAlpn; + this.useHybrid = other.useHybrid; this.enabledSecureTransportProtocols = other.getEnabledSecureTransportProtocols() == null ? new LinkedHashSet<>() : new LinkedHashSet<>(other.getEnabledSecureTransportProtocols()); } @@ -110,6 +116,7 @@ private void init() { crlPaths = new ArrayList<>(); crlValues = new ArrayList<>(); useAlpn = DEFAULT_USE_ALPN; + useHybrid = DEFAULT_USE_HYBRID; enabledSecureTransportProtocols = new LinkedHashSet<>(DEFAULT_ENABLED_SECURE_TRANSPORT_PROTOCOLS); } @@ -236,6 +243,13 @@ public boolean isUseAlpn() { return useAlpn; } + /** + * @return whether to use or not Hybrid key exchange protocol x25519MLKEM768 + */ + public boolean isUseHybrid() { + return useHybrid; + } + /** * Set the ALPN usage. * @@ -246,6 +260,11 @@ public SSLOptions setUseAlpn(boolean useAlpn) { return this; } + public SSLOptions setUseHybrid(boolean useHybrid) { + this.useHybrid = useHybrid; + return this; + } + /** * Returns the enabled SSL/TLS protocols * @return the enabled protocols diff --git a/src/main/java/io/vertx/core/net/TCPSSLOptions.java b/src/main/java/io/vertx/core/net/TCPSSLOptions.java index 3b2f6e569df..6204d905ce1 100755 --- a/src/main/java/io/vertx/core/net/TCPSSLOptions.java +++ b/src/main/java/io/vertx/core/net/TCPSSLOptions.java @@ -712,6 +712,13 @@ public boolean isUseAlpn() { return sslOptions.isUseAlpn(); } + /** + * @return whether to use or not Hybrid key exchange protocol x25519MLKEM768 + */ + public boolean isUseHybrid() { + return sslOptions.isUseHybrid(); + } + /** * Set the ALPN usage. * @@ -722,6 +729,11 @@ public TCPSSLOptions setUseAlpn(boolean useAlpn) { return this; } + public TCPSSLOptions setUseHybrid(boolean useHybrid) { + sslOptions.setUseHybrid(useHybrid); + return this; + } + /** * @return the SSL engine implementation to use */ diff --git a/src/main/java/io/vertx/core/net/impl/ChannelProvider.java b/src/main/java/io/vertx/core/net/impl/ChannelProvider.java index 10006a636e3..7d5d1266d16 100644 --- a/src/main/java/io/vertx/core/net/impl/ChannelProvider.java +++ b/src/main/java/io/vertx/core/net/impl/ChannelProvider.java @@ -34,6 +34,8 @@ import io.vertx.core.net.ProxyOptions; import io.vertx.core.net.ProxyType; import io.vertx.core.net.SocketAddress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.net.ssl.SSLHandshakeException; import java.net.InetAddress; @@ -49,6 +51,7 @@ */ public final class ChannelProvider { + private static final Logger log = LoggerFactory.getLogger(ChannelProvider.class); private final Bootstrap bootstrap; private final SslChannelProvider sslContextProvider; private final ContextInternal context; @@ -115,9 +118,17 @@ private void connect(Handler handler, SocketAddress remoteAddress, Sock private void initSSL(Handler handler, SocketAddress peerAddress, String serverName, boolean ssl, boolean useAlpn, Channel ch, Promise channelHandler) { if (ssl) { - SslHandler sslHandler = sslContextProvider.createClientSslHandler(peerAddress, serverName, useAlpn); + SslHandler sslHandler = null; + try { + sslHandler = sslContextProvider.createClientSslHandler(peerAddress, serverName, useAlpn); + } catch (Exception e) { + log.error(e.getMessage()); + ch.close(); + return; + } ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("ssl", sslHandler); + SslHandler finalSslHandler = sslHandler; pipeline.addLast(new ChannelInboundHandlerAdapter() { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { @@ -127,7 +138,7 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { if (completion.isSuccess()) { // Remove from the pipeline after handshake result ctx.pipeline().remove(this); - applicationProtocol = sslHandler.applicationProtocol(); + applicationProtocol = finalSslHandler.applicationProtocol(); if (handler != null) { context.dispatch(ch, handler); } diff --git a/src/main/java/io/vertx/core/net/impl/NetServerImpl.java b/src/main/java/io/vertx/core/net/impl/NetServerImpl.java index 9f8c3ca4949..a47925e94df 100644 --- a/src/main/java/io/vertx/core/net/impl/NetServerImpl.java +++ b/src/main/java/io/vertx/core/net/impl/NetServerImpl.java @@ -39,6 +39,7 @@ import io.vertx.core.spi.metrics.VertxMetrics; import io.vertx.core.streams.ReadStream; +import java.util.concurrent.ExecutionException; import java.util.function.BiConsumer; /** @@ -209,18 +210,28 @@ public void accept(Channel ch, SslChannelProvider sslChannelProvider) { if (idle != null) { ch.pipeline().remove(idle); } - configurePipeline(future.getNow(), sslChannelProvider); + try { + configurePipeline(future.getNow(), sslChannelProvider); + } catch (Exception e) { + log.error(e.getMessage()+ ", now closing the channel"); + ch.close(); + } } else { //No need to close the channel.HAProxyMessageDecoder already did handleException(future.cause()); } }); } else { - configurePipeline(ch, sslChannelProvider); + try { + configurePipeline(ch, sslChannelProvider); + } catch (Exception e) { + log.error(e.getMessage()+ ", now closing the channel"); + ch.close(); + } } } - private void configurePipeline(Channel ch, SslChannelProvider sslChannelProvider) { + private void configurePipeline(Channel ch, SslChannelProvider sslChannelProvider) throws Exception{ if (options.isSsl()) { ch.pipeline().addLast("ssl", sslChannelProvider.createServerHandler(HttpUtils.socketAddressToHostAndPort(ch.remoteAddress()))); ChannelPromise p = ch.newPromise(); diff --git a/src/main/java/io/vertx/core/net/impl/NetSocketImpl.java b/src/main/java/io/vertx/core/net/impl/NetSocketImpl.java index 883b653591f..4b8a2c001df 100644 --- a/src/main/java/io/vertx/core/net/impl/NetSocketImpl.java +++ b/src/main/java/io/vertx/core/net/impl/NetSocketImpl.java @@ -338,7 +338,12 @@ public Future upgradeToSsl(String serverName) { if (remoteAddress != null) { sslHandler = sslChannelProvider.createClientSslHandler(remoteAddress, serverName, false); } else { - sslHandler = sslChannelProvider.createServerHandler(HttpUtils.socketAddressToHostAndPort(chctx.channel().remoteAddress())); + try { + sslHandler = sslChannelProvider.createServerHandler(HttpUtils.socketAddressToHostAndPort(chctx.channel().remoteAddress())); + } catch (Exception e) { + promise.fail(e); + return; + } } chctx.pipeline().addFirst("ssl", sslHandler); } else { diff --git a/src/main/java/io/vertx/core/net/impl/SSLHelper.java b/src/main/java/io/vertx/core/net/impl/SSLHelper.java index 17747e45702..50ac4700abe 100755 --- a/src/main/java/io/vertx/core/net/impl/SSLHelper.java +++ b/src/main/java/io/vertx/core/net/impl/SSLHelper.java @@ -128,6 +128,7 @@ public static SSLEngineOptions resolveEngineOptions(SSLEngineOptions engineOptio private final ClientAuth clientAuth; private final boolean client; private final boolean useAlpn; + private final boolean useHybrid; private final String endpointIdentificationAlgorithm; private final SSLEngineOptions sslEngineOptions; private final List applicationProtocols; @@ -139,6 +140,7 @@ public static SSLEngineOptions resolveEngineOptions(SSLEngineOptions engineOptio private Future cachedProvider; public SSLHelper(TCPSSLOptions options, List applicationProtocols) { + this.useHybrid = options.isUseHybrid(); this.sslEngineOptions = options.getSslEngineOptions(); this.ssl = options.isSsl(); this.useAlpn = options.isUseAlpn(); @@ -264,7 +266,8 @@ public Future buildChannelProvider(SSLOptions sslOptions, Co trustAll, useAlpn, ctx.owner().getInternalWorkerPool().executor(), - c.useWorkerPool + c.useWorkerPool, + useHybrid )); } diff --git a/src/main/java/io/vertx/core/net/impl/SslChannelProvider.java b/src/main/java/io/vertx/core/net/impl/SslChannelProvider.java index 4c54eb412b9..2357e75d9ce 100644 --- a/src/main/java/io/vertx/core/net/impl/SslChannelProvider.java +++ b/src/main/java/io/vertx/core/net/impl/SslChannelProvider.java @@ -11,20 +11,28 @@ package io.vertx.core.net.impl; import io.netty.buffer.ByteBufAllocator; +import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.ssl.ReferenceCountedOpenSslEngine; import io.netty.handler.ssl.SniHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandler; +import io.netty.internal.tcnative.SSL; import io.netty.util.AsyncMapping; import io.netty.util.concurrent.ImmediateExecutor; import io.vertx.core.VertxException; import io.vertx.core.net.HostAndPort; import io.vertx.core.net.SocketAddress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLEngine; import javax.net.ssl.TrustManager; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @@ -35,12 +43,14 @@ */ public class SslChannelProvider { + private static final Logger log = LoggerFactory.getLogger(SslChannelProvider.class); private final long sslHandshakeTimeout; private final TimeUnit sslHandshakeTimeoutUnit; private final Executor workerPool; private final boolean useWorkerPool; private final boolean sni; private final boolean useAlpn; + private final boolean useHybrid; private final boolean trustAll; private final SslContextProvider sslContextProvider; private final SslContext[] sslContexts = new SslContext[2]; @@ -55,7 +65,7 @@ public SslChannelProvider(SslContextProvider sslContextProvider, boolean trustAll, boolean useAlpn, Executor workerPool, - boolean useWorkerPool) { + boolean useWorkerPool, boolean useHybrid) { this.workerPool = workerPool; this.useWorkerPool = useWorkerPool; this.useAlpn = useAlpn; @@ -64,6 +74,7 @@ public SslChannelProvider(SslContextProvider sslContextProvider, this.sslHandshakeTimeout = sslHandshakeTimeout; this.sslHandshakeTimeoutUnit = sslHandshakeTimeoutUnit; this.sslContextProvider = sslContextProvider; + this.useHybrid = useHybrid; } public int sniEntrySize() { @@ -131,7 +142,7 @@ public SslContext sslServerContext(boolean useAlpn) { }; } - public SslHandler createClientSslHandler(SocketAddress remoteAddress, String serverName, boolean useAlpn) { + public SslHandler createClientSslHandler(SocketAddress remoteAddress, String serverName, boolean useAlpn) throws Exception { SslContext sslContext = sslClientContext(serverName, useAlpn); SslHandler sslHandler; Executor delegatedTaskExec = useWorkerPool ? workerPool : ImmediateExecutor.INSTANCE; @@ -140,19 +151,35 @@ public SslHandler createClientSslHandler(SocketAddress remoteAddress, String ser } else { sslHandler = sslContext.newHandler(ByteBufAllocator.DEFAULT, remoteAddress.host(), remoteAddress.port(), delegatedTaskExec); } + if (useHybrid) { + SSLEngine engine = sslHandler.engine(); + try { + long sslPtr = ((ReferenceCountedOpenSslEngine) engine).sslPointer(); + boolean success = SSL.setCurvesList(sslPtr, "X25519MLKEM768"); + if (!success) { + throw new Exception("Failed to set hybrid PQC groups on SSL instance"); + } + } catch (Exception e) { + throw new Exception("Unable to create sslHandler: "+e.getMessage()); + } + } sslHandler.setHandshakeTimeout(sslHandshakeTimeout, sslHandshakeTimeoutUnit); return sslHandler; } - public ChannelHandler createServerHandler(HostAndPort remoteAddress) { + public ChannelHandler createServerHandler(HostAndPort remoteAddress) throws Exception { if (sni) { - return createSniHandler(remoteAddress); + SniHandler sniHandler = createSniHandler(useHybrid, remoteAddress); + if(sniHandler == null){ + throw new Exception("Unable to create a SNI handler"); + } + return sniHandler; } else { - return createServerSslHandler(useAlpn, remoteAddress); + return createServerSslHandler(useAlpn, useHybrid, remoteAddress); } } - private SslHandler createServerSslHandler(boolean useAlpn, HostAndPort remoteAddress) { + private SslHandler createServerSslHandler(boolean useAlpn, boolean useHybrid, HostAndPort remoteAddress) throws Exception { SslContext sslContext = sslServerContext(useAlpn); Executor delegatedTaskExec = useWorkerPool ? workerPool : ImmediateExecutor.INSTANCE; SslHandler sslHandler; @@ -161,13 +188,26 @@ private SslHandler createServerSslHandler(boolean useAlpn, HostAndPort remoteAdd } else { sslHandler = sslContext.newHandler(ByteBufAllocator.DEFAULT, delegatedTaskExec); } + if (useHybrid) { + SSLEngine engine = sslHandler.engine(); + try { + long sslPtr = ((ReferenceCountedOpenSslEngine) engine).sslPointer(); + boolean success = SSL.setCurvesList(sslPtr, "X25519MLKEM768"); + if (!success) { + throw new Exception("Failed to set hybrid PQC groups on SSL instance"); + } + } catch (Exception e) { + throw new Exception("Unable to create sslHandler: "+e.getMessage()); + } + } sslHandler.setHandshakeTimeout(sslHandshakeTimeout, sslHandshakeTimeoutUnit); return sslHandler; } - private SniHandler createSniHandler(HostAndPort remoteAddress) { + private SniHandler createSniHandler(boolean useHybrid, HostAndPort remoteAddress) { Executor delegatedTaskExec = useWorkerPool ? workerPool : ImmediateExecutor.INSTANCE; - return new VertxSniHandler(serverNameMapping(), sslHandshakeTimeoutUnit.toMillis(sslHandshakeTimeout), delegatedTaskExec, remoteAddress); + return new VertxSniHandler(serverNameMapping(), sslHandshakeTimeoutUnit.toMillis(sslHandshakeTimeout), delegatedTaskExec, + useHybrid, remoteAddress); } private static int idx(boolean useAlpn) { diff --git a/src/main/java/io/vertx/core/net/impl/VertxSniHandler.java b/src/main/java/io/vertx/core/net/impl/VertxSniHandler.java index 07142c6d1c4..a3357d3f64b 100644 --- a/src/main/java/io/vertx/core/net/impl/VertxSniHandler.java +++ b/src/main/java/io/vertx/core/net/impl/VertxSniHandler.java @@ -11,12 +11,17 @@ package io.vertx.core.net.impl; import io.netty.buffer.ByteBufAllocator; +import io.netty.handler.ssl.ReferenceCountedOpenSslEngine; import io.netty.handler.ssl.SniHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandler; +import io.netty.internal.tcnative.SSL; import io.netty.util.AsyncMapping; import io.vertx.core.net.HostAndPort; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.net.ssl.SSLEngine; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @@ -29,12 +34,16 @@ class VertxSniHandler extends SniHandler { private final Executor delegatedTaskExec; private final HostAndPort remoteAddress; + private final boolean useHybrid; + + private static final Logger log = LoggerFactory.getLogger(SslChannelProvider.class); public VertxSniHandler(AsyncMapping mapping, long handshakeTimeoutMillis, Executor delegatedTaskExec, - HostAndPort remoteAddress) { + boolean useHybrid, HostAndPort remoteAddress) { super(mapping, handshakeTimeoutMillis); this.delegatedTaskExec = delegatedTaskExec; + this.useHybrid = useHybrid; this.remoteAddress = remoteAddress; } @@ -46,6 +55,22 @@ protected SslHandler newSslHandler(SslContext context, ByteBufAllocator allocato } else { sslHandler = context.newHandler(allocator, delegatedTaskExec); } + if(useHybrid){ + SSLEngine engine = sslHandler.engine(); + try { + long sslPtr = ((ReferenceCountedOpenSslEngine) engine).sslPointer(); + boolean success = SSL.setCurvesList(sslPtr, "X25519MLKEM768"); + if (!success) { + throw new Exception("Failed to set hybrid PQC groups on SSL instance"); + } + } catch (Exception e) { + /* + todo : would like to throw instead of returning null to be consistent with + io.vertx.core.net.impl.SslChannelProvider.createServerSslHandler(...) but can't as we extend a netty class here. + */ + return null; + } + } sslHandler.setHandshakeTimeout(handshakeTimeoutMillis, TimeUnit.MILLISECONDS); return sslHandler; }