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 org.junit.Test;
19
20import java.util.Arrays;
21import java.util.LinkedHashSet;
22import java.util.Set;
23import javax.net.ssl.SSLSocket;
24import javax.net.ssl.SSLSocketFactory;
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;
30
31public final class ConnectionSpecTest {
32
33  @Test
34  public void cleartextBuilder() throws Exception {
35    ConnectionSpec cleartextSpec = new ConnectionSpec.Builder(false).build();
36    assertFalse(cleartextSpec.isTls());
37  }
38
39  @Test
40  public void tlsBuilder_explicitCiphers() throws Exception {
41    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
42        .cipherSuites(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)
43        .tlsVersions(TlsVersion.TLS_1_2)
44        .supportsTlsExtensions(true)
45        .build();
46    assertEquals(Arrays.asList(CipherSuite.TLS_RSA_WITH_RC4_128_MD5), tlsSpec.cipherSuites());
47    assertEquals(Arrays.asList(TlsVersion.TLS_1_2), tlsSpec.tlsVersions());
48    assertTrue(tlsSpec.supportsTlsExtensions());
49  }
50
51  @Test
52  public void tlsBuilder_defaultCiphers() throws Exception {
53    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
54        .tlsVersions(TlsVersion.TLS_1_2)
55        .supportsTlsExtensions(true)
56        .build();
57    assertNull(tlsSpec.cipherSuites());
58    assertEquals(Arrays.asList(TlsVersion.TLS_1_2), tlsSpec.tlsVersions());
59    assertTrue(tlsSpec.supportsTlsExtensions());
60  }
61
62  @Test
63  public void tls_defaultCiphers_noFallbackIndicator() throws Exception {
64    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
65        .tlsVersions(TlsVersion.TLS_1_2)
66        .supportsTlsExtensions(false)
67        .build();
68
69    SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
70    socket.setEnabledCipherSuites(new String[] {
71        CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
72        CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
73    });
74    socket.setEnabledProtocols(new String[] {
75        TlsVersion.TLS_1_2.javaName,
76        TlsVersion.TLS_1_1.javaName,
77    });
78
79    assertTrue(tlsSpec.isCompatible(socket));
80    tlsSpec.apply(socket, false /* isFallback */);
81
82    assertEquals(createSet(TlsVersion.TLS_1_2.javaName), createSet(socket.getEnabledProtocols()));
83
84    Set<String> expectedCipherSet =
85        createSet(
86            CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
87            CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName);
88    assertEquals(expectedCipherSet, expectedCipherSet);
89  }
90
91  @Test
92  public void tls_defaultCiphers_withFallbackIndicator() throws Exception {
93    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
94        .tlsVersions(TlsVersion.TLS_1_2)
95        .supportsTlsExtensions(false)
96        .build();
97
98    SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
99    socket.setEnabledCipherSuites(new String[] {
100        CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
101        CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
102    });
103    socket.setEnabledProtocols(new String[] {
104        TlsVersion.TLS_1_2.javaName,
105        TlsVersion.TLS_1_1.javaName,
106    });
107
108    assertTrue(tlsSpec.isCompatible(socket));
109    tlsSpec.apply(socket, true /* isFallback */);
110
111    assertEquals(createSet(TlsVersion.TLS_1_2.javaName), createSet(socket.getEnabledProtocols()));
112
113    Set<String> expectedCipherSet =
114        createSet(
115            CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
116            CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName);
117    if (Arrays.asList(socket.getSupportedCipherSuites()).contains("TLS_FALLBACK_SCSV")) {
118      expectedCipherSet.add("TLS_FALLBACK_SCSV");
119    }
120    assertEquals(expectedCipherSet, expectedCipherSet);
121  }
122
123  @Test
124  public void tls_explicitCiphers() throws Exception {
125    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
126        .cipherSuites(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)
127        .tlsVersions(TlsVersion.TLS_1_2)
128        .supportsTlsExtensions(false)
129        .build();
130
131    SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
132    socket.setEnabledCipherSuites(new String[] {
133        CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
134        CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
135    });
136    socket.setEnabledProtocols(new String[] {
137        TlsVersion.TLS_1_2.javaName,
138        TlsVersion.TLS_1_1.javaName,
139    });
140
141    assertTrue(tlsSpec.isCompatible(socket));
142    tlsSpec.apply(socket, true /* isFallback */);
143
144    assertEquals(createSet(TlsVersion.TLS_1_2.javaName), createSet(socket.getEnabledProtocols()));
145
146    Set<String> expectedCipherSet = createSet(CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName);
147    if (Arrays.asList(socket.getSupportedCipherSuites()).contains("TLS_FALLBACK_SCSV")) {
148      expectedCipherSet.add("TLS_FALLBACK_SCSV");
149    }
150    assertEquals(expectedCipherSet, expectedCipherSet);
151  }
152
153  @Test
154  public void tls_stringCiphersAndVersions() throws Exception {
155    // Supporting arbitrary input strings allows users to enable suites and versions that are not
156    // yet known to the library, but are supported by the platform.
157    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
158        .cipherSuites("MAGIC-CIPHER")
159        .tlsVersions("TLS9k")
160        .build();
161  }
162
163  public void tls_missingRequiredCipher() throws Exception {
164    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
165        .cipherSuites(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)
166        .tlsVersions(TlsVersion.TLS_1_2)
167        .supportsTlsExtensions(false)
168        .build();
169
170    SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
171    socket.setEnabledProtocols(new String[] {
172        TlsVersion.TLS_1_2.javaName,
173        TlsVersion.TLS_1_1.javaName,
174    });
175
176    socket.setEnabledCipherSuites(new String[] {
177        CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
178        CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
179    });
180    assertTrue(tlsSpec.isCompatible(socket));
181
182    socket.setEnabledCipherSuites(new String[] {
183        CipherSuite.TLS_RSA_WITH_RC4_128_SHA.javaName,
184    });
185    assertFalse(tlsSpec.isCompatible(socket));
186  }
187
188  @Test
189  public void tls_missingTlsVersion() throws Exception {
190    ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true)
191        .cipherSuites(CipherSuite.TLS_RSA_WITH_RC4_128_MD5)
192        .tlsVersions(TlsVersion.TLS_1_2)
193        .supportsTlsExtensions(false)
194        .build();
195
196    SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
197    socket.setEnabledCipherSuites(new String[] {
198        CipherSuite.TLS_RSA_WITH_RC4_128_MD5.javaName,
199    });
200
201    socket.setEnabledProtocols(
202        new String[] { TlsVersion.TLS_1_2.javaName, TlsVersion.TLS_1_1.javaName });
203    assertTrue(tlsSpec.isCompatible(socket));
204
205    socket.setEnabledProtocols(new String[] { TlsVersion.TLS_1_1.javaName });
206    assertFalse(tlsSpec.isCompatible(socket));
207  }
208
209  private static Set<String> createSet(String... values) {
210    return new LinkedHashSet<String>(Arrays.asList(values));
211  }
212}
213