summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java199
-rw-r--r--support/src/test/java/libcore/tlswire/handshake/CipherSuite.java105
-rw-r--r--support/src/test/java/libcore/tlswire/handshake/ServerNameHelloExtension.java2
-rw-r--r--support/src/test/java/tests/util/DelegatingSSLSocketFactory.java101
-rw-r--r--support/src/test/java/tests/util/ForEachRunner.java54
-rw-r--r--support/src/test/java/tests/util/Pair.java107
6 files changed, 470 insertions, 98 deletions
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 dabcdc6..8e4519d 100644
--- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java
@@ -28,15 +28,18 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
-import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -68,9 +71,18 @@ import libcore.io.IoUtils;
import libcore.io.Streams;
import libcore.java.security.StandardNames;
import libcore.java.security.TestKeyStore;
+import libcore.tlswire.handshake.CipherSuite;
+import libcore.tlswire.handshake.ClientHello;
+import libcore.tlswire.handshake.CompressionMethod;
import libcore.tlswire.handshake.HandshakeMessage;
+import libcore.tlswire.handshake.HelloExtension;
+import libcore.tlswire.handshake.ServerNameHelloExtension;
import libcore.tlswire.record.TlsProtocols;
import libcore.tlswire.record.TlsRecord;
+import libcore.tlswire.util.TlsProtocolVersion;
+import tests.util.ForEachRunner;
+import tests.util.DelegatingSSLSocketFactory;
+import tests.util.Pair;
public class SSLSocketTest extends TestCase {
@@ -1463,11 +1475,147 @@ public class SSLSocketTest extends TestCase {
test.close();
}
- public void test_SSLSocket_ClientHello_size() throws Exception {
+ public void test_SSLSocket_ClientHello_record_size() throws Exception {
// This test checks the size of ClientHello of the default SSLSocket. TLS/SSL handshakes
// with older/unpatched F5/BIG-IP appliances are known to stall and time out when
// the fragment containing ClientHello is between 256 and 511 (inclusive) bytes long.
- //
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, null, null);
+ SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+ sslSocketFactory = new DelegatingSSLSocketFactory(sslSocketFactory) {
+ @Override
+ protected void configureSocket(SSLSocket socket) {
+ // Enable SNI extension on the socket (this is typically enabled by default)
+ // to increase the size of ClientHello.
+ try {
+ Method setHostname =
+ socket.getClass().getMethod("setHostname", String.class);
+ setHostname.invoke(socket, "sslsockettest.androidcts.google.com");
+ } catch (NoSuchMethodException ignored) {
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to enable SNI", e);
+ }
+
+ // Enable Session Tickets extension on the socket (this is typically enabled
+ // by default) to increase the size of ClientHello.
+ try {
+ Method setUseSessionTickets =
+ socket.getClass().getMethod(
+ "setUseSessionTickets", boolean.class);
+ setUseSessionTickets.invoke(socket, true);
+ } catch (NoSuchMethodException ignored) {
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to enable Session Tickets", e);
+ }
+ }
+ };
+
+ TlsRecord firstReceivedTlsRecord = captureTlsHandshakeFirstTlsRecord(sslSocketFactory);
+ assertEquals("TLS record type", TlsProtocols.HANDSHAKE, firstReceivedTlsRecord.type);
+ HandshakeMessage handshakeMessage = HandshakeMessage.read(
+ new DataInputStream(new ByteArrayInputStream(firstReceivedTlsRecord.fragment)));
+ assertEquals("HandshakeMessage type",
+ HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
+ int fragmentLength = firstReceivedTlsRecord.fragment.length;
+ if ((fragmentLength >= 256) && (fragmentLength <= 511)) {
+ fail("Fragment containing ClientHello is of dangerous length: "
+ + fragmentLength + " bytes");
+ }
+ }
+
+ public void test_SSLSocket_ClientHello_cipherSuites() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ String[] cipherSuites = new String[clientHello.cipherSuites.size()];
+ for (int i = 0; i < clientHello.cipherSuites.size(); i++) {
+ CipherSuite cipherSuite = clientHello.cipherSuites.get(i);
+ cipherSuites[i] = cipherSuite.getAndroidName();
+ }
+ StandardNames.assertDefaultCipherSuites(cipherSuites);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ public void test_SSLSocket_ClientHello_clientProtocolVersion() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ assertEquals(TlsProtocolVersion.TLSv1_2, clientHello.clientVersion);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ public void test_SSLSocket_ClientHello_compressionMethods() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ assertEquals(Arrays.asList(CompressionMethod.NULL), clientHello.compressionMethods);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ public void test_SSLSocket_ClientHello_SNI() throws Exception {
+ ForEachRunner.runNamed(new ForEachRunner.Callback<SSLSocketFactory>() {
+ @Override
+ public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+ ClientHello clientHello = captureTlsHandshakeClientHello(sslSocketFactory);
+ ServerNameHelloExtension sniExtension = (ServerNameHelloExtension)
+ clientHello.findExtensionByType(HelloExtension.TYPE_SERVER_NAME);
+ assertNotNull(sniExtension);
+ assertEquals(Arrays.asList("localhost.localdomain"), sniExtension.hostnames);
+ }
+ }, getSSLSocketFactoriesToTest());
+ }
+
+ private List<Pair<String, SSLSocketFactory>> getSSLSocketFactoriesToTest()
+ throws NoSuchAlgorithmException, KeyManagementException {
+ List<Pair<String, SSLSocketFactory>> result =
+ new ArrayList<Pair<String, SSLSocketFactory>>();
+ result.add(Pair.of("default", (SSLSocketFactory) SSLSocketFactory.getDefault()));
+ for (String sslContextProtocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+ SSLContext sslContext = SSLContext.getInstance(sslContextProtocol);
+ if (StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT.equals(sslContextProtocol)) {
+ continue;
+ }
+ sslContext.init(null, null, null);
+ result.add(Pair.of(
+ "SSLContext(\"" + sslContext.getProtocol() + "\")",
+ sslContext.getSocketFactory()));
+ }
+ return result;
+ }
+
+ private ClientHello captureTlsHandshakeClientHello(SSLSocketFactory sslSocketFactory)
+ throws Exception {
+ TlsRecord record = captureTlsHandshakeFirstTlsRecord(sslSocketFactory);
+ assertEquals("TLS record type", TlsProtocols.HANDSHAKE, record.type);
+ ByteArrayInputStream fragmentIn = new ByteArrayInputStream(record.fragment);
+ HandshakeMessage handshakeMessage = HandshakeMessage.read(new DataInputStream(fragmentIn));
+ assertEquals("HandshakeMessage type",
+ HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
+ // Assert that the fragment does not contain any more messages
+ assertEquals(0, fragmentIn.available());
+
+ return (ClientHello) handshakeMessage;
+ }
+
+ private TlsRecord captureTlsHandshakeFirstTlsRecord(SSLSocketFactory sslSocketFactory)
+ throws Exception {
+ byte[] firstReceivedChunk = captureTlsHandshakeFirstTransmittedChunkBytes(sslSocketFactory);
+ ByteArrayInputStream firstReceivedChunkIn = new ByteArrayInputStream(firstReceivedChunk);
+ TlsRecord record = TlsRecord.read(new DataInputStream(firstReceivedChunkIn));
+ // Assert that the chunk does not contain any more data
+ assertEquals(0, firstReceivedChunkIn.available());
+
+ return record;
+ }
+
+ private byte[] captureTlsHandshakeFirstTransmittedChunkBytes(
+ final SSLSocketFactory sslSocketFactory) throws Exception {
// Since there's no straightforward way to obtain a ClientHello from SSLSocket, this test
// does the following:
// 1. Creates a listening server socket (a plain one rather than a TLS/SSL one).
@@ -1511,33 +1659,19 @@ public class SSLSocketTest extends TestCase {
executorService.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(null, null, null);
- SSLSocket client = (SSLSocket) sslContext.getSocketFactory().createSocket();
+ Socket client = new Socket();
sockets[0] = client;
try {
- // Enable SNI extension on the socket (this is typically enabled by default)
- // to increase the size of ClientHello.
- try {
- Method setHostname =
- client.getClass().getMethod("setHostname", String.class);
- setHostname.invoke(client, "sslsockettest.androidcts.google.com");
- } catch (NoSuchMethodException ignored) {}
-
- // Enable Session Tickets extension on the socket (this is typically enabled
- // by default) to increase the size of ClientHello.
- try {
- Method setUseSessionTickets =
- client.getClass().getMethod(
- "setUseSessionTickets", boolean.class);
- setUseSessionTickets.invoke(client, true);
- } catch (NoSuchMethodException ignored) {}
-
client.connect(finalListeningSocket.getLocalSocketAddress());
// Initiate the TLS/SSL handshake which is expected to fail as soon as the
// server socket receives a ClientHello.
try {
- client.startHandshake();
+ SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
+ client,
+ "localhost.localdomain",
+ finalListeningSocket.getLocalPort(),
+ true);
+ sslSocket.startHandshake();
fail();
return null;
} catch (IOException expected) {}
@@ -1556,22 +1690,7 @@ public class SSLSocketTest extends TestCase {
});
// Wait for the ClientHello to arrive
- byte[] firstReceivedChunk = readFirstReceivedChunkFuture.get(10, TimeUnit.SECONDS);
-
- // Check for ClientHello length that may cause handshake to fail/time out with older
- // F5/BIG-IP appliances.
- TlsRecord firstReceivedTlsRecord = TlsRecord.read(
- new DataInputStream(new ByteArrayInputStream(firstReceivedChunk)));
- assertEquals("TLS record type", TlsProtocols.HANDSHAKE, firstReceivedTlsRecord.type);
- HandshakeMessage handshakeMessage = HandshakeMessage.read(
- new DataInputStream(new ByteArrayInputStream(firstReceivedTlsRecord.fragment)));
- assertEquals("HandshakeMessage type",
- HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
- int fragmentLength = firstReceivedTlsRecord.fragment.length;
- if ((fragmentLength >= 256) && (fragmentLength <= 511)) {
- fail("Fragment containing ClientHello is of dangerous length: "
- + fragmentLength + " bytes");
- }
+ return readFirstReceivedChunkFuture.get(10, TimeUnit.SECONDS);
} finally {
executorService.shutdownNow();
IoUtils.closeQuietly(listeningSocket);
diff --git a/support/src/test/java/libcore/tlswire/handshake/CipherSuite.java b/support/src/test/java/libcore/tlswire/handshake/CipherSuite.java
index 959379d..88ce2f2 100644
--- a/support/src/test/java/libcore/tlswire/handshake/CipherSuite.java
+++ b/support/src/test/java/libcore/tlswire/handshake/CipherSuite.java
@@ -27,33 +27,41 @@ public class CipherSuite {
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
private static final CipherSuite[] CIPHER_SUITES = new CipherSuite[] {
new CipherSuite(0x0000, "TLS_NULL_WITH_NULL_NULL"),
- new CipherSuite(0x0001, "TLS_RSA_WITH_NULL_MD5"),
- new CipherSuite(0x0002, "TLS_RSA_WITH_NULL_SHA"),
- new CipherSuite(0x0003, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"),
- new CipherSuite(0x0004, "TLS_RSA_WITH_RC4_128_MD5"),
- new CipherSuite(0x0005, "TLS_RSA_WITH_RC4_128_SHA"),
+ new CipherSuite(0x0001, "TLS_RSA_WITH_NULL_MD5", "SSL_RSA_WITH_NULL_MD5"),
+ new CipherSuite(0x0002, "TLS_RSA_WITH_NULL_SHA", "SSL_RSA_WITH_NULL_SHA"),
+ new CipherSuite(0x0003, "TLS_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_RSA_EXPORT_WITH_RC4_40_MD5"),
+ new CipherSuite(0x0004, "TLS_RSA_WITH_RC4_128_MD5", "SSL_RSA_WITH_RC4_128_MD5"),
+ new CipherSuite(0x0005, "TLS_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_RC4_128_SHA"),
new CipherSuite(0x0006, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"),
new CipherSuite(0x0007, "TLS_RSA_WITH_IDEA_CBC_SHA"),
- new CipherSuite(0x0008, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"),
- new CipherSuite(0x0009, "TLS_RSA_WITH_DES_CBC_SHA"),
- new CipherSuite(0x000a, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"),
+ new CipherSuite(0x0008, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA"),
+ new CipherSuite(0x0009, "TLS_RSA_WITH_DES_CBC_SHA", "SSL_RSA_WITH_DES_CBC_SHA"),
+ new CipherSuite(0x000a, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"),
new CipherSuite(0x000b, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"),
new CipherSuite(0x000c, "TLS_DH_DSS_WITH_DES_CBC_SHA"),
new CipherSuite(0x000d, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"),
new CipherSuite(0x000e, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"),
new CipherSuite(0x000f, "TLS_DH_RSA_WITH_DES_CBC_SHA"),
new CipherSuite(0x0010, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"),
- new CipherSuite(0x0011, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"),
- new CipherSuite(0x0012, "TLS_DHE_DSS_WITH_DES_CBC_SHA"),
- new CipherSuite(0x0013, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"),
- new CipherSuite(0x0014, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"),
- new CipherSuite(0x0015, "TLS_DHE_RSA_WITH_DES_CBC_SHA"),
- new CipherSuite(0x0016, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"),
- new CipherSuite(0x0017, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"),
- new CipherSuite(0x0018, "TLS_DH_anon_WITH_RC4_128_MD5"),
- new CipherSuite(0x0019, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"),
- new CipherSuite(0x001a, "TLS_DH_anon_WITH_DES_CBC_SHA"),
- new CipherSuite(0x001b, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"),
+ new CipherSuite(0x0011, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"),
+ new CipherSuite(0x0012, "TLS_DHE_DSS_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA"),
+ new CipherSuite(0x0013, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"),
+ new CipherSuite(0x0014, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"),
+ new CipherSuite(0x0015, "TLS_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_RSA_WITH_DES_CBC_SHA"),
+ new CipherSuite(0x0016, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA"),
+ new CipherSuite(0x0017, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5",
+ "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5"),
+ new CipherSuite(0x0018, "TLS_DH_anon_WITH_RC4_128_MD5", "SSL_DH_anon_WITH_RC4_128_MD5"),
+ new CipherSuite(0x0019, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"),
+ new CipherSuite(0x001a, "TLS_DH_anon_WITH_DES_CBC_SHA", "SSL_DH_anon_WITH_DES_CBC_SHA"),
+ new CipherSuite(0x001b, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA"),
new CipherSuite(0x001e, "TLS_KRB5_WITH_DES_CBC_SHA"),
new CipherSuite(0x001f, "TLS_KRB5_WITH_3DES_EDE_CBC_SHA"),
new CipherSuite(0x0020, "TLS_KRB5_WITH_RC4_128_SHA"),
@@ -369,59 +377,38 @@ public class CipherSuite {
throw new RuntimeException(
"Cipher suite multiply defined: " + Integer.toHexString(cipherSuite.code));
}
- if (byName.put(cipherSuite.name, cipherSuite) != null) {
+ String name = cipherSuite.name;
+ if (byName.put(name, cipherSuite) != null) {
throw new RuntimeException(
"Cipher suite multiply defined: " + cipherSuite.name);
}
+ String androidName = cipherSuite.getAndroidName();
+ if (!name.equals(androidName)) {
+ if (byName.put(androidName, cipherSuite) != null) {
+ throw new RuntimeException(
+ "Cipher suite multiply defined: " + cipherSuite.androidName);
+ }
+ }
}
- // Add alternative names used in Android's platform-default TLS/SSL stack.
- addAltName(byName,
- "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
- addAltName(byName,
- "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA");
- addAltName(byName, "TLS_DHE_DSS_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA");
- addAltName(byName,
- "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA");
- addAltName(byName,
- "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA");
- addAltName(byName, "TLS_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_RSA_WITH_DES_CBC_SHA");
- addAltName(byName,
- "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA");
- addAltName(byName,
- "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5");
- addAltName(byName,
- "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA", "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA");
- addAltName(byName, "TLS_DH_anon_WITH_DES_CBC_SHA", "SSL_DH_anon_WITH_DES_CBC_SHA");
- addAltName(byName, "TLS_DH_anon_WITH_RC4_128_MD5", "SSL_DH_anon_WITH_RC4_128_MD5");
- addAltName(byName,
- "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA");
- addAltName(byName, "TLS_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_RSA_EXPORT_WITH_RC4_40_MD5");
- addAltName(byName, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
- addAltName(byName, "TLS_RSA_WITH_DES_CBC_SHA", "SSL_RSA_WITH_DES_CBC_SHA");
- addAltName(byName, "TLS_RSA_WITH_NULL_MD5", "SSL_RSA_WITH_NULL_MD5");
- addAltName(byName, "TLS_RSA_WITH_NULL_SHA", "SSL_RSA_WITH_NULL_SHA");
- addAltName(byName, "TLS_RSA_WITH_RC4_128_MD5", "SSL_RSA_WITH_RC4_128_MD5");
- addAltName(byName, "TLS_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_RC4_128_SHA");
-
CODE_TO_CIPHER_SUITE = byCode;
NAME_TO_CIPHER_SUITE = byName;
}
- private static void addAltName(Map<String, CipherSuite> byName, String name, String altName) {
- CipherSuite cipherSuite = byName.get(name);
- if (cipherSuite == null) {
- throw new IllegalArgumentException("Cipher suite not found: " + name);
- }
- byName.put(altName, cipherSuite);
- }
-
public final int code;
public final String name;
+ private final String androidName;
private CipherSuite(int code, String name) {
this.code = code;
this.name = name;
+ this.androidName = null;
+ }
+
+ private CipherSuite(int code, String name, String androidName) {
+ this.code = code;
+ this.name = name;
+ this.androidName = androidName;
}
public static CipherSuite valueOf(String name) {
@@ -440,6 +427,10 @@ public class CipherSuite {
return new CipherSuite(code, Integer.toHexString(code));
}
+ public String getAndroidName() {
+ return (androidName != null) ? androidName : name;
+ }
+
@Override
public String toString() {
return name;
diff --git a/support/src/test/java/libcore/tlswire/handshake/ServerNameHelloExtension.java b/support/src/test/java/libcore/tlswire/handshake/ServerNameHelloExtension.java
index afc9cfe..5b06246 100644
--- a/support/src/test/java/libcore/tlswire/handshake/ServerNameHelloExtension.java
+++ b/support/src/test/java/libcore/tlswire/handshake/ServerNameHelloExtension.java
@@ -29,7 +29,7 @@ import java.util.List;
public class ServerNameHelloExtension extends HelloExtension {
private static final int TYPE_HOST_NAME = 0;
- private List<String> hostnames;
+ public List<String> hostnames;
@Override
protected void parseData() throws IOException {
diff --git a/support/src/test/java/tests/util/DelegatingSSLSocketFactory.java b/support/src/test/java/tests/util/DelegatingSSLSocketFactory.java
new file mode 100644
index 0000000..5513210
--- /dev/null
+++ b/support/src/test/java/tests/util/DelegatingSSLSocketFactory.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 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 tests.util;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * {@link SSLSocketFactory} which delegates all invocations to the provided delegate
+ * {@code SSLSocketFactory}.
+ */
+public class DelegatingSSLSocketFactory extends SSLSocketFactory {
+
+ private final SSLSocketFactory mDelegate;
+
+ public DelegatingSSLSocketFactory(SSLSocketFactory delegate) {
+ this.mDelegate = delegate;
+ }
+
+ /**
+ * Invoked after obtaining a socket from the delegate and before returning it to the caller.
+ *
+ * <p>The default implementation does nothing.
+ */
+ protected void configureSocket(@SuppressWarnings("unused") SSLSocket socket) {}
+
+ @Override
+ public String[] getDefaultCipherSuites() {
+ return mDelegate.getDefaultCipherSuites();
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ return mDelegate.getSupportedCipherSuites();
+ }
+
+ @Override
+ public Socket createSocket() throws IOException {
+ SSLSocket socket = (SSLSocket) mDelegate.createSocket();
+ configureSocket(socket);
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+ throws IOException {
+ SSLSocket socket = (SSLSocket) mDelegate.createSocket(s, host, port, autoClose);
+ configureSocket(socket);
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+ SSLSocket socket = (SSLSocket) mDelegate.createSocket(host, port);
+ configureSocket(socket);
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
+ throws IOException, UnknownHostException {
+ SSLSocket socket = (SSLSocket) mDelegate.createSocket(host, port, localHost, localPort);
+ configureSocket(socket);
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ SSLSocket socket = (SSLSocket) mDelegate.createSocket(host, port);
+ configureSocket(socket);
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+ int localPort) throws IOException {
+ SSLSocket socket =
+ (SSLSocket) mDelegate.createSocket(address, port, localAddress, localPort);
+ configureSocket(socket);
+ return socket;
+ }
+}
diff --git a/support/src/test/java/tests/util/ForEachRunner.java b/support/src/test/java/tests/util/ForEachRunner.java
new file mode 100644
index 0000000..2c222b2
--- /dev/null
+++ b/support/src/test/java/tests/util/ForEachRunner.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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 tests.util;
+
+/**
+ * Runner which executes the provided code under test (via a callback) for each provided input
+ * value.
+ */
+public final class ForEachRunner {
+
+ /**
+ * Callback parameterized with a value.
+ */
+ public interface Callback<T> {
+ /**
+ * Invokes the callback for the provided value.
+ */
+ void run(T value) throws Exception;
+ }
+
+ private ForEachRunner() {}
+
+ /**
+ * Invokes the provided callback for each of the provided named values.
+ *
+ * @param namesAndValues named values represented as name-value pairs.
+ *
+ * @param <T> type of value.
+ */
+ public static <T> void runNamed(Callback<T> callback, Iterable<Pair<String, T>> namesAndValues)
+ throws Exception {
+ for (Pair<String, T> nameAndValue : namesAndValues) {
+ try {
+ callback.run(nameAndValue.getSecond());
+ } catch (Throwable e) {
+ throw new Exception("Failed for " + nameAndValue.getFirst() + ": " + e.getMessage(), e);
+ }
+ }
+ }
+}
diff --git a/support/src/test/java/tests/util/Pair.java b/support/src/test/java/tests/util/Pair.java
new file mode 100644
index 0000000..9b0906d
--- /dev/null
+++ b/support/src/test/java/tests/util/Pair.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 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 tests.util;
+
+/**
+ * Pair of typed values.
+ *
+ * <p>Pairs are obtained using {@link #of(Object, Object) of}.
+ *
+ * @param <F> type of the first value.
+ * @param <S> type of the second value.
+ */
+public class Pair<F, S> {
+ private final F mFirst;
+ private final S mSecond;
+
+ private Pair(F first, S second) {
+ mFirst = first;
+ mSecond = second;
+ }
+
+ /**
+ * Gets the pair consisting of the two provided values.
+ *
+ * @param first first value or {@code null}.
+ * @param second second value or {@code null}.
+ */
+ public static <F, S> Pair<F, S> of(F first, S second) {
+ return new Pair<F, S>(first, second);
+ }
+
+ /**
+ * Gets the first value from this pair.
+ *
+ * @return value or {@code null}.
+ */
+ public F getFirst() {
+ return mFirst;
+ }
+
+ /**
+ * Gets the second value from this pair.
+ *
+ * @return value or {@code null}.
+ */
+ public S getSecond() {
+ return mSecond;
+ }
+
+ @Override
+ public String toString() {
+ return "Pair[" + mFirst + ", " + mSecond + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mFirst == null) ? 0 : mFirst.hashCode());
+ result = prime * result + ((mSecond == null) ? 0 : mSecond.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ @SuppressWarnings("rawtypes")
+ Pair other = (Pair) obj;
+ if (mFirst == null) {
+ if (other.mFirst != null) {
+ return false;
+ }
+ } else if (!mFirst.equals(other.mFirst)) {
+ return false;
+ }
+ if (mSecond == null) {
+ if (other.mSecond != null) {
+ return false;
+ }
+ } else if (!mSecond.equals(other.mSecond)) {
+ return false;
+ }
+ return true;
+ }
+}