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 */ 16 package com.squareup.okhttp; 17 18 import java.util.Arrays; 19 import java.util.LinkedHashSet; 20 import java.util.Set; 21 import java.util.concurrent.CopyOnWriteArraySet; 22 import javax.net.ssl.SSLSocket; 23 import javax.net.ssl.SSLSocketFactory; 24 import org.junit.Assume; 25 import org.junit.Test; 26 27 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertFalse; 29 import static org.junit.Assert.assertNull; 30 import static org.junit.Assert.assertTrue; 31 import static org.junit.Assert.fail; 32 33 public final class ConnectionSpecTest { noTlsVersions()34 @Test public void noTlsVersions() throws Exception { 35 try { 36 new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 37 .tlsVersions(new TlsVersion[0]) 38 .build(); 39 fail(); 40 } catch (IllegalArgumentException expected) { 41 assertEquals("At least one TLS version is required", expected.getMessage()); 42 } 43 } 44 noCipherSuites()45 @Test public void noCipherSuites() throws Exception { 46 try { 47 new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 48 .cipherSuites(new CipherSuite[0]) 49 .build(); 50 fail(); 51 } catch (IllegalArgumentException expected) { 52 assertEquals("At least one cipher suite is required", expected.getMessage()); 53 } 54 } 55 cleartextBuilder()56 @Test public void cleartextBuilder() throws Exception { 57 ConnectionSpec cleartextSpec = new ConnectionSpec.Builder(false).build(); 58 assertFalse(cleartextSpec.isTls()); 59 } 60 tlsBuilder_explicitCiphers()61 @Test public void tlsBuilder_explicitCiphers() throws Exception { 62 ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true) 63 .cipherSuites(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA) 64 .tlsVersions(TlsVersion.TLS_1_2) 65 .supportsTlsExtensions(true) 66 .build(); 67 assertEquals(Arrays.asList(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA), tlsSpec.cipherSuites()); 68 assertEquals(Arrays.asList(TlsVersion.TLS_1_2), tlsSpec.tlsVersions()); 69 assertTrue(tlsSpec.supportsTlsExtensions()); 70 } 71 tlsBuilder_defaultCiphers()72 @Test public void tlsBuilder_defaultCiphers() throws Exception { 73 ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true) 74 .tlsVersions(TlsVersion.TLS_1_2) 75 .supportsTlsExtensions(true) 76 .build(); 77 assertNull(tlsSpec.cipherSuites()); 78 assertEquals(Arrays.asList(TlsVersion.TLS_1_2), tlsSpec.tlsVersions()); 79 assertTrue(tlsSpec.supportsTlsExtensions()); 80 } 81 tls_defaultCiphers_noFallbackIndicator()82 @Test public void tls_defaultCiphers_noFallbackIndicator() throws Exception { 83 ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true) 84 // Android-changed: Use TLS 1.3 and 1.2 for testing 85 .tlsVersions(TlsVersion.TLS_1_3) 86 .supportsTlsExtensions(false) 87 .build(); 88 89 SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); 90 socket.setEnabledCipherSuites(new String[] { 91 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.javaName, 92 // Android-changed: USe TLS 1.3 and 1.2 for testing - TLS 1.3 suites are implicit 93 // CipherSuite.TLS_AES_128_GCM_SHA384.javaName, 94 }); 95 socket.setEnabledProtocols(new String[] { 96 // Android-changed: Use TLS 1.3 and 1.2 for testing 97 TlsVersion.TLS_1_3.javaName, 98 TlsVersion.TLS_1_2.javaName, 99 }); 100 101 assertTrue(tlsSpec.isCompatible(socket)); 102 tlsSpec.apply(socket, false /* isFallback */); 103 104 // Android-changed: Use TLS 1.3 and 1.2 for testing 105 assertEquals(set(TlsVersion.TLS_1_3.javaName), set(socket.getEnabledProtocols())); 106 107 Set<String> expectedCipherSet = 108 set( 109 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.javaName, 110 // Android-changed: USe TLS 1.3 and 1.2 for testing - TLS 1.3 suites are implicit 111 CipherSuite.TLS_AES_128_GCM_SHA256.javaName, 112 CipherSuite.TLS_AES_256_GCM_SHA384.javaName, 113 CipherSuite.TLS_CHACHA20_POLY1305_SHA256.javaName); 114 assertEquals(expectedCipherSet, set(socket.getEnabledCipherSuites())); 115 } 116 tls_defaultCiphers_withFallbackIndicator()117 @Test public void tls_defaultCiphers_withFallbackIndicator() throws Exception { 118 ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true) 119 // Android-changed: Use TLS 1.3 and 1.2 for testing 120 .tlsVersions(TlsVersion.TLS_1_3) 121 .supportsTlsExtensions(false) 122 .build(); 123 124 SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); 125 socket.setEnabledCipherSuites(new String[] { 126 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.javaName, 127 // Android-changed: USe TLS 1.3 and 1.2 for testing - TLS 1.3 suites are implicit 128 // CipherSuite.TLS_AES_128_GCM_SHA384.javaName, 129 }); 130 socket.setEnabledProtocols(new String[] { 131 // Android-changed: Use TLS 1.3 and 1.2 for testing 132 TlsVersion.TLS_1_3.javaName, 133 TlsVersion.TLS_1_2.javaName, 134 }); 135 136 assertTrue(tlsSpec.isCompatible(socket)); 137 tlsSpec.apply(socket, true /* isFallback */); 138 139 // Android-changed: Use TLS 1.3 and 1.2 for testing 140 assertEquals(set(TlsVersion.TLS_1_3.javaName), set(socket.getEnabledProtocols())); 141 142 Set<String> expectedCipherSet = 143 set( 144 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.javaName, 145 // Android-changed: USe TLS 1.3 and 1.2 for testing - TLS 1.3 suites are implicit 146 CipherSuite.TLS_AES_128_GCM_SHA256.javaName, 147 CipherSuite.TLS_AES_256_GCM_SHA384.javaName, 148 CipherSuite.TLS_CHACHA20_POLY1305_SHA256.javaName); 149 if (Arrays.asList(socket.getSupportedCipherSuites()).contains("TLS_FALLBACK_SCSV")) { 150 expectedCipherSet.add("TLS_FALLBACK_SCSV"); 151 } 152 assertEquals(expectedCipherSet, set(socket.getEnabledCipherSuites())); 153 } 154 tls_explicitCiphers()155 @Test public void tls_explicitCiphers() throws Exception { 156 ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true) 157 .cipherSuites(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA) 158 .tlsVersions(TlsVersion.TLS_1_2) 159 .supportsTlsExtensions(false) 160 .build(); 161 162 SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); 163 socket.setEnabledCipherSuites(new String[] { 164 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.javaName, 165 // Android-changed: USe TLS 1.3 and 1.2 for testing - TLS 1.3 suites are implicit 166 // CipherSuite.TLS_AES_128_GCM_SHA384.javaName, 167 }); 168 socket.setEnabledProtocols(new String[] { 169 // Android-changed: Use TLS 1.3 and 1.2 for testing 170 TlsVersion.TLS_1_3.javaName, 171 TlsVersion.TLS_1_2.javaName, 172 }); 173 174 assertTrue(tlsSpec.isCompatible(socket)); 175 tlsSpec.apply(socket, true /* isFallback */); 176 177 assertEquals(set(TlsVersion.TLS_1_2.javaName), set(socket.getEnabledProtocols())); 178 179 Set<String> expectedCipherSet = set(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.javaName); 180 if (Arrays.asList(socket.getSupportedCipherSuites()).contains("TLS_FALLBACK_SCSV")) { 181 expectedCipherSet.add("TLS_FALLBACK_SCSV"); 182 } 183 assertEquals(expectedCipherSet, expectedCipherSet); 184 } 185 tls_stringCiphersAndVersions()186 @Test public void tls_stringCiphersAndVersions() throws Exception { 187 // Supporting arbitrary input strings allows users to enable suites and versions that are not 188 // yet known to the library, but are supported by the platform. 189 ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 190 .cipherSuites("MAGIC-CIPHER") 191 .tlsVersions("TLS9k") 192 .build(); 193 } 194 tls_missingRequiredCipher()195 @Test public void tls_missingRequiredCipher() throws Exception { 196 ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true) 197 .cipherSuites(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA) 198 .tlsVersions(TlsVersion.TLS_1_2) 199 .supportsTlsExtensions(false) 200 .build(); 201 202 SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); 203 socket.setEnabledProtocols(new String[] { 204 // Android-changed: Use TLS 1.3 and 1.2 for testing 205 TlsVersion.TLS_1_3.javaName, 206 TlsVersion.TLS_1_2.javaName, 207 }); 208 209 socket.setEnabledCipherSuites(new String[] { 210 // Android-changed: Replace removed CBC cipher with GCM version 211 CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256.javaName, 212 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.javaName, 213 }); 214 assertTrue(tlsSpec.isCompatible(socket)); 215 216 socket.setEnabledCipherSuites(new String[] { 217 // Android-changed: Replace removed CBC cipher with GCM version 218 CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256.javaName, 219 }); 220 assertFalse(tlsSpec.isCompatible(socket)); 221 } 222 allEnabledCipherSuites()223 @Test public void allEnabledCipherSuites() throws Exception { 224 ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 225 .allEnabledCipherSuites() 226 .build(); 227 assertNull(tlsSpec.cipherSuites()); 228 229 SSLSocket sslSocket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); 230 sslSocket.setEnabledCipherSuites(new String[] { 231 // Android-changed: Replace removed CBC cipher with GCM version 232 CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256.javaName, 233 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.javaName, 234 }); 235 236 tlsSpec.apply(sslSocket, false); 237 assertEquals(Arrays.asList( 238 // Android-changed: Replace removed CBC cipher with GCM version 239 CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256.javaName, 240 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.javaName), 241 Arrays.asList(sslSocket.getEnabledCipherSuites())); 242 } 243 allEnabledTlsVersions()244 @Test public void allEnabledTlsVersions() throws Exception { 245 ConnectionSpec tlsSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 246 .allEnabledTlsVersions() 247 .build(); 248 assertNull(tlsSpec.tlsVersions()); 249 250 SSLSocket sslSocket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); 251 sslSocket.setEnabledProtocols(new String[] { 252 // Android-changed: Use TLS 1.3 and 1.2 for testing 253 TlsVersion.TLS_1_2.javaName, 254 TlsVersion.TLS_1_3.javaName, 255 }); 256 257 tlsSpec.apply(sslSocket, false); 258 // Android-changed: Use TLS 1.3 and 1.2 for testing 259 assertEquals(Arrays.asList(TlsVersion.TLS_1_2.javaName(), TlsVersion.TLS_1_3.javaName()), 260 Arrays.asList(sslSocket.getEnabledProtocols())); 261 } 262 tls_missingTlsVersion()263 @Test public void tls_missingTlsVersion() throws Exception { 264 ConnectionSpec tlsSpec = new ConnectionSpec.Builder(true) 265 .cipherSuites(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA) 266 .tlsVersions(TlsVersion.TLS_1_2) 267 .supportsTlsExtensions(false) 268 .build(); 269 270 SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(); 271 // Android-changed: Only testable if TLS v1.1 is available as TLS 1.3 ciphers are 272 // not changeable on Android. 273 Assume.assumeTrue( 274 Arrays.asList(socket.getEnabledProtocols()).contains(TlsVersion.TLS_1_1.javaName)); 275 276 socket.setEnabledCipherSuites(new String[] { 277 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.javaName, 278 }); 279 280 socket.setEnabledProtocols( 281 new String[] { TlsVersion.TLS_1_2.javaName, TlsVersion.TLS_1_1.javaName }); 282 assertTrue(tlsSpec.isCompatible(socket)); 283 284 socket.setEnabledProtocols(new String[] { TlsVersion.TLS_1_1.javaName }); 285 assertFalse(tlsSpec.isCompatible(socket)); 286 } 287 equalsAndHashCode()288 @Test public void equalsAndHashCode() throws Exception { 289 ConnectionSpec allCipherSuites = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 290 .allEnabledCipherSuites() 291 .build(); 292 ConnectionSpec allTlsVersions = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 293 .allEnabledTlsVersions() 294 .build(); 295 296 Set<Object> set = new CopyOnWriteArraySet<>(); 297 assertTrue(set.add(ConnectionSpec.MODERN_TLS)); 298 assertTrue(set.add(ConnectionSpec.COMPATIBLE_TLS)); 299 assertTrue(set.add(ConnectionSpec.CLEARTEXT)); 300 assertTrue(set.add(allTlsVersions)); 301 assertTrue(set.add(allCipherSuites)); 302 303 assertTrue(set.remove(ConnectionSpec.MODERN_TLS)); 304 assertTrue(set.remove(ConnectionSpec.COMPATIBLE_TLS)); 305 assertTrue(set.remove(ConnectionSpec.CLEARTEXT)); 306 assertTrue(set.remove(allTlsVersions)); 307 assertTrue(set.remove(allCipherSuites)); 308 assertTrue(set.isEmpty()); 309 } 310 allEnabledToString()311 @Test public void allEnabledToString() throws Exception { 312 ConnectionSpec connectionSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 313 .allEnabledTlsVersions() 314 .allEnabledCipherSuites() 315 .build(); 316 assertEquals("ConnectionSpec(cipherSuites=[all enabled], tlsVersions=[all enabled], " 317 + "supportsTlsExtensions=true)", connectionSpec.toString()); 318 } 319 simpleToString()320 @Test public void simpleToString() throws Exception { 321 ConnectionSpec connectionSpec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 322 .tlsVersions(TlsVersion.TLS_1_2) 323 .cipherSuites(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA) 324 .build(); 325 assertEquals("ConnectionSpec(cipherSuites=[TLS_RSA_WITH_AES_128_CBC_SHA], tlsVersions=[TLS_1_2], " 326 + "supportsTlsExtensions=true)", connectionSpec.toString()); 327 } 328 set(T... values)329 private static <T> Set<T> set(T... values) { 330 return new LinkedHashSet<>(Arrays.asList(values)); 331 } 332 } 333