Add test to make sure TLS elliptic curves list is sensible
We accidentally regressed Android's TLS client by taking a change to
BoringSSL that limited client support to secp256r1. Note that BoringSSL
still sets the default, but add this test to make sure that our usage of
BoringSSL API doesn't cause unintended regressions.
(cherry picked from commit c97ba7f9b6a7621a55b95a7d5fd00cdffd6f09d5 with
removal of "x25519")
Test: make -j32 build-art-host vogar && vogar --mode host --classpath out/host/common/obj/JAVA_LIBRARIES/core-tests-support-hostdex_intermediates/classes.jack --classpath out/host/common/obj/JAVA_LIBRARIES/core-tests-hostdex_intermediates/classes.jack libcore/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java -- test_SSLSocket_ClientHello_supportedCurves
Bug: 31393711
Change-Id: I9ec9b46f7f504dc239ae6a0da042458ebfbe9c63
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
index d17e32b..5e94b21 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
@@ -80,6 +80,8 @@
import libcore.tlswire.handshake.CipherSuite;
import libcore.tlswire.handshake.ClientHello;
import libcore.tlswire.handshake.CompressionMethod;
+import libcore.tlswire.handshake.EllipticCurve;
+import libcore.tlswire.handshake.EllipticCurvesHelloExtension;
import libcore.tlswire.handshake.HandshakeMessage;
import libcore.tlswire.handshake.HelloExtension;
import libcore.tlswire.handshake.ServerNameHelloExtension;
@@ -1693,6 +1695,31 @@
}, getSSLSocketFactoriesToTest());
}
+ public void test_SSLSocket_ClientHello_supportedCurves() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+
+ EllipticCurvesHelloExtension ecExtension = (EllipticCurvesHelloExtension)
+ clientHello.findExtensionByType(HelloExtension.TYPE_ELLIPTIC_CURVES);
+ final String[] supportedCurves;
+ if (ecExtension == null) {
+ supportedCurves = new String[0];
+ } else {
+ assertTrue(ecExtension.wellFormed);
+ supportedCurves = new String[ecExtension.supported.size()];
+ for (int i = 0; i < ecExtension.supported.size(); i++) {
+ EllipticCurve curve = ecExtension.supported.get(i);
+ supportedCurves[i] = curve.toString();
+ }
+ }
+
+ StandardNames.assertDefaultEllipticCurves(supportedCurves);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
public void test_SSLSocket_ClientHello_clientProtocolVersion() throws Exception {
ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
@Override
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index d414d4b..675aa3d 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -988,6 +988,14 @@
"TLS_PSK_WITH_AES_256_CBC_SHA"
);
+ // Should be updated to match BoringSSL's defaults when they change.
+ // https://android.googlesource.com/platform/external/boringssl/+/master/src/ssl/t1_lib.c#305
+ public static final List<String> ELLIPTIC_CURVES_DEFAULT = Arrays.asList(
+ "secp256r1 (23)",
+ "secp384r1 (24)",
+ "secp521r1 (25)"
+ );
+
private static final Set<String> PERMITTED_DEFAULT_KEY_EXCHANGE_ALGS =
new HashSet<String>(Arrays.asList("RSA",
"DHE_RSA",
@@ -1154,6 +1162,10 @@
}
}
+ public static void assertDefaultEllipticCurves(String[] curves) {
+ assertEquals(ELLIPTIC_CURVES_DEFAULT, Arrays.asList(curves));
+ }
+
public static void assertSSLContextEnabledProtocols(String version, String[] protocols) {
assertEquals("For protocol \"" + version + "\"",
Arrays.toString(SSL_CONTEXT_PROTOCOLS_ENABLED.get(version)),
diff --git a/support/src/test/java/libcore/tlswire/handshake/EllipticCurve.java b/support/src/test/java/libcore/tlswire/handshake/EllipticCurve.java
new file mode 100644
index 0000000..a409c41
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/handshake/EllipticCurve.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed 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
+ *
+ * http://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 libcore.tlswire.handshake;
+
+/**
+ * {@code EllipticCurve} enum from RFC 4492 section 5.1.1. Curves are assigned
+ * via the
+ * <a href="https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8">IANA registry</a>.
+ */
+public enum EllipticCurve {
+ SECT163K1(1, "sect163k1"),
+ SECT163R1(2, "sect163r1"),
+ SECT163R2(3, "sect163r2"),
+ SECT193R1(4, "sect193r1"),
+ SECT193R2(5, "sect193r2"),
+ SECT233K1(6, "sect233k1"),
+ SECT233R1(7, "sect233r1"),
+ SECT239K1(8, "sect239k1"),
+ SECT283K1(9, "sect283k1"),
+ SECT283R1(10, "sect283r1"),
+ SECT409K1(11, "sect409k1"),
+ SECT409R1(12, "sect409r1"),
+ SECT571K1(13, "sect571k1"),
+ SECT571R1(14, "sect571r1"),
+ SECP160K1(15, "secp160k1"),
+ SECP160R1(16, "secp160r1"),
+ SECP160R2(17, "secp160r2"),
+ SECP192K1(18, "secp192k1"),
+ SECP192R1(19, "secp192r1"),
+ SECP224K1(20, "secp224k1"),
+ SECP256K1(22, "secp256k1"),
+ SECP256R1(23, "secp256r1"),
+ SECP384R1(24, "secp384r1"),
+ SECP521R1(25, "secp521r1"),
+ BRAINPOOLP256R1(26, "brainpoolP256r1"),
+ BRAINPOOLP384R1(27, "brainpoolP384r1"),
+ BRAINPOOLP521R1(28, "brainpoolP521r1"),
+ X25519(29, "x25519"),
+ X448(30, "x448"),
+ ARBITRARY_PRIME(0xFF01, "arbitrary_explicit_prime_curves"),
+ ARBITRARY_CHAR2(0xFF02, "arbitrary_explicit_char2_curves");
+
+ public final int identifier;
+ public final String name;
+
+ private EllipticCurve(int identifier, String name) {
+ this.identifier = identifier;
+ this.name = name;
+ }
+
+ public static EllipticCurve fromIdentifier(int identifier) {
+ for (EllipticCurve curve : values()) {
+ if (curve.identifier == identifier) {
+ return curve;
+ }
+ }
+ throw new AssertionError("Unknown curve identifier " + identifier);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(name);
+ sb.append(" (");
+ sb.append(identifier);
+ sb.append(')');
+ return sb.toString();
+ }
+}
diff --git a/support/src/test/java/libcore/tlswire/handshake/EllipticCurvesHelloExtension.java b/support/src/test/java/libcore/tlswire/handshake/EllipticCurvesHelloExtension.java
new file mode 100644
index 0000000..19749a3
--- /dev/null
+++ b/support/src/test/java/libcore/tlswire/handshake/EllipticCurvesHelloExtension.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed 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
+ *
+ * http://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 libcore.tlswire.handshake;
+
+import libcore.tlswire.util.IoUtils;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@code elliptic_curves} {@link HelloExtension} from RFC 4492 section 5.1.1.
+ */
+public class EllipticCurvesHelloExtension extends HelloExtension {
+ public List<EllipticCurve> supported;
+ public boolean wellFormed;
+
+ @Override
+ protected void parseData() throws IOException {
+ byte[] ellipticCurvesListBytes = IoUtils.readTlsVariableLengthByteVector(
+ new DataInputStream(new ByteArrayInputStream(data)), 0xffff);
+ ByteArrayInputStream ellipticCurvesListIn = new ByteArrayInputStream(ellipticCurvesListBytes);
+ DataInputStream in = new DataInputStream(ellipticCurvesListIn);
+ wellFormed = (ellipticCurvesListIn.available() % 2) == 0;
+ supported = new ArrayList<EllipticCurve>(ellipticCurvesListIn.available() / 2);
+ while (ellipticCurvesListIn.available() >= 2) {
+ int curve_id = in.readUnsignedShort();
+ supported.add(EllipticCurve.fromIdentifier(curve_id));
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("HelloExtension{type: elliptic_curves, wellFormed: ");
+ sb.append(wellFormed);
+ sb.append(", supported: ");
+ sb.append(supported);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/support/src/test/java/libcore/tlswire/handshake/HelloExtension.java b/support/src/test/java/libcore/tlswire/handshake/HelloExtension.java
index a648cdf..2a77687 100644
--- a/support/src/test/java/libcore/tlswire/handshake/HelloExtension.java
+++ b/support/src/test/java/libcore/tlswire/handshake/HelloExtension.java
@@ -29,6 +29,7 @@
public class HelloExtension {
public static final int TYPE_SERVER_NAME = 0;
+ public static final int TYPE_ELLIPTIC_CURVES = 10;
public static final int TYPE_PADDING = 21;
public static final int TYPE_SESSION_TICKET = 35;
public static final int TYPE_RENEGOTIATION_INFO = 65281;
@@ -45,7 +46,7 @@
TYPE_TO_NAME.put(7, "client_authz");
TYPE_TO_NAME.put(8, "server_authz");
TYPE_TO_NAME.put(9, "cert_type");
- TYPE_TO_NAME.put(10, "elliptic_curves");
+ TYPE_TO_NAME.put(TYPE_ELLIPTIC_CURVES, "elliptic_curves");
TYPE_TO_NAME.put(11, "ec_point_formats");
TYPE_TO_NAME.put(12, "srp");
TYPE_TO_NAME.put(13, "signature_algorithms");
@@ -75,6 +76,9 @@
case TYPE_SERVER_NAME:
result = new ServerNameHelloExtension();
break;
+ case TYPE_ELLIPTIC_CURVES:
+ result = new EllipticCurvesHelloExtension();
+ break;
default:
result = new HelloExtension();
break;