ConnectionSpecTest.java revision 6c251e20f00c7574b217bd4351ac81666f574380
1/*
2 * Copyright (C) 2015 Square, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.squareup.okhttp;
17
18import java.util.Arrays;
19import java.util.LinkedHashSet;
20import java.util.Set;
21import java.util.concurrent.CopyOnWriteArraySet;
22import javax.net.ssl.SSLSocket;
23import javax.net.ssl.SSLSocketFactory;
24import org.junit.Test;
25
26import static org.junit.Assert.assertEquals;
27import static org.junit.Assert.assertFalse;
28import static org.junit.Assert.assertNull;
29import static org.junit.Assert.assertTrue;
30import static org.junit.Assert.fail;
31
32public final class ConnectionSpecTest {
33  @Test public void noTlsVersions() throws Exception {
34    try {
35      new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
36          .tlsVersions(new TlsVersion[0])
37          .build();
38      fail();
39    } catch (IllegalArgumentException expected) {
40      assertEquals("At least one TLS version is required", expected.getMessage());
41    }
42  }
43
44  @Test public void noCipherSuites() throws Exception {
45    try {
46      new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
47          .cipherSuites(new CipherSuite[0])
48          .build();
49      fail();
50    } catch (IllegalArgumentException expected) {
51      assertEquals("At least one cipher suite is required", expected.getMessage());
52    }
53  }
54
55  @Test public void cleartextBuilder() throws Exception {
56    ConnectionSpec cleartextSpec = new ConnectionSpec.Builder(false).build();
57    assertFalse(cleartextSpec.isTls());
58  }
59
60  @Test public void tlsBuilder_explicitCiphers() throws Exception {
61    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
62        .cipherSuites(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)
63        .tlsVersions(TlsVersion.TLS_1_2)
64        .supportsTlsExtensions(true)
65        .build();
66    assertEquals(Arrays.asList(CipherSuite.TLS_RSA_WITH_RC4_128_MD5), tlsSpec.cipherSuites());
67    assertEquals(Arrays.asList(TlsVersion.TLS_1_2), tlsSpec.tlsVersions());
68    assertTrue(tlsSpec.supportsTlsExtensions());
69  }
70
71  @Test public void tlsBuilder_defaultCiphers() throws Exception {
72    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
73        .tlsVersions(TlsVersion.TLS_1_2)
74        .supportsTlsExtensions(true)
75        .build();
76    assertNull(tlsSpec.cipherSuites());
77    assertEquals(Arrays.asList(TlsVersion.TLS_1_2), tlsSpec.tlsVersions());
78    assertTrue(tlsSpec.supportsTlsExtensions());
79  }
80
81  @Test public void tls_defaultCiphers_noFallbackIndicator() throws Exception {
82    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
83        .tlsVersions(TlsVersion.TLS_1_2)
84        .supportsTlsExtensions(false)
85        .build();
86
87    SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
88    socket.setEnabledCipherSuites(new String[] {
89        CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
90        CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
91    });
92    socket.setEnabledProtocols(new String[] {
93        TlsVersion.TLS_1_2.javaName,
94        TlsVersion.TLS_1_1.javaName,
95    });
96
97    assertTrue(tlsSpec.isCompatible(socket));
98    tlsSpec.apply(socket, false /* isFallback */);
99
100    assertEquals(set(TlsVersion.TLS_1_2.javaName), set(socket.getEnabledProtocols()));
101
102    Set<String> expectedCipherSet =
103        set(
104            CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
105            CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName);
106    assertEquals(expectedCipherSet, expectedCipherSet);
107  }
108
109  @Test public void tls_defaultCiphers_withFallbackIndicator() throws Exception {
110    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
111        .tlsVersions(TlsVersion.TLS_1_2)
112        .supportsTlsExtensions(false)
113        .build();
114
115    SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
116    socket.setEnabledCipherSuites(new String[] {
117        CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
118        CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
119    });
120    socket.setEnabledProtocols(new String[] {
121        TlsVersion.TLS_1_2.javaName,
122        TlsVersion.TLS_1_1.javaName,
123    });
124
125    assertTrue(tlsSpec.isCompatible(socket));
126    tlsSpec.apply(socket, true /* isFallback */);
127
128    assertEquals(set(TlsVersion.TLS_1_2.javaName), set(socket.getEnabledProtocols()));
129
130    Set<String> expectedCipherSet =
131        set(
132            CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
133            CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName);
134    if (Arrays.asList(socket.getSupportedCipherSuites()).contains("TLS_FALLBACK_SCSV")) {
135      expectedCipherSet.add("TLS_FALLBACK_SCSV");
136    }
137    assertEquals(expectedCipherSet, expectedCipherSet);
138  }
139
140  @Test public void tls_explicitCiphers() throws Exception {
141    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
142        .cipherSuites(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)
143        .tlsVersions(TlsVersion.TLS_1_2)
144        .supportsTlsExtensions(false)
145        .build();
146
147    SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
148    socket.setEnabledCipherSuites(new String[] {
149        CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
150        CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
151    });
152    socket.setEnabledProtocols(new String[] {
153        TlsVersion.TLS_1_2.javaName,
154        TlsVersion.TLS_1_1.javaName,
155    });
156
157    assertTrue(tlsSpec.isCompatible(socket));
158    tlsSpec.apply(socket, true /* isFallback */);
159
160    assertEquals(set(TlsVersion.TLS_1_2.javaName), set(socket.getEnabledProtocols()));
161
162    Set<String> expectedCipherSet = set(CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName);
163    if (Arrays.asList(socket.getSupportedCipherSuites()).contains("TLS_FALLBACK_SCSV")) {
164      expectedCipherSet.add("TLS_FALLBACK_SCSV");
165    }
166    assertEquals(expectedCipherSet, expectedCipherSet);
167  }
168
169  @Test public void tls_stringCiphersAndVersions() throws Exception {
170    // Supporting arbitrary input strings allows users to enable suites and versions that are not
171    // yet known to the library, but are supported by the platform.
172    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
173        .cipherSuites("MAGIC-CIPHER")
174        .tlsVersions("TLS9k")
175        .build();
176  }
177
178  @Test public void tls_missingRequiredCipher() throws Exception {
179    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
180        .cipherSuites(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)
181        .tlsVersions(TlsVersion.TLS_1_2)
182        .supportsTlsExtensions(false)
183        .build();
184
185    SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
186    socket.setEnabledProtocols(new String[] {
187        TlsVersion.TLS_1_2.javaName,
188        TlsVersion.TLS_1_1.javaName,
189    });
190
191    socket.setEnabledCipherSuites(new String[] {
192        CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
193        CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
194    });
195    assertTrue(tlsSpec.isCompatible(socket));
196
197    socket.setEnabledCipherSuites(new String[] {
198        CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
199    });
200    assertFalse(tlsSpec.isCompatible(socket));
201  }
202
203  @Test public void allEnabledCipherSuites() throws Exception {
204    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
205        .allEnabledCipherSuites()
206        .build();
207    assertNull(tlsSpec.cipherSuites());
208
209    SSLSocket sslSocket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
210    sslSocket.setEnabledCipherSuites(new String[] {
211        CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
212        CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
213    });
214
215    tlsSpec.apply(sslSocket, false);
216    assertEquals(Arrays.asList(
217            CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
218            CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName),
219        Arrays.asList(sslSocket.getEnabledCipherSuites()));
220  }
221
222  @Test public void allEnabledTlsVersions() throws Exception {
223    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
224        .allEnabledTlsVersions()
225        .build();
226    assertNull(tlsSpec.tlsVersions());
227
228    SSLSocket sslSocket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
229    sslSocket.setEnabledProtocols(new String[] {
230        TlsVersion.SSL_3_0.javaName(),
231        TlsVersion.TLS_1_1.javaName()
232    });
233
234    tlsSpec.apply(sslSocket, false);
235    assertEquals(Arrays.asList(TlsVersion.SSL_3_0.javaName(), TlsVersion.TLS_1_1.javaName()),
236        Arrays.asList(sslSocket.getEnabledProtocols()));
237  }
238
239  @Test public void tls_missingTlsVersion() throws Exception {
240    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
241        .cipherSuites(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)
242        .tlsVersions(TlsVersion.TLS_1_2)
243        .supportsTlsExtensions(false)
244        .build();
245
246    SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
247    socket.setEnabledCipherSuites(new String[] {
248        CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
249    });
250
251    socket.setEnabledProtocols(
252        new String[] { TlsVersion.TLS_1_2.javaName, TlsVersion.TLS_1_1.javaName });
253    assertTrue(tlsSpec.isCompatible(socket));
254
255    socket.setEnabledProtocols(new String[] { TlsVersion.TLS_1_1.javaName });
256    assertFalse(tlsSpec.isCompatible(socket));
257  }
258
259  @Test public void equalsAndHashCode() throws Exception {
260    ConnectionSpec allCipherSuites = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
261        .allEnabledCipherSuites()
262        .build();
263    ConnectionSpec allTlsVersions = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
264        .allEnabledTlsVersions()
265        .build();
266
267    Set<Object> set = new CopyOnWriteArraySet<>();
268    assertTrue(set.add(ConnectionSpec.MODERN_TLS));
269    assertTrue(set.add(ConnectionSpec.COMPATIBLE_TLS));
270    assertTrue(set.add(ConnectionSpec.CLEARTEXT));
271    assertTrue(set.add(allTlsVersions));
272    assertTrue(set.add(allCipherSuites));
273
274    assertTrue(set.remove(ConnectionSpec.MODERN_TLS));
275    assertTrue(set.remove(ConnectionSpec.COMPATIBLE_TLS));
276    assertTrue(set.remove(ConnectionSpec.CLEARTEXT));
277    assertTrue(set.remove(allTlsVersions));
278    assertTrue(set.remove(allCipherSuites));
279    assertTrue(set.isEmpty());
280  }
281
282  @Test public void allEnabledToString() throws Exception {
283    ConnectionSpec connectionSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
284        .allEnabledTlsVersions()
285        .allEnabledCipherSuites()
286        .build();
287    assertEquals("ConnectionSpec(cipherSuites=[all enabled], tlsVersions=[all enabled], "
288        + "supportsTlsExtensions=true)", connectionSpec.toString());
289  }
290
291  @Test public void simpleToString() throws Exception {
292    ConnectionSpec connectionSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
293        .tlsVersions(TlsVersion.TLS_1_2)
294        .cipherSuites(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)
295        .build();
296    assertEquals("ConnectionSpec(cipherSuites=[TLS_RSA_WITH_RC4_128_MD5], tlsVersions=[TLS_1_2], "
297        + "supportsTlsExtensions=true)", connectionSpec.toString());
298  }
299
300  private static <T> Set<T> set(T... values) {
301    return new LinkedHashSet<>(Arrays.asList(values));
302  }
303}
304