Skip to content

Commit

Permalink
Allow to configure groups via BoringSSLContextOption (#755)
Browse files Browse the repository at this point in the history
Motivation:

At the moment its only possible to specify the used named groups per JVM
via system property. We can do better when using our native provider.

Modifications:

Introduce BoringSSLContextOption.GROUPS that can be used to configure
the named groups per context

Result:

More flexible way of configure ssl.
  • Loading branch information
normanmaurer authored Nov 11, 2024
1 parent c8f08e7 commit 4ca596c
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2024 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.incubator.codec.quic;

import io.netty.handler.ssl.SslContextOption;

/**
* {@link SslContextOption}s that are specific to BoringSSL.
*
* @param <T> the type of the value.
*/
public final class BoringSSLContextOption<T> extends SslContextOption<T> {
private BoringSSLContextOption(String name) {
super(name);
}

/**
* Set the groups that should be used. This will override curves set with {@code -Djdk.tls.namedGroups}.
* <p>
* See <a href="https://github.com/google/boringssl/blob/master/include/openssl/ssl.h#L2632">
* SSL_CTX_set1_groups_list</a>.
*/
public static final BoringSSLContextOption<String[]> GROUPS = new BoringSSLContextOption<>("GROUPS");
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.netty.incubator.codec.quic;

import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContextOption;
import io.netty.handler.ssl.util.KeyManagerFactoryWrapper;
import io.netty.handler.ssl.util.TrustManagerFactoryWrapper;
import io.netty.util.Mapping;
Expand All @@ -34,6 +35,10 @@
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.netty.util.internal.ObjectUtil.checkNotNull;

Expand Down Expand Up @@ -155,7 +160,10 @@ public static QuicSslContext buildForServerWithSni(Mapping<? super String, ? ext
return forServer(SNI_KEYMANAGER, null).sni(mapping).build();
}

private static final Map.Entry[] EMPTY_ENTRIES = new Map.Entry[0];

private final boolean forServer;
private final Map<SslContextOption<?>, Object> options = new HashMap<>();
private TrustManagerFactory trustManagerFactory;
private String keyPassword;
private KeyManagerFactory keyManagerFactory;
Expand All @@ -176,6 +184,18 @@ private QuicSslContextBuilder sni(Mapping<? super String, ? extends QuicSslConte
return this;
}

/**
* Configure a {@link SslContextOption}.
*/
public <T> QuicSslContextBuilder option(SslContextOption<T> option, T value) {
if (value == null) {
options.remove(option);
} else {
options.put(option, value);
}
return this;
}

/**
* Enable / disable the usage of early data.
*/
Expand Down Expand Up @@ -373,13 +393,23 @@ public QuicSslContextBuilder clientAuth(ClientAuth clientAuth) {
public QuicSslContext build() {
if (forServer) {
return new QuicheQuicSslContext(true, sessionTimeout, sessionCacheSize, clientAuth, trustManagerFactory,
keyManagerFactory, keyPassword, mapping, earlyData, keylog, applicationProtocols);
keyManagerFactory, keyPassword, mapping, earlyData, keylog,
applicationProtocols, toArray(options.entrySet(), EMPTY_ENTRIES));
} else {
return new QuicheQuicSslContext(false, sessionTimeout, sessionCacheSize, clientAuth, trustManagerFactory,
keyManagerFactory, keyPassword, mapping, earlyData, keylog,
applicationProtocols);
applicationProtocols, toArray(options.entrySet(), EMPTY_ENTRIES));
}
}


private static <T> T[] toArray(Iterable<? extends T> iterable, T[] prototype) {
if (iterable == null) {
return null;
}
final List<T> list = new ArrayList<>();
for (T element : iterable) {
list.add(element);
}
return list.toArray(prototype);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.ssl.ApplicationProtocolNegotiator;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContextOption;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.AbstractReferenceCounted;
import io.netty.util.Mapping;
Expand Down Expand Up @@ -53,6 +54,7 @@
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.Executor;
Expand Down Expand Up @@ -153,7 +155,7 @@ final class QuicheQuicSslContext extends QuicSslContext {
@Nullable KeyManagerFactory keyManagerFactory, String password,
@Nullable Mapping<? super String, ? extends QuicSslContext> mapping,
@Nullable Boolean earlyData, @Nullable BoringSSLKeylog keylog,
String... applicationProtocols) {
String[] applicationProtocols, Map.Entry<SslContextOption<?>, Object>... ctxOptions) {
Quic.ensureAvailability();
this.server = server;
this.clientAuth = server ? checkNotNull(clientAuth, "clientAuth") : ClientAuth.NONE;
Expand All @@ -179,6 +181,24 @@ final class QuicheQuicSslContext extends QuicSslContext {
} else {
keyManager = chooseKeyManager(keyManagerFactory);
}
String[] groups = NAMED_GROUPS;
if (ctxOptions != null) {
for (Map.Entry<SslContextOption<?>, Object> ctxOpt : ctxOptions) {
SslContextOption<?> option = ctxOpt.getKey();

if (option == BoringSSLContextOption.GROUPS) {
String[] groupsArray = (String[]) ctxOpt.getValue();
Set<String> groupsSet = new LinkedHashSet<String>(groupsArray.length);
for (String group : groupsArray) {
groupsSet.add(GroupsConverter.toBoringSSL(group));
}
groups = groupsSet.toArray(EmptyArrays.EMPTY_STRINGS);
} else {
LOGGER.debug("Skipping unsupported " + SslContextOption.class.getSimpleName()
+ ": " + ctxOpt.getKey());
}
}
}
final BoringSSLPrivateKeyMethod privateKeyMethod;
if (keyManagerFactory instanceof BoringSSLKeylessManagerFactory) {
privateKeyMethod = new BoringSSLAsyncPrivateKeyMethodAdapter(engineMap,
Expand All @@ -199,8 +219,8 @@ final class QuicheQuicSslContext extends QuicSslContext {
BoringSSL.subjectNames(trustManager.getAcceptedIssuers())));
boolean success = false;
try {
if (NAMED_GROUPS.length > 0 && BoringSSL.SSLContext_set1_groups_list(nativeSslContext.ctx, NAMED_GROUPS) == 0) {
String msg = "failed to set curves / groups list: " + Arrays.toString(NAMED_GROUPS);
if (groups.length > 0 && BoringSSL.SSLContext_set1_groups_list(nativeSslContext.ctx, groups) == 0) {
String msg = "failed to set curves / groups list: " + Arrays.toString(groups);
String lastError = BoringSSL.ERR_last_error();
if (lastError != null) {
// We have some more details about why the operations failed, include these into the message.
Expand Down

0 comments on commit 4ca596c

Please sign in to comment.