summaryrefslogtreecommitdiffstats
path: root/support/src
diff options
context:
space:
mode:
authorAlex Klyubin <klyubin@google.com>2014-12-02 11:15:42 -0800
committerAlex Klyubin <klyubin@google.com>2015-01-22 15:51:24 -0800
commitb1fe85cc976c676eb50ff886596c93e04fd71d82 (patch)
tree7435f453030ed20fce5f08b7e646c51fbe8acd10 /support/src
parent194940746a618d361b26838e2c6ff04c358adff7 (diff)
downloadlibcore-b1fe85cc976c676eb50ff886596c93e04fd71d82.zip
libcore-b1fe85cc976c676eb50ff886596c93e04fd71d82.tar.gz
libcore-b1fe85cc976c676eb50ff886596c93e04fd71d82.tar.bz2
Add SSLSocket tests which inspect emitted ClientHello fields.
This CL adds basic tests which capture the ClientHello emitted by SSLSocket and assert that the various fields are as expected. In particular, this CL adds tests for: * client protocol version, * cipher suite list, * compression methods, * server_name extension (SNI). Change-Id: I387c44363ad26f064885f9bfa28572da37871078
Diffstat (limited to 'support/src')
-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
5 files changed, 311 insertions, 58 deletions
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;
+ }
+}