1 /* 2 * Copyright (C) 2010 The Android Open Source Project 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 17 package org.conscrypt.javax.net.ssl; 18 19 import static org.conscrypt.TestUtils.UTF_8; 20 import static org.junit.Assert.assertArrayEquals; 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertNotNull; 24 import static org.junit.Assert.assertNotSame; 25 import static org.junit.Assert.assertNull; 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assert.fail; 28 29 import java.io.IOException; 30 import java.nio.ByteBuffer; 31 import java.util.Arrays; 32 import java.util.concurrent.Callable; 33 import java.util.concurrent.CountDownLatch; 34 import java.util.concurrent.ExecutorService; 35 import java.util.concurrent.Executors; 36 import java.util.concurrent.Future; 37 import javax.crypto.SecretKey; 38 import javax.crypto.spec.SecretKeySpec; 39 import javax.net.ssl.KeyManager; 40 import javax.net.ssl.SSLContext; 41 import javax.net.ssl.SSLEngine; 42 import javax.net.ssl.SSLEngineResult; 43 import javax.net.ssl.SSLEngineResult.HandshakeStatus; 44 import javax.net.ssl.SSLEngineResult.Status; 45 import javax.net.ssl.SSLException; 46 import javax.net.ssl.SSLHandshakeException; 47 import javax.net.ssl.SSLParameters; 48 import javax.net.ssl.SSLSession; 49 import javax.net.ssl.X509ExtendedKeyManager; 50 import libcore.java.security.StandardNames; 51 import org.conscrypt.Conscrypt; 52 import org.conscrypt.TestUtils; 53 import org.conscrypt.java.security.TestKeyStore; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.junit.runners.JUnit4; 57 58 @RunWith(JUnit4.class) 59 public class SSLEngineTest { 60 @Test 61 public void test_SSLEngine_defaultConfiguration() throws Exception { 62 SSLConfigurationAsserts.assertSSLEngineDefaultConfiguration( 63 TestSSLContext.create().clientContext.createSSLEngine()); 64 } 65 66 @Test 67 public void test_SSLEngine_getSupportedCipherSuites_returnsCopies() throws Exception { 68 TestSSLContext c = TestSSLContext.create(); 69 SSLEngine e = c.clientContext.createSSLEngine(); 70 assertNotSame(e.getSupportedCipherSuites(), e.getSupportedCipherSuites()); 71 c.close(); 72 } 73 74 @Test 75 public void test_SSLEngine_getSupportedCipherSuites_connect() throws Exception { 76 // note the rare usage of non-RSA keys 77 TestKeyStore testKeyStore = new TestKeyStore.Builder() 78 .keyAlgorithms("RSA", "DSA", "EC", "EC_RSA") 79 .aliasPrefix("rsa-dsa-ec") 80 .ca(true) 81 .build(); 82 test_SSLEngine_getSupportedCipherSuites_connect(testKeyStore, false); 83 test_SSLEngine_getSupportedCipherSuites_connect(testKeyStore, true); 84 } 85 86 // http://b/18554122 87 @Test 88 public void test_SSLEngine_underflowsOnEmptyBuffersDuringHandshake() throws Exception { 89 final SSLEngine sslEngine = SSLContext.getDefault().createSSLEngine(); 90 sslEngine.setUseClientMode(false); 91 ByteBuffer input = ByteBuffer.allocate(1024); 92 input.flip(); 93 ByteBuffer output = ByteBuffer.allocate(1024); 94 sslEngine.beginHandshake(); 95 assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, sslEngine.getHandshakeStatus()); 96 SSLEngineResult result = sslEngine.unwrap(input, output); 97 assertEquals(SSLEngineResult.Status.BUFFER_UNDERFLOW, result.getStatus()); 98 assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus()); 99 } 100 101 // http://b/18554122 102 @Test 103 public void test_SSLEngine_underflowsOnEmptyBuffersAfterHandshake() throws Exception { 104 // Note that create performs the handshake. 105 final TestSSLEnginePair engines = TestSSLEnginePair.create(null /* hooks */); 106 ByteBuffer input = ByteBuffer.allocate(1024); 107 input.flip(); 108 ByteBuffer output = ByteBuffer.allocate(1024); 109 assertEquals(SSLEngineResult.Status.BUFFER_UNDERFLOW, 110 engines.client.unwrap(input, output).getStatus()); 111 } 112 113 private void test_SSLEngine_getSupportedCipherSuites_connect( 114 TestKeyStore testKeyStore, boolean secureRenegotiation) throws Exception { 115 KeyManager pskKeyManager = 116 PSKKeyManagerProxy.getConscryptPSKKeyManager(new PSKKeyManagerProxy() { 117 @Override 118 protected SecretKey getKey( 119 String identityHint, String identity, SSLEngine engine) { 120 return new SecretKeySpec("Just an arbitrary key".getBytes(UTF_8), "RAW"); 121 } 122 }); 123 TestSSLContext c = TestSSLContext.newBuilder() 124 .client(testKeyStore) 125 .server(testKeyStore) 126 .additionalClientKeyManagers(new KeyManager[] {pskKeyManager}) 127 .additionalServerKeyManagers(new KeyManager[] {pskKeyManager}) 128 .build(); 129 130 // Create a TestSSLContext where the KeyManager returns wrong (randomly generated) private 131 // keys, matching the algorithm and parameters of the correct keys. 132 // I couldn't find a more elegant way to achieve this other than temporarily replacing the 133 // first X509ExtendedKeyManager element of TestKeyStore.keyManagers while invoking 134 // TestSSLContext.create. 135 TestSSLContext cWithWrongPrivateKeys; 136 { 137 // Create a RandomPrivateKeyX509ExtendedKeyManager based on the first 138 // X509ExtendedKeyManager in c.serverKeyManagers. 139 KeyManager randomPrivateKeyX509ExtendedKeyManager = null; 140 for (KeyManager keyManager : c.serverKeyManagers) { 141 if (keyManager instanceof X509ExtendedKeyManager) { 142 randomPrivateKeyX509ExtendedKeyManager = 143 new RandomPrivateKeyX509ExtendedKeyManager( 144 (X509ExtendedKeyManager) keyManager); 145 break; 146 } 147 } 148 if (randomPrivateKeyX509ExtendedKeyManager == null) { 149 fail("No X509ExtendedKeyManager in c.serverKeyManagers"); 150 } 151 152 // Find the first X509ExtendedKeyManager in testKeyStore.keyManagers 153 int replaceIndex = -1; 154 for (int i = 0; i < testKeyStore.keyManagers.length; i++) { 155 KeyManager keyManager = testKeyStore.keyManagers[i]; 156 if (keyManager instanceof X509ExtendedKeyManager) { 157 replaceIndex = i; 158 break; 159 } 160 } 161 if (replaceIndex == -1) { 162 fail("No X509ExtendedKeyManager in testKeyStore.keyManagers"); 163 } 164 165 // Temporarily substitute the RandomPrivateKeyX509ExtendedKeyManager in place of the 166 // original X509ExtendedKeyManager. 167 KeyManager originalKeyManager = testKeyStore.keyManagers[replaceIndex]; 168 testKeyStore.keyManagers[replaceIndex] = randomPrivateKeyX509ExtendedKeyManager; 169 cWithWrongPrivateKeys = TestSSLContext.create(testKeyStore, testKeyStore); 170 testKeyStore.keyManagers[replaceIndex] = originalKeyManager; 171 } 172 173 // To catch all the errors. 174 StringBuilder error = new StringBuilder(); 175 176 String[] cipherSuites = c.clientContext.createSSLEngine().getSupportedCipherSuites(); 177 for (String cipherSuite : cipherSuites) { 178 try { 179 // Skip cipher suites that are obsoleted. 180 if (StandardNames.IS_RI && "TLSv1.2".equals(c.clientContext.getProtocol()) 181 && StandardNames.CIPHER_SUITES_OBSOLETE_TLS12.contains(cipherSuite)) { 182 continue; 183 } 184 /* 185 * Signaling Cipher Suite Values (SCSV) cannot be used on their own, but instead in 186 * conjunction with other cipher suites. 187 */ 188 if (cipherSuite.equals(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION) 189 || cipherSuite.equals(StandardNames.CIPHER_SUITE_FALLBACK)) { 190 continue; 191 } 192 /* 193 * Kerberos cipher suites require external setup. See "Kerberos Requirements" in 194 * https://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html 195 * #KRBRequire 196 */ 197 if (cipherSuite.startsWith("TLS_KRB5_")) { 198 continue; 199 } 200 201 final String[] cipherSuiteArray = (secureRenegotiation 202 ? new String[] {cipherSuite, 203 StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION} 204 : new String[] {cipherSuite}); 205 206 // Check that handshake succeeds. 207 TestSSLEnginePair pair = null; 208 try { 209 pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() { 210 @Override 211 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 212 client.setEnabledCipherSuites(cipherSuiteArray); 213 server.setEnabledCipherSuites(cipherSuiteArray); 214 } 215 }); 216 assertConnected(pair); 217 218 boolean needsRecordSplit = "TLS".equalsIgnoreCase(c.clientContext.getProtocol()) 219 && cipherSuite.contains("_CBC_"); 220 221 assertSendsCorrectly("This is the client. Hello!".getBytes(UTF_8), pair.client, 222 pair.server, needsRecordSplit); 223 assertSendsCorrectly("This is the server. Hi!".getBytes(UTF_8), pair.server, 224 pair.client, needsRecordSplit); 225 } finally { 226 if (pair != null) { 227 pair.close(); 228 } 229 } 230 231 // Check that handshake fails when the server does not possess the private key 232 // corresponding to the server's certificate. This is achieved by using SSLContext 233 // cWithWrongPrivateKeys whose KeyManager returns wrong private keys that match 234 // the algorithm (and parameters) of the correct keys. 235 boolean serverAuthenticatedUsingPublicKey = true; 236 if (cipherSuite.contains("_anon_")) { 237 serverAuthenticatedUsingPublicKey = false; 238 } else if ((cipherSuite.startsWith("TLS_PSK_")) 239 || (cipherSuite.startsWith("TLS_ECDHE_PSK_"))) { 240 serverAuthenticatedUsingPublicKey = false; 241 } 242 if (serverAuthenticatedUsingPublicKey) { 243 TestSSLEnginePair p = null; 244 try { 245 p = TestSSLEnginePair.create( 246 cWithWrongPrivateKeys, new TestSSLEnginePair.Hooks() { 247 @Override 248 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 249 client.setEnabledCipherSuites(cipherSuiteArray); 250 server.setEnabledCipherSuites(cipherSuiteArray); 251 } 252 }); 253 assertNotConnected(p); 254 } catch (IOException expected) { 255 // Ignored. 256 } finally { 257 if (p != null) { 258 p.close(); 259 } 260 } 261 } 262 } catch (Exception e) { 263 String message = ("Problem trying to connect cipher suite " + cipherSuite); 264 System.out.println(message); 265 e.printStackTrace(); 266 error.append(message); 267 error.append('\n'); 268 } 269 } 270 c.close(); 271 272 if (error.length() > 0) { 273 throw new Exception("One or more problems in " 274 + "test_SSLEngine_getSupportedCipherSuites_connect:\n" + error); 275 } 276 } 277 278 private static void assertSendsCorrectly(final byte[] sourceBytes, SSLEngine source, 279 SSLEngine dest, boolean needsRecordSplit) throws SSLException { 280 ByteBuffer sourceOut = ByteBuffer.wrap(sourceBytes); 281 SSLSession sourceSession = source.getSession(); 282 ByteBuffer sourceToDest = ByteBuffer.allocate(sourceSession.getPacketBufferSize()); 283 SSLEngineResult sourceOutRes = source.wrap(sourceOut, sourceToDest); 284 sourceToDest.flip(); 285 286 String sourceCipherSuite = source.getSession().getCipherSuite(); 287 assertEquals(sourceCipherSuite, sourceBytes.length, sourceOutRes.bytesConsumed()); 288 assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING, 289 sourceOutRes.getHandshakeStatus()); 290 291 SSLSession destSession = dest.getSession(); 292 ByteBuffer destIn = ByteBuffer.allocate(destSession.getApplicationBufferSize()); 293 294 int numUnwrapCalls = 0; 295 while (destIn.position() != sourceOut.limit()) { 296 SSLEngineResult destRes = dest.unwrap(sourceToDest, destIn); 297 assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING, 298 destRes.getHandshakeStatus()); 299 if (needsRecordSplit && numUnwrapCalls == 0) { 300 assertEquals(sourceCipherSuite, 1, destRes.bytesProduced()); 301 } 302 numUnwrapCalls++; 303 } 304 305 destIn.flip(); 306 byte[] actual = new byte[destIn.remaining()]; 307 destIn.get(actual); 308 assertEquals(sourceCipherSuite, Arrays.toString(sourceBytes), Arrays.toString(actual)); 309 310 if (needsRecordSplit) { 311 assertEquals(sourceCipherSuite, 2, numUnwrapCalls); 312 } else { 313 assertEquals(sourceCipherSuite, 1, numUnwrapCalls); 314 assertSendsCorrectlyWhenSplit(sourceBytes, source, dest); 315 } 316 } 317 318 private static void assertSendsCorrectlyWhenSplit(final byte[] sourceBytes, SSLEngine source, 319 SSLEngine dest) throws SSLException { 320 // Split the input into three to test the version that accepts ByteBuffer[]. Three 321 // is chosen somewhat arbitrarily as a number larger than the minimum of 2 but small 322 // enough that it's not unwieldy. 323 ByteBuffer[] sourceBufs = new ByteBuffer[3]; 324 int sourceLen = sourceBytes.length; 325 sourceBufs[0] = ByteBuffer.wrap(sourceBytes, 0, sourceLen / 3); 326 sourceBufs[1] = ByteBuffer.wrap(sourceBytes, sourceLen / 3, sourceLen / 3); 327 sourceBufs[2] = ByteBuffer.wrap( 328 sourceBytes, 2 * (sourceLen / 3), sourceLen - 2 * (sourceLen / 3)); 329 SSLSession sourceSession = source.getSession(); 330 ByteBuffer sourceToDest = ByteBuffer.allocate(sourceSession.getPacketBufferSize()); 331 SSLEngineResult sourceOutRes = source.wrap(sourceBufs, sourceToDest); 332 sourceToDest.flip(); 333 String sourceCipherSuite = source.getSession().getCipherSuite(); 334 assertEquals(sourceCipherSuite, sourceBytes.length, sourceOutRes.bytesConsumed()); 335 assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING, 336 sourceOutRes.getHandshakeStatus()); 337 SSLSession destSession = dest.getSession(); 338 ByteBuffer destIn = ByteBuffer.allocate(destSession.getApplicationBufferSize()); 339 int numUnwrapCalls = 0; 340 while (destIn.position() != sourceBytes.length) { 341 SSLEngineResult destRes = dest.unwrap(sourceToDest, destIn); 342 assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING, 343 destRes.getHandshakeStatus()); 344 numUnwrapCalls++; 345 } 346 destIn.flip(); 347 byte[] actual = new byte[destIn.remaining()]; 348 destIn.get(actual); 349 assertEquals(sourceCipherSuite, Arrays.toString(sourceBytes), Arrays.toString(actual)); 350 assertEquals(sourceCipherSuite, 3, numUnwrapCalls); 351 } 352 353 @Test 354 public void test_SSLEngine_getEnabledCipherSuites_returnsCopies() throws Exception { 355 TestSSLContext c = TestSSLContext.create(); 356 SSLEngine e = c.clientContext.createSSLEngine(); 357 assertNotSame(e.getEnabledCipherSuites(), e.getEnabledCipherSuites()); 358 c.close(); 359 } 360 361 @Test 362 public void test_SSLEngine_setEnabledCipherSuites_storesCopy() throws Exception { 363 TestSSLContext c = TestSSLContext.create(); 364 SSLEngine e = c.clientContext.createSSLEngine(); 365 String[] array = new String[] {e.getEnabledCipherSuites()[0]}; 366 String originalFirstElement = array[0]; 367 e.setEnabledCipherSuites(array); 368 array[0] = "Modified after having been set"; 369 assertEquals(originalFirstElement, e.getEnabledCipherSuites()[0]); 370 } 371 372 @Test 373 public void test_SSLEngine_setEnabledCipherSuites() throws Exception { 374 TestSSLContext c = TestSSLContext.create(); 375 SSLEngine e = c.clientContext.createSSLEngine(); 376 377 try { 378 e.setEnabledCipherSuites(null); 379 fail(); 380 } catch (IllegalArgumentException expected) { 381 // Ignored. 382 } 383 try { 384 e.setEnabledCipherSuites(new String[1]); 385 fail(); 386 } catch (IllegalArgumentException expected) { 387 // Ignored. 388 } 389 try { 390 e.setEnabledCipherSuites(new String[] {"Bogus"}); 391 fail(); 392 } catch (IllegalArgumentException expected) { 393 // Ignored. 394 } 395 396 e.setEnabledCipherSuites(new String[0]); 397 e.setEnabledCipherSuites(e.getEnabledCipherSuites()); 398 e.setEnabledCipherSuites(e.getSupportedCipherSuites()); 399 400 // Check that setEnabledCipherSuites affects getEnabledCipherSuites 401 String[] cipherSuites = new String[] {e.getSupportedCipherSuites()[0]}; 402 e.setEnabledCipherSuites(cipherSuites); 403 assertEquals(Arrays.asList(cipherSuites), Arrays.asList(e.getEnabledCipherSuites())); 404 405 c.close(); 406 } 407 408 @Test 409 public void test_SSLEngine_getSupportedProtocols_returnsCopies() throws Exception { 410 TestSSLContext c = TestSSLContext.create(); 411 SSLEngine e = c.clientContext.createSSLEngine(); 412 assertNotSame(e.getSupportedProtocols(), e.getSupportedProtocols()); 413 c.close(); 414 } 415 416 @Test 417 public void test_SSLEngine_getEnabledProtocols_returnsCopies() throws Exception { 418 TestSSLContext c = TestSSLContext.create(); 419 SSLEngine e = c.clientContext.createSSLEngine(); 420 assertNotSame(e.getEnabledProtocols(), e.getEnabledProtocols()); 421 c.close(); 422 } 423 424 @Test 425 public void test_SSLEngine_setEnabledProtocols_storesCopy() throws Exception { 426 TestSSLContext c = TestSSLContext.create(); 427 SSLEngine e = c.clientContext.createSSLEngine(); 428 String[] array = new String[] {e.getEnabledProtocols()[0]}; 429 String originalFirstElement = array[0]; 430 e.setEnabledProtocols(array); 431 array[0] = "Modified after having been set"; 432 assertEquals(originalFirstElement, e.getEnabledProtocols()[0]); 433 } 434 435 @Test 436 public void test_SSLEngine_setEnabledProtocols() throws Exception { 437 TestSSLContext c = TestSSLContext.create(); 438 SSLEngine e = c.clientContext.createSSLEngine(); 439 440 try { 441 e.setEnabledProtocols(null); 442 fail(); 443 } catch (IllegalArgumentException expected) { 444 // Ignored. 445 } 446 try { 447 e.setEnabledProtocols(new String[1]); 448 fail(); 449 } catch (IllegalArgumentException expected) { 450 // Ignored. 451 } 452 try { 453 e.setEnabledProtocols(new String[] {"Bogus"}); 454 fail(); 455 } catch (IllegalArgumentException expected) { 456 // Ignored. 457 } 458 e.setEnabledProtocols(new String[0]); 459 e.setEnabledProtocols(e.getEnabledProtocols()); 460 e.setEnabledProtocols(e.getSupportedProtocols()); 461 462 // Check that setEnabledProtocols affects getEnabledProtocols 463 for (String protocol : e.getSupportedProtocols()) { 464 if ("SSLv2Hello".equals(protocol)) { 465 try { 466 e.setEnabledProtocols(new String[] {protocol}); 467 fail("Should fail when SSLv2Hello is set by itself"); 468 } catch (IllegalArgumentException expected) { 469 // Ignored. 470 } 471 } else { 472 String[] protocols = new String[] {protocol}; 473 e.setEnabledProtocols(protocols); 474 assertEquals(Arrays.deepToString(protocols), 475 Arrays.deepToString(e.getEnabledProtocols())); 476 } 477 } 478 479 c.close(); 480 } 481 482 @Test 483 public void test_SSLEngine_getSession() throws Exception { 484 TestSSLContext c = TestSSLContext.create(); 485 SSLEngine e = c.clientContext.createSSLEngine(); 486 SSLSession session = e.getSession(); 487 assertNotNull(session); 488 assertFalse(session.isValid()); 489 c.close(); 490 } 491 492 @Test 493 public void test_SSLEngine_beginHandshake() throws Exception { 494 TestSSLContext c = TestSSLContext.create(); 495 496 try { 497 c.clientContext.createSSLEngine().beginHandshake(); 498 fail(); 499 } catch (IllegalStateException expected) { 500 // Ignored. 501 } 502 c.close(); 503 504 TestSSLEnginePair p = TestSSLEnginePair.create(null); 505 assertConnected(p); 506 p.close(); 507 } 508 509 @Test 510 public void test_SSLEngine_beginHandshake_noKeyStore() throws Exception { 511 TestSSLContext c = TestSSLContext.newBuilder() 512 .useDefaults(false) 513 .clientContext(SSLContext.getDefault()) 514 .serverContext(SSLContext.getDefault()) 515 .build(); 516 SSLEngine[] p = null; 517 try { 518 // TODO Fix KnownFailure AlertException "NO SERVER CERTIFICATE FOUND" 519 // ServerHandshakeImpl.selectSuite should not select a suite without a required cert 520 p = TestSSLEnginePair.connect(c, null); 521 fail(); 522 } catch (SSLHandshakeException expected) { 523 // Ignored. 524 } finally { 525 if (p != null) { 526 TestSSLEnginePair.close(p); 527 } 528 } 529 c.close(); 530 } 531 532 @Test 533 public void test_SSLEngine_beginHandshake_noClientCertificate() throws Exception { 534 TestSSLContext c = TestSSLContext.create(); 535 SSLEngine[] engines = TestSSLEnginePair.connect(c, null); 536 assertConnected(engines[0], engines[1]); 537 c.close(); 538 TestSSLEnginePair.close(engines); 539 } 540 541 // http://b/37078438 542 @Test 543 public void test_SSLEngine_beginHandshake_redundantCalls() throws Exception { 544 TestSSLContext c = TestSSLContext.create(); 545 SSLEngine client = c.clientContext.createSSLEngine(c.host.getHostName(), c.port); 546 client.setUseClientMode(true); 547 client.beginHandshake(); 548 client.beginHandshake(); // This call should be ignored 549 c.close(); 550 } 551 552 @Test 553 public void test_SSLEngine_getUseClientMode() throws Exception { 554 TestSSLContext c = TestSSLContext.create(); 555 assertFalse(c.clientContext.createSSLEngine().getUseClientMode()); 556 assertFalse(c.clientContext.createSSLEngine(null, -1).getUseClientMode()); 557 c.close(); 558 } 559 560 @Test 561 public void test_SSLEngine_setUseClientMode() throws Exception { 562 boolean[] finished; 563 TestSSLEnginePair p; 564 565 // client is client, server is server 566 finished = new boolean[2]; 567 p = test_SSLEngine_setUseClientMode(true, false, finished); 568 assertConnected(p); 569 assertTrue(finished[0]); 570 assertTrue(finished[1]); 571 p.close(); 572 573 // client is server, server is client 574 finished = new boolean[2]; 575 p = test_SSLEngine_setUseClientMode(false, true, finished); 576 assertConnected(p); 577 assertTrue(finished[0]); 578 assertTrue(finished[1]); 579 p.close(); 580 581 // both are client 582 /* 583 * Our implementation throws an SSLHandshakeException, but RI just 584 * stalls forever 585 */ 586 p = null; 587 try { 588 p = test_SSLEngine_setUseClientMode(true, true, null); 589 assertNotConnected(p); 590 } catch (SSLHandshakeException maybeExpected) { 591 // Ignored. 592 } finally { 593 if (p != null) { 594 p.close(); 595 } 596 } 597 598 p = test_SSLEngine_setUseClientMode(false, false, null); 599 // both are server 600 assertNotConnected(p); 601 p.close(); 602 } 603 604 @Test 605 public void test_SSLEngine_setUseClientMode_afterHandshake() throws Exception { 606 // can't set after handshake 607 TestSSLEnginePair pair = TestSSLEnginePair.create(null); 608 try { 609 pair.server.setUseClientMode(false); 610 fail(); 611 } catch (IllegalArgumentException expected) { 612 // Ignored. 613 } 614 try { 615 pair.client.setUseClientMode(false); 616 fail(); 617 } catch (IllegalArgumentException expected) { 618 // Ignored. 619 } 620 pair.close(); 621 } 622 623 private TestSSLEnginePair test_SSLEngine_setUseClientMode(final boolean clientClientMode, 624 final boolean serverClientMode, final boolean[] finished) throws Exception { 625 TestSSLContext c; 626 if (!clientClientMode && serverClientMode) { 627 c = TestSSLContext.create(TestKeyStore.getServer(), TestKeyStore.getClient()); 628 } else { 629 c = TestSSLContext.create(); 630 } 631 632 return TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() { 633 @Override 634 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 635 client.setUseClientMode(clientClientMode); 636 server.setUseClientMode(serverClientMode); 637 } 638 }, finished); 639 } 640 641 @Test 642 public void test_SSLEngine_clientAuth() throws Exception { 643 TestSSLContext c = TestSSLContext.create(); 644 SSLEngine e = c.clientContext.createSSLEngine(); 645 646 assertFalse(e.getWantClientAuth()); 647 assertFalse(e.getNeedClientAuth()); 648 649 // confirm turning one on by itself 650 e.setWantClientAuth(true); 651 assertTrue(e.getWantClientAuth()); 652 assertFalse(e.getNeedClientAuth()); 653 654 // confirm turning setting on toggles the other 655 e.setNeedClientAuth(true); 656 assertFalse(e.getWantClientAuth()); 657 assertTrue(e.getNeedClientAuth()); 658 659 // confirm toggling back 660 e.setWantClientAuth(true); 661 assertTrue(e.getWantClientAuth()); 662 assertFalse(e.getNeedClientAuth()); 663 664 // TODO Fix KnownFailure "init - invalid private key" 665 TestSSLContext clientAuthContext = TestSSLContext.create( 666 TestKeyStore.getClientCertificate(), TestKeyStore.getServer()); 667 TestSSLEnginePair p = 668 TestSSLEnginePair.create(clientAuthContext, new TestSSLEnginePair.Hooks() { 669 @Override 670 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 671 server.setWantClientAuth(true); 672 } 673 }); 674 assertConnected(p); 675 assertNotNull(p.client.getSession().getLocalCertificates()); 676 TestKeyStore.assertChainLength(p.client.getSession().getLocalCertificates()); 677 TestSSLContext.assertClientCertificateChain( 678 clientAuthContext.clientTrustManager, p.client.getSession().getLocalCertificates()); 679 clientAuthContext.close(); 680 c.close(); 681 p.close(); 682 } 683 684 /** 685 * http://code.google.com/p/android/issues/detail?id=31903 686 * This test case directly tests the fix for the issue. 687 */ 688 @Test 689 public void test_SSLEngine_clientAuthWantedNoClientCert() throws Exception { 690 TestSSLContext clientAuthContext = 691 TestSSLContext.create(TestKeyStore.getClient(), TestKeyStore.getServer()); 692 TestSSLEnginePair p = 693 TestSSLEnginePair.create(clientAuthContext, new TestSSLEnginePair.Hooks() { 694 @Override 695 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 696 server.setWantClientAuth(true); 697 } 698 }); 699 assertConnected(p); 700 clientAuthContext.close(); 701 p.close(); 702 } 703 704 /** 705 * http://code.google.com/p/android/issues/detail?id=31903 706 * This test case verifies that if the server requires a client cert 707 * (setNeedClientAuth) but the client does not provide one SSL connection 708 * establishment will fail 709 */ 710 @Test 711 public void test_SSLEngine_clientAuthNeededNoClientCert() throws Exception { 712 TestSSLContext clientAuthContext = 713 TestSSLContext.create(TestKeyStore.getClient(), TestKeyStore.getServer()); 714 TestSSLEnginePair p = null; 715 try { 716 p = TestSSLEnginePair.create(clientAuthContext, new TestSSLEnginePair.Hooks() { 717 @Override 718 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 719 server.setNeedClientAuth(true); 720 } 721 }); 722 fail(); 723 } catch (SSLHandshakeException expected) { 724 // Ignored. 725 } finally { 726 clientAuthContext.close(); 727 if (p != null) { 728 p.close(); 729 } 730 } 731 } 732 733 @Test 734 public void test_SSLEngine_endpointVerification_Success() throws Exception { 735 TestUtils.assumeSetEndpointIdentificationAlgorithmAvailable(); 736 TestSSLContext c = TestSSLContext.create(); 737 TestSSLEnginePair p = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() { 738 @Override 739 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 740 SSLParameters p = client.getSSLParameters(); 741 p.setEndpointIdentificationAlgorithm("HTTPS"); 742 client.setSSLParameters(p); 743 } 744 }); 745 assertConnected(p); 746 c.close(); 747 } 748 749 @Test 750 public void test_SSLEngine_getEnableSessionCreation() throws Exception { 751 TestSSLContext c = TestSSLContext.create(); 752 SSLEngine e = c.clientContext.createSSLEngine(); 753 assertTrue(e.getEnableSessionCreation()); 754 c.close(); 755 TestSSLEnginePair.close(new SSLEngine[] {e}); 756 } 757 758 @Test 759 public void test_SSLEngine_setEnableSessionCreation_server() throws Exception { 760 TestSSLEnginePair p = null; 761 try { 762 p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() { 763 @Override 764 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 765 server.setEnableSessionCreation(false); 766 } 767 }); 768 assertNotConnected(p); 769 } catch (SSLException maybeExpected) { 770 // Ignored. 771 } finally { 772 if (p != null) { 773 p.close(); 774 } 775 } 776 } 777 778 @Test 779 public void test_SSLEngine_setEnableSessionCreation_client() throws Exception { 780 TestSSLEnginePair p = null; 781 try { 782 p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() { 783 @Override 784 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 785 client.setEnableSessionCreation(false); 786 } 787 }); 788 fail(); 789 } catch (SSLException expected) { 790 // Ignored. 791 } finally { 792 if (p != null) { 793 p.close(); 794 } 795 } 796 } 797 798 @Test 799 public void test_SSLEngine_getSSLParameters() throws Exception { 800 TestSSLContext c = TestSSLContext.create(); 801 SSLEngine e = c.clientContext.createSSLEngine(); 802 803 SSLParameters p = e.getSSLParameters(); 804 assertNotNull(p); 805 806 String[] cipherSuites = p.getCipherSuites(); 807 assertNotSame(cipherSuites, e.getEnabledCipherSuites()); 808 assertEquals(Arrays.asList(cipherSuites), Arrays.asList(e.getEnabledCipherSuites())); 809 810 String[] protocols = p.getProtocols(); 811 assertNotSame(protocols, e.getEnabledProtocols()); 812 assertEquals(Arrays.asList(protocols), Arrays.asList(e.getEnabledProtocols())); 813 814 assertEquals(p.getWantClientAuth(), e.getWantClientAuth()); 815 assertEquals(p.getNeedClientAuth(), e.getNeedClientAuth()); 816 817 c.close(); 818 } 819 820 @Test 821 public void test_SSLEngine_setSSLParameters() throws Exception { 822 TestSSLContext c = TestSSLContext.create(); 823 SSLEngine e = c.clientContext.createSSLEngine(); 824 String[] defaultCipherSuites = e.getEnabledCipherSuites(); 825 String[] defaultProtocols = e.getEnabledProtocols(); 826 String[] supportedCipherSuites = e.getSupportedCipherSuites(); 827 String[] supportedProtocols = e.getSupportedProtocols(); 828 829 { 830 SSLParameters p = new SSLParameters(); 831 e.setSSLParameters(p); 832 assertEquals( 833 Arrays.asList(defaultCipherSuites), Arrays.asList(e.getEnabledCipherSuites())); 834 assertEquals(Arrays.asList(defaultProtocols), Arrays.asList(e.getEnabledProtocols())); 835 } 836 837 { 838 SSLParameters p = new SSLParameters(supportedCipherSuites, supportedProtocols); 839 e.setSSLParameters(p); 840 assertEquals(Arrays.asList(supportedCipherSuites), 841 Arrays.asList(e.getEnabledCipherSuites())); 842 assertEquals(Arrays.asList(supportedProtocols), Arrays.asList(e.getEnabledProtocols())); 843 } 844 { 845 SSLParameters p = new SSLParameters(); 846 847 p.setNeedClientAuth(true); 848 assertFalse(e.getNeedClientAuth()); 849 assertFalse(e.getWantClientAuth()); 850 e.setSSLParameters(p); 851 assertTrue(e.getNeedClientAuth()); 852 assertFalse(e.getWantClientAuth()); 853 854 p.setWantClientAuth(true); 855 assertTrue(e.getNeedClientAuth()); 856 assertFalse(e.getWantClientAuth()); 857 e.setSSLParameters(p); 858 assertFalse(e.getNeedClientAuth()); 859 assertTrue(e.getWantClientAuth()); 860 861 p.setWantClientAuth(false); 862 assertFalse(e.getNeedClientAuth()); 863 assertTrue(e.getWantClientAuth()); 864 e.setSSLParameters(p); 865 assertFalse(e.getNeedClientAuth()); 866 assertFalse(e.getWantClientAuth()); 867 } 868 c.close(); 869 } 870 871 @Test 872 public void test_TestSSLEnginePair_create() throws Exception { 873 TestSSLEnginePair test = TestSSLEnginePair.create(null); 874 assertNotNull(test.c); 875 assertNotNull(test.server); 876 assertNotNull(test.client); 877 assertConnected(test); 878 test.close(); 879 } 880 881 private final int NUM_STRESS_ITERATIONS = 1000; 882 883 @Test 884 public void test_SSLEngine_Multiple_Thread_Success() throws Exception { 885 final TestSSLEnginePair pair = TestSSLEnginePair.create(); 886 try { 887 assertConnected(pair); 888 889 final CountDownLatch startUpSync = new CountDownLatch(2); 890 ExecutorService executor = Executors.newFixedThreadPool(2); 891 Future<Void> client = executor.submit(new Callable<Void>() { 892 @Override 893 public Void call() throws Exception { 894 startUpSync.countDown(); 895 896 for (int i = 0; i < NUM_STRESS_ITERATIONS; i++) { 897 assertSendsCorrectly("This is the client. Hello!".getBytes(UTF_8), 898 pair.client, pair.server, false); 899 } 900 901 return null; 902 } 903 }); 904 Future<Void> server = executor.submit(new Callable<Void>() { 905 @Override 906 public Void call() throws Exception { 907 startUpSync.countDown(); 908 909 for (int i = 0; i < NUM_STRESS_ITERATIONS; i++) { 910 assertSendsCorrectly("This is the server. Hi!".getBytes(UTF_8), pair.server, 911 pair.client, false); 912 } 913 914 return null; 915 } 916 }); 917 executor.shutdown(); 918 client.get(); 919 server.get(); 920 } finally { 921 pair.close(); 922 } 923 } 924 925 @Test 926 public void test_SSLEngine_CloseOutbound() throws Exception { 927 final TestSSLEnginePair pair = TestSSLEnginePair.create(); 928 try { 929 assertConnected(pair); 930 931 // Closing the outbound direction should cause a close_notify to be sent 932 pair.client.closeOutbound(); 933 ByteBuffer clientOut = ByteBuffer 934 .allocate(pair.client.getSession().getPacketBufferSize()); 935 SSLEngineResult res = pair.client.wrap(ByteBuffer.wrap(new byte[0]), clientOut); 936 assertEquals(Status.CLOSED, res.getStatus()); 937 assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus()); 938 assertTrue(res.bytesProduced() > 0); 939 940 // Read the close_notify in the server 941 clientOut.flip(); 942 ByteBuffer serverIn = ByteBuffer 943 .allocate(pair.server.getSession().getApplicationBufferSize()); 944 res = pair.server.unwrap(clientOut, serverIn); 945 assertEquals(Status.CLOSED, res.getStatus()); 946 assertEquals(HandshakeStatus.NEED_WRAP, res.getHandshakeStatus()); 947 948 // Reading the close_notify should cause a close_notify to be sent back 949 ByteBuffer serverOut = ByteBuffer 950 .allocate(pair.server.getSession().getPacketBufferSize()); 951 res = pair.server.wrap(ByteBuffer.wrap(new byte[0]), serverOut); 952 assertEquals(Status.CLOSED, res.getStatus()); 953 assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus()); 954 assertTrue(res.bytesProduced() > 0); 955 956 // Read the close_notify in the client 957 serverOut.flip(); 958 ByteBuffer clientIn = ByteBuffer 959 .allocate(pair.client.getSession().getApplicationBufferSize()); 960 res = pair.client.unwrap(serverOut, clientIn); 961 assertEquals(Status.CLOSED, res.getStatus()); 962 assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus()); 963 964 // Both sides have received close_notify messages, so both peers should have 965 // registered that they're finished 966 assertTrue(pair.client.isInboundDone() && pair.client.isOutboundDone()); 967 assertTrue(pair.server.isInboundDone() && pair.server.isOutboundDone()); 968 } finally { 969 pair.close(); 970 } 971 } 972 973 @Test 974 public void test_SSLEngine_TlsUnique() throws Exception { 975 TestSSLEnginePair pair = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() { 976 @Override 977 void beforeBeginHandshake(SSLEngine client, SSLEngine server) { 978 assertNull(Conscrypt.getTlsUnique(client)); 979 assertNull(Conscrypt.getTlsUnique(server)); 980 } 981 }); 982 try { 983 assertConnected(pair); 984 985 byte[] clientTlsUnique = Conscrypt.getTlsUnique(pair.client); 986 byte[] serverTlsUnique = Conscrypt.getTlsUnique(pair.server); 987 assertNotNull(clientTlsUnique); 988 assertNotNull(serverTlsUnique); 989 assertArrayEquals(clientTlsUnique, serverTlsUnique); 990 } finally { 991 pair.close(); 992 } 993 } 994 995 private void assertConnected(TestSSLEnginePair e) { 996 assertConnected(e.client, e.server); 997 } 998 999 private void assertNotConnected(TestSSLEnginePair e) { 1000 assertNotConnected(e.client, e.server); 1001 } 1002 1003 private void assertConnected(SSLEngine a, SSLEngine b) { 1004 assertTrue(connected(a, b)); 1005 } 1006 1007 private void assertNotConnected(SSLEngine a, SSLEngine b) { 1008 assertFalse(connected(a, b)); 1009 } 1010 1011 private boolean connected(SSLEngine a, SSLEngine b) { 1012 return (a.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING 1013 && b.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING 1014 && a.getSession() != null && b.getSession() != null && !a.isInboundDone() 1015 && !b.isInboundDone() && !a.isOutboundDone() && !b.isOutboundDone()); 1016 } 1017 } 1018