|
29 | 29 | import static org.conscrypt.NativeConstants.TLS1_VERSION;
|
30 | 30 | import static org.conscrypt.TestUtils.openTestFile;
|
31 | 31 | import static org.conscrypt.TestUtils.readTestFile;
|
| 32 | +import static org.junit.Assert.assertArrayEquals; |
32 | 33 | import static org.junit.Assert.assertEquals;
|
33 | 34 | import static org.junit.Assert.assertFalse;
|
34 | 35 | import static org.junit.Assert.assertNotNull;
|
@@ -528,6 +529,9 @@ public long beforeHandshake(long c) throws SSLException {
|
528 | 529 | public void afterHandshake(long session, long ssl, long context, Socket socket,
|
529 | 530 | FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
|
530 | 531 | assertFalse(NativeCrypto.SSL_ech_accepted(ssl, null));
|
| 532 | + assertNull(NativeCrypto.SSL_get0_ech_name_override(ssl, null)); |
| 533 | + byte[] retryConfigs = NativeCrypto.SSL_get0_ech_retry_configs(ssl, null); |
| 534 | + assertEquals(5, retryConfigs.length); // should be the invalid ECH Config List |
531 | 535 | super.afterHandshake(session, ssl, context, socket, fd, callback);
|
532 | 536 | }
|
533 | 537 | };
|
@@ -626,6 +630,103 @@ public void afterHandshake(long session, long ssl, long context, Socket socket,
|
626 | 630 | assertTrue(serverCallback.serverCertificateRequestedInvoked);
|
627 | 631 | }
|
628 | 632 |
|
| 633 | + @Test |
| 634 | + public void test_SSL_do_handshake_ech_retry_configs() throws Exception { |
| 635 | + final ServerSocket listener = newServerSocket(); |
| 636 | + |
| 637 | + final byte[] key = readTestFile("boringssl-ech-private-key.bin"); |
| 638 | + final byte[] serverConfig = readTestFile("boringssl-server-ech-config.bin"); |
| 639 | + final byte[] originalClientConfigList = readTestFile("boringssl-ech-config-list.bin"); |
| 640 | + final byte[] clientConfigList = originalClientConfigList.clone(); |
| 641 | + clientConfigList[20] = (byte) (clientConfigList[20] % 255 + 1); // corrupt it |
| 642 | + |
| 643 | + Hooks cHooks = new ClientHooks() { |
| 644 | + @Override |
| 645 | + public long beforeHandshake(long c) throws SSLException { |
| 646 | + long ssl = super.beforeHandshake(c); |
| 647 | + assertEquals(1, NativeCrypto.SSL_set_protocol_versions(ssl, null, TLS1_VERSION, TLS1_3_VERSION)); |
| 648 | + assertTrue(NativeCrypto.SSL_set1_ech_config_list(ssl, null, clientConfigList)); |
| 649 | + return ssl; |
| 650 | + } |
| 651 | + |
| 652 | + @Override |
| 653 | + public void afterHandshake(long session, long ssl, long context, Socket socket, |
| 654 | + FileDescriptor fd, SSLHandshakeCallbacks callback) { |
| 655 | + fail(); |
| 656 | + } |
| 657 | + }; |
| 658 | + Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) { |
| 659 | + @Override |
| 660 | + public long beforeHandshake(long c) throws SSLException { |
| 661 | + long ssl = super.beforeHandshake(c); |
| 662 | + assertEquals(1, NativeCrypto.SSL_set_protocol_versions(ssl, null, TLS1_VERSION, TLS1_3_VERSION)); |
| 663 | + assertTrue(NativeCrypto.SSL_CTX_ech_enable_server(c, null, key, serverConfig)); |
| 664 | + return ssl; |
| 665 | + } |
| 666 | + |
| 667 | + @Override |
| 668 | + public void afterHandshake(long session, long ssl, long context, Socket socket, |
| 669 | + FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { |
| 670 | + assertTrue(NativeCrypto.SSL_ech_accepted(ssl, null)); |
| 671 | + super.afterHandshake(session, ssl, context, socket, fd, callback); |
| 672 | + } |
| 673 | + }; |
| 674 | + Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null, true); |
| 675 | + Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null, null, true); |
| 676 | + TestSSLHandshakeCallbacks clientCallback = null; |
| 677 | + TestSSLHandshakeCallbacks serverCallback = null; |
| 678 | + try { |
| 679 | + clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); |
| 680 | + serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); |
| 681 | + } catch (ExecutionException e) { |
| 682 | + // caused by SSLProtocolException |
| 683 | + } |
| 684 | + assertNull(clientCallback); |
| 685 | + assertNull(serverCallback); |
| 686 | + assertArrayEquals(originalClientConfigList, cHooks.echRetryConfigs); |
| 687 | + assertEquals("example.com", cHooks.echNameOverride); |
| 688 | + assertNotNull(cHooks.echRetryConfigs); |
| 689 | + assertNull(sHooks.echNameOverride); |
| 690 | + assertNull(sHooks.echRetryConfigs); |
| 691 | + |
| 692 | + final byte[] echRetryConfigsFromPrevious = cHooks.echRetryConfigs; |
| 693 | + cHooks = new ClientHooks() { |
| 694 | + @Override |
| 695 | + public long beforeHandshake(long c) throws SSLException { |
| 696 | + long ssl = super.beforeHandshake(c); |
| 697 | + assertEquals(1, NativeCrypto.SSL_set_protocol_versions(ssl, null, TLS1_VERSION, TLS1_3_VERSION)); |
| 698 | + assertTrue(NativeCrypto.SSL_set1_ech_config_list(ssl, null, echRetryConfigsFromPrevious)); |
| 699 | + return ssl; |
| 700 | + } |
| 701 | + |
| 702 | + @Override |
| 703 | + public void afterHandshake(long session, long ssl, long context, Socket socket, |
| 704 | + FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception { |
| 705 | + assertTrue(NativeCrypto.SSL_ech_accepted(ssl, null)); |
| 706 | + super.afterHandshake(session, ssl, context, socket, fd, callback); |
| 707 | + } |
| 708 | + }; |
| 709 | + |
| 710 | + client = handshake(listener, 0, true, cHooks, null, null); |
| 711 | + server = handshake(listener, 0, false, sHooks, null, null); |
| 712 | + clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); |
| 713 | + serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); |
| 714 | + assertTrue(clientCallback.verifyCertificateChainCalled); |
| 715 | + assertEqualCertificateChains( |
| 716 | + getServerCertificateRefs(), clientCallback.certificateChainRefs); |
| 717 | + assertFalse(serverCallback.verifyCertificateChainCalled); |
| 718 | + assertFalse(clientCallback.clientCertificateRequestedCalled); |
| 719 | + assertFalse(serverCallback.clientCertificateRequestedCalled); |
| 720 | + assertFalse(clientCallback.clientPSKKeyRequestedInvoked); |
| 721 | + assertFalse(serverCallback.clientPSKKeyRequestedInvoked); |
| 722 | + assertFalse(clientCallback.serverPSKKeyRequestedInvoked); |
| 723 | + assertFalse(serverCallback.serverPSKKeyRequestedInvoked); |
| 724 | + assertTrue(clientCallback.handshakeCompletedCalled); |
| 725 | + assertTrue(serverCallback.handshakeCompletedCalled); |
| 726 | + assertFalse(clientCallback.serverCertificateRequestedInvoked); |
| 727 | + assertTrue(serverCallback.serverCertificateRequestedInvoked); |
| 728 | + } |
| 729 | + |
629 | 730 | @Test
|
630 | 731 | public void test_SSL_set_enable_ech_grease() throws Exception {
|
631 | 732 | long c = NativeCrypto.SSL_CTX_new();
|
@@ -688,6 +789,11 @@ public void test_SSL_CTX_ech_enable_server() throws Exception {
|
688 | 789 | NativeCrypto.SSL_CTX_free(c, null);
|
689 | 790 | }
|
690 | 791 |
|
| 792 | + @Test(expected = NullPointerException.class) |
| 793 | + public void test_SSL_get0_ech_retry_configs_withNullShouldThrow() throws Exception { |
| 794 | + NativeCrypto.SSL_get0_ech_retry_configs(NULL, null); |
| 795 | + } |
| 796 | + |
691 | 797 | @Test(expected = NullPointerException.class)
|
692 | 798 | public void test_SSL_CTX_ech_enable_server_NULL_SSL_CTX() throws Exception {
|
693 | 799 | NativeCrypto.SSL_CTX_ech_enable_server(NULL, null, null, null);
|
@@ -933,6 +1039,8 @@ public static class Hooks {
|
933 | 1039 | boolean pskEnabled;
|
934 | 1040 | byte[] pskKey;
|
935 | 1041 | List<String> enabledCipherSuites;
|
| 1042 | + byte[] echRetryConfigs; |
| 1043 | + String echNameOverride; |
936 | 1044 |
|
937 | 1045 | /**
|
938 | 1046 | * @throws SSLException if an error occurs creating the context.
|
@@ -1254,6 +1362,12 @@ public void clientCertificateRequested(long s) {
|
1254 | 1362 | public static Future<TestSSLHandshakeCallbacks> handshake(final ServerSocket listener,
|
1255 | 1363 | final int timeout, final boolean client, final Hooks hooks, final byte[] alpnProtocols,
|
1256 | 1364 | final ApplicationProtocolSelectorAdapter alpnSelector) {
|
| 1365 | + return handshake(listener, timeout, client, hooks, alpnProtocols, alpnSelector, false); |
| 1366 | + } |
| 1367 | + |
| 1368 | + public static Future<TestSSLHandshakeCallbacks> handshake(final ServerSocket listener, |
| 1369 | + final int timeout, final boolean client, final Hooks hooks, final byte[] alpnProtocols, |
| 1370 | + final ApplicationProtocolSelectorAdapter alpnSelector, final boolean useEchRetryConfig) { |
1257 | 1371 | ExecutorService executor = Executors.newSingleThreadExecutor();
|
1258 | 1372 | Future<TestSSLHandshakeCallbacks> future =
|
1259 | 1373 | executor.submit(new Callable<TestSSLHandshakeCallbacks>() {
|
@@ -1294,7 +1408,19 @@ public TestSSLHandshakeCallbacks call() throws Exception {
|
1294 | 1408 | if (!client && alpnSelector != null) {
|
1295 | 1409 | NativeCrypto.setHasApplicationProtocolSelector(s, null, true);
|
1296 | 1410 | }
|
1297 |
| - NativeCrypto.SSL_do_handshake(s, null, fd, callback, timeout); |
| 1411 | + if (useEchRetryConfig) { |
| 1412 | + try { |
| 1413 | + NativeCrypto.SSL_do_handshake(s, null, fd, callback, timeout); |
| 1414 | + } catch (SSLProtocolException e) { |
| 1415 | + hooks.echRetryConfigs = |
| 1416 | + NativeCrypto.SSL_get0_ech_retry_configs(s, null); |
| 1417 | + hooks.echNameOverride = |
| 1418 | + NativeCrypto.SSL_get0_ech_name_override(s, null); |
| 1419 | + throw e; |
| 1420 | + } |
| 1421 | + } else { |
| 1422 | + NativeCrypto.SSL_do_handshake(s, null, fd, callback, timeout); |
| 1423 | + } |
1298 | 1424 | session = NativeCrypto.SSL_get1_session(s, null);
|
1299 | 1425 | if (DEBUG) {
|
1300 | 1426 | System.out.println("ssl=0x" + Long.toString(s, 16)
|
|
0 commit comments