diff --git a/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java b/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java index fdd434d7c..db1376d68 100644 --- a/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java +++ b/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java @@ -432,7 +432,7 @@ public final void setEnabledProtocols(String[] protocols) { } @Override - final String getCurveNameForTesting() { + public final String getCurveNameForTesting() { return engine.getCurveNameForTesting(); } diff --git a/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java b/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java index 62f30bea1..c457a1636 100644 --- a/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java +++ b/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java @@ -761,7 +761,7 @@ public final void setEnabledProtocols(String[] protocols) { } @Override - final String getCurveNameForTesting() { + public final String getCurveNameForTesting() { return ssl.getCurveNameForTesting(); } diff --git a/common/src/main/java/org/conscrypt/NativeSsl.java b/common/src/main/java/org/conscrypt/NativeSsl.java index 762314d5a..6c21a076a 100644 --- a/common/src/main/java/org/conscrypt/NativeSsl.java +++ b/common/src/main/java/org/conscrypt/NativeSsl.java @@ -39,6 +39,7 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; @@ -278,6 +279,71 @@ byte[] getTlsChannelId() throws SSLException { return NativeCrypto.SSL_get_tls_channel_id(ssl, this); } + private static int toBoringSslGroup(String javaNamedGroup) { + switch (javaNamedGroup) { + case "X25519": + case "x25519": + return NativeConstants.NID_X25519; + case "P-256": + case "secp256r1": + return NativeConstants.NID_X9_62_prime256v1; + case "P-384": + case "secp384r1": + return NativeConstants.NID_secp384r1; + case "P-521": + case "secp521r1": + return NativeConstants.NID_secp521r1; + case "X25519MLKEM768": + return NativeConstants.NID_X25519MLKEM768; + case "X25519Kyber768Draft00": + return NativeConstants.NID_X25519Kyber768Draft00; + case "MLKEM1024": + return NativeConstants.NID_ML_KEM_1024; + default: + return -1; // Unknown group. + } + } + + /** + * Converts a list of java named groups to an array of groups that can be passed to BoringSSL. + * + *
Unknown groups are ignored, as required by the API documentation:
+ * SSLParameters.getNamedGroups()
+ */
+ static int[] toBoringSslGroups(String[] javaNamedGroups) {
+ int[] outputGroups = new int[javaNamedGroups.length];
+ int i = 0;
+ for (String javaNamedGroup : javaNamedGroups) {
+ int group = toBoringSslGroup(javaNamedGroup);
+ if (group > 0) {
+ outputGroups[i] = group;
+ i++;
+ }
+ }
+ if (i == 0) {
+ throw new IllegalArgumentException(
+ "No valid known group found in: " + Arrays.toString(javaNamedGroups));
+ }
+ if (i < javaNamedGroups.length) {
+ return Arrays.copyOf(outputGroups, i);
+ }
+ return outputGroups;
+ }
+
+ static int[] parseNamedGroupsProperty(String namedGroupsProperty) {
+ if (namedGroupsProperty == null) {
+ throw new NullPointerException("namedGroupsProperty is null");
+ }
+ String[] namedGroups = namedGroupsProperty.replace(" ", "").split(",");
+ return toBoringSslGroups(namedGroups);
+ }
+
+ private static void setDefaultNamedGroups(long ssl, NativeSsl sslHolder) {
+ // When the groups list is empty, the default named groups are used.
+ NativeCrypto.SSL_set1_groups(ssl, sslHolder, new int[0]);
+ }
+
void initialize(String hostname, OpenSSLKey channelIdPrivateKey) throws IOException {
boolean enableSessionCreation = parameters.getEnableSessionCreation();
if (!enableSessionCreation) {
@@ -320,6 +386,16 @@ void initialize(String hostname, OpenSSLKey channelIdPrivateKey) throws IOExcept
ssl, this, parameters.enabledCipherSuites, parameters.enabledProtocols);
}
+ String namedGroupsProperty = System.getProperty("jdk.tls.namedGroups");
+ if (namedGroupsProperty == null || namedGroupsProperty.isEmpty()) {
+ // If the property is not set or empty, use the default named groups. See:
+ // https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html
+ setDefaultNamedGroups(ssl, this);
+ } else {
+ int[] groups = parseNamedGroupsProperty(namedGroupsProperty);
+ NativeCrypto.SSL_set1_groups(ssl, this, groups);
+ }
+
if (parameters.applicationProtocols.length > 0) {
NativeCrypto.setApplicationProtocols(ssl, this, isClient(), parameters.applicationProtocols);
}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java b/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
index 5aabdc358..f0c2571e1 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
@@ -151,4 +151,6 @@ public final byte[] getAlpnSelectedProtocol() {
public final void setAlpnProtocols(byte[] protocols) {
setApplicationProtocols(SSLUtils.decodeProtocols(protocols == null ? EmptyArray.BYTE : protocols));
}
+
+ @Override public abstract String getCurveNameForTesting();
}
diff --git a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java
index 083a10a9e..7b5e6132b 100644
--- a/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java
+++ b/common/src/test/java/org/conscrypt/javax/net/ssl/SSLSocketTest.java
@@ -27,6 +27,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
+import org.conscrypt.OpenSSLSocketImpl;
import org.conscrypt.TestUtils;
import org.conscrypt.java.security.StandardNames;
import org.conscrypt.java.security.TestKeyStore;
@@ -39,6 +40,7 @@
import org.conscrypt.tlswire.handshake.HelloExtension;
import org.conscrypt.tlswire.util.TlsProtocolVersion;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -61,6 +63,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -93,6 +96,10 @@ public class SSLSocketTest {
private final ExecutorService executor =
Executors.newCachedThreadPool(t -> new Thread(threadGroup, t));
+ String getCurveName(SSLSocket socket) {
+ return ((OpenSSLSocketImpl) socket).getCurveNameForTesting();
+ }
+
/**
* Returns the named groups, or null if the method is not available (older versions of
* Java/Android).
@@ -117,8 +124,23 @@ void setNamedGroups(SSLParameters params, String[] namedGroups) {
}
}
+ // value of jdk.tls.namedGroups property before the test. null if the property was not set.
+ private String tlsNamedGroupsProperty;
+
+ @Before
+ public void setUp() throws Exception {
+ tlsNamedGroupsProperty = System.getProperty("jdk.tls.namedGroups");
+ }
+
@After
public void teardown() throws InterruptedException {
+ // Restore the property to its original value, to make sure that the test does not
+ // have any side effects on other tests when setting the property.
+ if (tlsNamedGroupsProperty == null) {
+ System.clearProperty("jdk.tls.namedGroups");
+ } else {
+ System.setProperty("jdk.tls.namedGroups", tlsNamedGroupsProperty);
+ }
executor.shutdownNow();
assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS));
}
@@ -911,6 +933,80 @@ public void test_SSLSocket_ClientHello_cipherSuites() throws Exception {
getSSLSocketFactoriesToTest());
}
+ @Test
+ public void handshake_noNamedGroupsProperty_usesDefaultGroups() throws Exception {
+ System.clearProperty("jdk.tls.namedGroups");
+ TestSSLContext context = TestSSLContext.create();
+ final SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(
+ context.host, context.port);
+ final SSLSocket server = (SSLSocket) context.serverSocket.accept();
+ Future