diff --git a/android/lint.xml b/android/lint.xml
index 5d43d7ff6..8ad971986 100644
--- a/android/lint.xml
+++ b/android/lint.xml
@@ -28,4 +28,10 @@
+
+
+
+
+
+
diff --git a/common/src/jni/main/cpp/conscrypt/native_crypto.cc b/common/src/jni/main/cpp/conscrypt/native_crypto.cc
index b4a1010b7..54dfbcdab 100644
--- a/common/src/jni/main/cpp/conscrypt/native_crypto.cc
+++ b/common/src/jni/main/cpp/conscrypt/native_crypto.cc
@@ -126,6 +126,16 @@ static SSL_CIPHER* to_SSL_CIPHER(JNIEnv* env, jlong ssl_cipher_address, bool thr
return ssl_cipher;
}
+static SSL_ECH_KEYS* to_SSL_ECH_KEYS(JNIEnv* env, jlong ssl_ech_keys_address, bool throwIfNull) {
+ SSL_ECH_KEYS* ssl_ech_keys =
+ reinterpret_cast(static_cast(ssl_ech_keys_address));
+ if ((ssl_ech_keys == nullptr) && throwIfNull) {
+ JNI_TRACE("ssl_ech_keys == null");
+ conscrypt::jniutil::throwNullPointerException(env, "ssl_ech_keys == null");
+ }
+ return ssl_ech_keys;
+}
+
template
static T* fromContextObject(JNIEnv* env, jobject contextObject) {
if (contextObject == nullptr) {
@@ -7825,7 +7835,12 @@ static int sslSelect(JNIEnv* env, int type, jobject fdObject, AppData* appData,
if (fds[1].revents & POLLIN) {
char token;
do {
- (void)read(appData->fdsEmergency[0], &token, 1);
+ // TEMP - fixes build error
+ int foo = 0;
+ foo = read(appData->fdsEmergency[0], &token, 1);
+ if (foo > 0) {
+ CONSCRYPT_LOG_VERBOSE("FOO: %d", foo);
+ }
} while (errno == EINTR);
}
}
@@ -7856,7 +7871,12 @@ static void sslNotify(AppData* appData) {
char token = '*';
do {
errno = 0;
- (void)write(appData->fdsEmergency[1], &token, 1);
+ // TEMP - fixes build error
+ int foo = 0;
+ foo = write(appData->fdsEmergency[1], &token, 1);
+ if (foo > 0) {
+ CONSCRYPT_LOG_VERBOSE("FOO: %d", foo);
+ }
} while (errno == EINTR);
errno = errnoBackup;
#endif
@@ -11815,6 +11835,198 @@ static jlong NativeCrypto_SSL_get1_session(JNIEnv* env, jclass, jlong ssl_addres
return reinterpret_cast(SSL_get1_session(ssl));
}
+static void NativeCrypto_SSL_set_enable_ech_grease(JNIEnv* env, jclass, jlong ssl_address,
+ CONSCRYPT_UNUSED jobject ssl_holder,
+ jboolean enable) {
+ CHECK_ERROR_QUEUE_ON_RETURN;
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_enable_ech_grease(%d)", ssl, enable);
+ if (ssl == nullptr) {
+ return;
+ }
+ SSL_set_enable_ech_grease(ssl, enable ? 1 : 0);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_enable_ech_grease(%d) => success", ssl, enable);
+}
+
+static jboolean NativeCrypto_SSL_set1_ech_config_list(JNIEnv* env, jclass, jlong ssl_address,
+ CONSCRYPT_UNUSED jobject ssl_holder,
+ jbyteArray configJavaBytes) {
+ CHECK_ERROR_QUEUE_ON_RETURN;
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set1_ech_config_list(%p)", ssl, configJavaBytes);
+ if (ssl == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "Null pointer, ssl address");
+ ERR_clear_error();
+ return JNI_FALSE;
+ }
+ ScopedByteArrayRO configBytes(env, configJavaBytes);
+ if (configBytes.get() == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "Null pointer, ech config");
+ ERR_clear_error();
+ JNI_TRACE("NativeCrypto_SSL_set1_ech_config_list => could not read config bytes");
+ return JNI_FALSE;
+ }
+ int ret = SSL_set1_ech_config_list(ssl, reinterpret_cast(configBytes.get()),
+ configBytes.size());
+ if (!ret) {
+ conscrypt::jniutil::throwParsingException(env, "Error parsing ECH config");
+ ERR_clear_error();
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set1_ech_config_list(%p) => threw exception", ssl,
+ configJavaBytes);
+ return JNI_FALSE;
+ }
+
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set1_ech_config_list(%p) => %d", ssl, configJavaBytes, ret);
+ return ret;
+}
+
+static jstring NativeCrypto_SSL_get0_ech_name_override(JNIEnv* env, jclass, jlong ssl_address,
+ CONSCRYPT_UNUSED jobject ssl_holder) {
+ CHECK_ERROR_QUEUE_ON_RETURN;
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get0_ech_name_override()", ssl);
+ if (ssl == nullptr) {
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get0_ech_name_override() => nullptr", ssl);
+ return nullptr;
+ }
+ const char* ech_name_override;
+ size_t ech_name_override_len;
+ SSL_get0_ech_name_override(ssl, &ech_name_override, &ech_name_override_len);
+ if (ech_name_override_len > 0) {
+ jstring name = env->NewStringUTF(ech_name_override);
+ return name;
+ }
+ return nullptr;
+}
+
+static jbyteArray NativeCrypto_SSL_get0_ech_retry_configs(JNIEnv* env, jclass, jlong ssl_address,
+ CONSCRYPT_UNUSED jobject ssl_holder) {
+ CHECK_ERROR_QUEUE_ON_RETURN;
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get0_ech_retry_configs()", ssl);
+ if (ssl == nullptr) {
+ return nullptr;
+ }
+ const uint8_t* retry_configs;
+ size_t retry_configs_len;
+ SSL_get0_ech_retry_configs(ssl, &retry_configs, &retry_configs_len);
+ if (retry_configs_len <= 0) {
+ return nullptr;
+ }
+ jbyteArray result = env->NewByteArray(static_cast(retry_configs_len));
+ if (result == nullptr) {
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get0_ech_retry_configs() => creating byte array failed",
+ ssl);
+ return nullptr;
+ }
+ env->SetByteArrayRegion(result, 0, static_cast(retry_configs_len),
+ reinterpret_cast(retry_configs));
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get0_ech_retry_configs() => %p", ssl, result);
+ return result;
+}
+
+static jlong NativeCrypto_SSL_ECH_KEYS_new(JNIEnv* env, jclass) {
+ CHECK_ERROR_QUEUE_ON_RETURN;
+ bssl::UniquePtr sslEchKeys(SSL_ECH_KEYS_new());
+ if (sslEchKeys.get() == nullptr) {
+ conscrypt::jniutil::throwExceptionFromBoringSSLError(env, "SSL_ECH_KEYS_new");
+ return 0;
+ }
+ JNI_TRACE("NativeCrypto_SSL_ECH_KEYS_new => %p", sslEchKeys.get());
+ return (jlong)sslEchKeys.release();
+}
+
+static void NativeCrypto_SSL_ECH_KEYS_up_ref(JNIEnv* env, jclass, jlong ssl_ech_keys_address) {
+ CHECK_ERROR_QUEUE_ON_RETURN;
+ SSL_ECH_KEYS* ssl_ech_keys = to_SSL_ECH_KEYS(env, ssl_ech_keys_address, true);
+ JNI_TRACE("ssl_ech_keys=%p NativeCrypto_SSL_ECH_KEYS_up_ref", ssl_ech_keys);
+ if (ssl_ech_keys == nullptr) {
+ return;
+ }
+ SSL_ECH_KEYS_up_ref(ssl_ech_keys);
+}
+
+static void NativeCrypto_SSL_ECH_KEYS_free(JNIEnv* env, jclass, jlong ssl_ech_keys_address) {
+ CHECK_ERROR_QUEUE_ON_RETURN;
+ SSL_ECH_KEYS* ssl_ech_keys = to_SSL_ECH_KEYS(env, ssl_ech_keys_address, true);
+ JNI_TRACE("ssl_ech_keys=%p NativeCrypto_SSL_ECH_KEYS_free", ssl_ech_keys);
+ if (ssl_ech_keys == nullptr) {
+ return;
+ }
+ SSL_ECH_KEYS_free(ssl_ech_keys);
+}
+
+static jboolean NativeCrypto_SSL_ech_accepted(JNIEnv* env, jclass, jlong ssl_address,
+ CONSCRYPT_UNUSED jobject ssl_holder) {
+ JNI_TRACE("NativeCrypto_SSL_ech_accepted");
+ CHECK_ERROR_QUEUE_ON_RETURN;
+ SSL* ssl = to_SSL(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_ech_accepted", ssl);
+ if (ssl == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "Null pointer, ssl address");
+ ERR_clear_error();
+ return JNI_FALSE;
+ }
+ jboolean accepted = SSL_ech_accepted(ssl);
+
+ if (!accepted) {
+ conscrypt::jniutil::throwParsingException(env, "Invalid ECH config list");
+ ERR_clear_error();
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_ech_accepted => threw exception", ssl);
+ return JNI_FALSE;
+ }
+
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_ech_accepted => %d", ssl, accepted);
+ return accepted;
+}
+
+static jboolean NativeCrypto_SSL_CTX_ech_enable_server(JNIEnv* env, jclass, jlong ssl_ctx_address,
+ CONSCRYPT_UNUSED jobject holder,
+ jbyteArray keyJavaBytes,
+ jbyteArray configJavaBytes) {
+ CHECK_ERROR_QUEUE_ON_RETURN;
+ SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true);
+ JNI_TRACE("NativeCrypto_SSL_CTX_ech_enable_server(keyJavaBytes=%p, configJavaBytes=%p)",
+ keyJavaBytes, configJavaBytes);
+ ScopedByteArrayRO keyBytes(env, keyJavaBytes);
+ if (keyBytes.get() == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "Null pointer, key bytes");
+ ERR_clear_error();
+ JNI_TRACE(
+ "NativeCrypto_SSL_CTX_ech_enable_server => threw exception: "
+ "could not read key bytes");
+ return JNI_FALSE;
+ }
+ ScopedByteArrayRO configBytes(env, configJavaBytes);
+ if (configBytes.get() == nullptr) {
+ conscrypt::jniutil::throwNullPointerException(env, "Null pointer, config bytes");
+ ERR_clear_error();
+ JNI_TRACE(
+ "NativeCrypto_SSL_CTX_ech_enable_server => threw exception: "
+ "could not read config bytes");
+ return JNI_FALSE;
+ }
+ const uint8_t* ech_key = reinterpret_cast(keyBytes.get());
+ size_t ech_key_size = keyBytes.size();
+ const uint8_t* ech_config = reinterpret_cast(configBytes.get());
+ size_t ech_config_size = configBytes.size();
+ bssl::UniquePtr keys(SSL_ECH_KEYS_new());
+ bssl::ScopedEVP_HPKE_KEY key;
+ if (!keys ||
+ !EVP_HPKE_KEY_init(key.get(), EVP_hpke_x25519_hkdf_sha256(), ech_key, ech_key_size) ||
+ !SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1, ech_config, ech_config_size,
+ key.get()) ||
+ !SSL_CTX_set1_ech_keys(ssl_ctx, keys.get())) {
+ conscrypt::jniutil::throwInvalidKeyException(env, "Key config error");
+ ERR_clear_error();
+ JNI_TRACE(
+ "NativeCrypto_SSL_CTX_ech_enable_server: "
+ "Error setting server's ECHConfig and private key\n");
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
// TESTING METHODS END
#define CONSCRYPT_NATIVE_METHOD(functionName, signature) \
@@ -12172,6 +12384,17 @@ static JNINativeMethod sNativeCryptoMethods[] = {
CONSCRYPT_NATIVE_METHOD(Scrypt_generate_key, "([B[BIIII)[B"),
CONSCRYPT_NATIVE_METHOD(SSL_CTX_set_spake_credential, "([B[B[B[BZIJ" REF_SSL_CTX ")V"),
+ // FOR ECH TESTING
+ CONSCRYPT_NATIVE_METHOD(SSL_set_enable_ech_grease, "(J" REF_SSL "Z)V"),
+ CONSCRYPT_NATIVE_METHOD(SSL_set1_ech_config_list, "(J" REF_SSL "[B)Z"),
+ CONSCRYPT_NATIVE_METHOD(SSL_get0_ech_name_override, "(J" REF_SSL ")Ljava/lang/String;"),
+ CONSCRYPT_NATIVE_METHOD(SSL_get0_ech_retry_configs, "(J" REF_SSL ")[B"),
+ CONSCRYPT_NATIVE_METHOD(SSL_ECH_KEYS_new, "()J"),
+ CONSCRYPT_NATIVE_METHOD(SSL_ECH_KEYS_up_ref, "(J)V"),
+ CONSCRYPT_NATIVE_METHOD(SSL_ECH_KEYS_free, "(J)V"),
+ CONSCRYPT_NATIVE_METHOD(SSL_ech_accepted, "(J" REF_SSL ")Z"),
+ CONSCRYPT_NATIVE_METHOD(SSL_CTX_ech_enable_server, "(J" REF_SSL_CTX "[B[B)Z"),
+
// Used for testing only.
CONSCRYPT_NATIVE_METHOD(BIO_read, "(J[B)I"),
CONSCRYPT_NATIVE_METHOD(BIO_write, "(J[BII)V"),
diff --git a/common/src/main/java/org/conscrypt/NativeCrypto.java b/common/src/main/java/org/conscrypt/NativeCrypto.java
index d2222f7b5..2b8ad17b0 100644
--- a/common/src/main/java/org/conscrypt/NativeCrypto.java
+++ b/common/src/main/java/org/conscrypt/NativeCrypto.java
@@ -1623,6 +1623,32 @@ static native byte[] Scrypt_generate_key(
*/
static native boolean usesBoringSsl_FIPS_mode();
+ /* ECH */
+
+ static native void SSL_set_enable_ech_grease(long ssl, NativeSsl ssl_holder, boolean enable);
+
+ static native boolean SSL_set1_ech_config_list(
+ long ssl, NativeSsl ssl_holder, byte[] echConfig);
+
+ static native String SSL_get0_ech_name_override(long ssl, NativeSsl ssl_holder);
+
+ static native byte[] SSL_get0_ech_retry_configs(long ssl, NativeSsl ssl_holder);
+
+ static native byte[] SSL_marshal_ech_config(short configId, byte[] key, String publicName);
+
+ static native long SSL_ECH_KEYS_new();
+
+ static native void SSL_ECH_KEYS_up_ref(long sslEchKeys);
+
+ static native void SSL_ECH_KEYS_free(long sslEchKeys);
+
+ static native byte[] SSL_ECH_KEYS_marshal_retry_configs(byte[] key);
+
+ static native boolean SSL_ech_accepted(long ssl, NativeSsl ssl_holder);
+
+ static native boolean SSL_CTX_ech_enable_server(
+ long sslCtx, AbstractSessionContext holder, byte[] key, byte[] config);
+
/**
* Used for testing only.
*/
diff --git a/openjdk/build.gradle b/openjdk/build.gradle
index b63b4c8de..4092c6e70 100644
--- a/openjdk/build.gradle
+++ b/openjdk/build.gradle
@@ -350,6 +350,15 @@ def testInterop = tasks.register("testInterop", Test) {
}
check.dependsOn testInterop
+// Added to see results of new ECH tests when running tests from the command line
+tasks.withType(Test).configureEach {
+ testLogging {
+ exceptionFormat "full"
+ events "started", "skipped", "passed", "failed"
+ showStandardStreams true
+ }
+}
+
jacocoTestReport {
additionalSourceDirs.from files("$rootDir/openjdk/src/test/java", "$rootDir/common/src/main/java")
executionData tasks.withType(Test)
diff --git a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
index f46e133b8..8ad685064 100644
--- a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
+++ b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
@@ -25,6 +25,7 @@
import static org.conscrypt.NativeConstants.SSL_VERIFY_PEER;
import static org.conscrypt.NativeConstants.TLS1_1_VERSION;
import static org.conscrypt.NativeConstants.TLS1_2_VERSION;
+import static org.conscrypt.NativeConstants.TLS1_3_VERSION;
import static org.conscrypt.NativeConstants.TLS1_VERSION;
import static org.conscrypt.TestUtils.decodeHex;
import static org.conscrypt.TestUtils.isWindows;
@@ -66,6 +67,7 @@
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
@@ -94,6 +96,8 @@
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLProtocolException;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
import javax.security.auth.x500.X500Principal;
@RunWith(JUnit4.class)
@@ -117,7 +121,6 @@ public class NativeCryptoTest {
private static OpenSSLKey CHANNEL_ID_PRIVATE_KEY;
private static byte[] CHANNEL_ID;
private static Method m_Platform_getFileDescriptor;
-
private static RSAPrivateCrtKey TEST_RSA_KEY;
@BeforeClass
@@ -477,6 +480,298 @@ public void test_SSL_set_mode_and_clear_mode() throws Exception {
NativeCrypto.SSL_CTX_free(c, null);
}
+ @Test
+ public void test_SSL_do_handshake_ech_grease_only() throws Exception {
+ final ServerSocket listener = newServerSocket();
+
+ final byte[] key = readTestFile("boringssl-ech-private-key.bin");
+ final byte[] serverConfig = readTestFile("boringssl-server-ech-config.bin");
+ Hooks cHooks = new ClientHooks() {
+ @Override
+ public long beforeHandshake(long c) throws SSLException {
+ long ssl = super.beforeHandshake(c);
+ assertEquals(1,
+ NativeCrypto.SSL_set_protocol_versions(
+ ssl, null, TLS1_VERSION, TLS1_3_VERSION));
+ NativeCrypto.SSL_set_enable_ech_grease(ssl, null, true);
+ return ssl;
+ }
+
+ @Override
+ public void afterHandshake(long session, long ssl, long context, Socket socket,
+ FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+ byte[] retryConfigs = NativeCrypto.SSL_get0_ech_retry_configs(ssl, null);
+ assertEquals(5, retryConfigs.length); // should be the invalid ECH Config List
+ super.afterHandshake(session, ssl, context, socket, fd, callback);
+ }
+ };
+ Hooks sHooks = new ServerHooks(SERVER_PRIVATE_KEY, ENCODED_SERVER_CERTIFICATES) {
+ @Override
+ public long beforeHandshake(long c) throws SSLException {
+ long ssl = super.beforeHandshake(c);
+ assertEquals(1,
+ NativeCrypto.SSL_set_protocol_versions(
+ ssl, null, TLS1_VERSION, TLS1_3_VERSION));
+ assertTrue(NativeCrypto.SSL_CTX_ech_enable_server(c, null, key, serverConfig));
+ return ssl;
+ }
+ };
+ Future client = handshake(listener, 0, true, cHooks, null, null);
+ Future server =
+ handshake(listener, 0, false, sHooks, null, null);
+ TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertTrue(clientCallback.verifyCertificateChainCalled);
+ assertEqualCertificateChains(SERVER_CERTIFICATE_REFS, clientCallback.certificateChainRefs);
+ assertFalse(serverCallback.verifyCertificateChainCalled);
+ assertFalse(clientCallback.clientCertificateRequestedCalled);
+ assertFalse(serverCallback.clientCertificateRequestedCalled);
+ assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+ assertTrue(clientCallback.handshakeCompletedCalled);
+ assertTrue(serverCallback.handshakeCompletedCalled);
+ assertFalse(clientCallback.serverCertificateRequestedInvoked);
+ assertTrue(serverCallback.serverCertificateRequestedInvoked);
+ }
+
+ /** Convenient debug print for ECH Config Lists */
+ private void printEchConfigList(String msg, byte[] buf) {
+ int blen = buf.length;
+ System.out.print(msg + " (" + blen + "):\n ");
+ for (int i = 0; i < blen; i++) {
+ if ((i != 0) && (i % 16 == 0))
+ System.out.print("\n ");
+ System.out.print(String.format("%02x:", Byte.toUnsignedInt(buf[i])));
+ }
+ System.out.print("\n");
+ }
+
+ @Test
+ public void test_SSL_do_handshake_ech_client_server() throws Exception {
+ final ServerSocket listener = newServerSocket();
+
+ final byte[] key = readTestFile("boringssl-ech-private-key.bin");
+ final byte[] serverConfig = readTestFile("boringssl-server-ech-config.bin");
+ final byte[] clientConfigList = readTestFile("boringssl-ech-config-list.bin");
+ Hooks cHooks = new ClientHooks() {
+ @Override
+ public long beforeHandshake(long c) throws SSLException {
+ long ssl = super.beforeHandshake(c);
+ assertEquals(1,
+ NativeCrypto.SSL_set_protocol_versions(
+ ssl, null, TLS1_VERSION, TLS1_3_VERSION));
+ assertTrue(NativeCrypto.SSL_set1_ech_config_list(ssl, null, clientConfigList));
+ return ssl;
+ }
+
+ @Override
+ public void afterHandshake(long session, long ssl, long context, Socket socket,
+ FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+ assertTrue(NativeCrypto.SSL_ech_accepted(ssl, null));
+ super.afterHandshake(session, ssl, context, socket, fd, callback);
+ }
+ };
+ Hooks sHooks = new ServerHooks(SERVER_PRIVATE_KEY, ENCODED_SERVER_CERTIFICATES) {
+ @Override
+ public long beforeHandshake(long c) throws SSLException {
+ long ssl = super.beforeHandshake(c);
+ assertEquals(1,
+ NativeCrypto.SSL_set_protocol_versions(
+ ssl, null, TLS1_VERSION, TLS1_3_VERSION));
+ assertTrue(NativeCrypto.SSL_CTX_ech_enable_server(c, null, key, serverConfig));
+ return ssl;
+ }
+
+ @Override
+ public void afterHandshake(long session, long ssl, long context, Socket socket,
+ FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+ assertTrue(NativeCrypto.SSL_ech_accepted(ssl, null));
+ super.afterHandshake(session, ssl, context, socket, fd, callback);
+ }
+ };
+ Future client = handshake(listener, 0, true, cHooks, null, null);
+ Future server =
+ handshake(listener, 0, false, sHooks, null, null);
+ TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertTrue(clientCallback.verifyCertificateChainCalled);
+ assertEqualCertificateChains(SERVER_CERTIFICATE_REFS, clientCallback.certificateChainRefs);
+ assertFalse(serverCallback.verifyCertificateChainCalled);
+ assertFalse(clientCallback.clientCertificateRequestedCalled);
+ assertFalse(serverCallback.clientCertificateRequestedCalled);
+ assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+ assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+ assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+ assertTrue(clientCallback.handshakeCompletedCalled);
+ assertTrue(serverCallback.handshakeCompletedCalled);
+ assertFalse(clientCallback.serverCertificateRequestedInvoked);
+ assertTrue(serverCallback.serverCertificateRequestedInvoked);
+ }
+
+ @Test
+ public void test_SSL_set_enable_ech_grease() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c, null);
+
+ NativeCrypto.SSL_set_enable_ech_grease(s, null, true);
+ NativeCrypto.SSL_set_enable_ech_grease(s, null, false);
+
+ NativeCrypto.SSL_free(s, null);
+ NativeCrypto.SSL_CTX_free(c, null);
+ }
+
+ @Test
+ public void test_SSL_set1_ech_valid_config_list() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c, null);
+
+ final byte[] configList = readTestFile("boringssl-ech-config-list.bin");
+ assertTrue(NativeCrypto.SSL_set1_ech_config_list(s, null, configList));
+ }
+
+ @Test
+ public void test_SSL_set1_ech_invalid_config_list() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c, null);
+
+ byte[] badConfigList = {
+ 0x00, 0x05, (byte) 0xfe, 0x0d, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+ boolean set = false;
+ assertThrows(ParsingException.class,
+ () -> NativeCrypto.SSL_set1_ech_config_list(s, null, badConfigList));
+ NativeCrypto.SSL_free(s, null);
+ NativeCrypto.SSL_CTX_free(c, null);
+ }
+
+ @Test
+ public void test_SSL_set1_ech_config_list_withNull() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c, null);
+ assertThrows(NullPointerException.class,
+ () -> NativeCrypto.SSL_set1_ech_config_list(s, null, null));
+ }
+
+ @Test
+ public void test_SSL_ECH_KEYS_new() throws Exception {
+ long k = NativeCrypto.SSL_ECH_KEYS_new();
+ NativeCrypto.SSL_ECH_KEYS_up_ref(k);
+ assertTrue(k != NULL);
+ long k2 = NativeCrypto.SSL_ECH_KEYS_new();
+ NativeCrypto.SSL_ECH_KEYS_up_ref(k2);
+ assertTrue(k != k2);
+ NativeCrypto.SSL_ECH_KEYS_free(k);
+ NativeCrypto.SSL_ECH_KEYS_free(k2);
+ }
+
+ @Test
+ public void test_SSL_ech_accepted() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ long s = NativeCrypto.SSL_new(c, null);
+
+ assertThrows(
+ ParsingException.class, () -> assertFalse(NativeCrypto.SSL_ech_accepted(s, null)));
+
+ NativeCrypto.SSL_free(s, null);
+ NativeCrypto.SSL_CTX_free(c, null);
+ }
+
+ @Test
+ public void test_SSL_CTX_ech_enable_server() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+
+ final byte[] key = readTestFile("boringssl-ech-private-key.bin");
+ final byte[] serverConfig = readTestFile("boringssl-server-ech-config.bin");
+ assertTrue(NativeCrypto.SSL_CTX_ech_enable_server(c, null, key, serverConfig));
+
+ NativeCrypto.SSL_CTX_free(c, null);
+ }
+
+ @Test
+ public void test_SSL_get0_ech_retry_configs_withNullShouldThrow() throws Exception {
+ assertThrows(NullPointerException.class,
+ () -> NativeCrypto.SSL_get0_ech_retry_configs(NULL, null));
+ }
+
+ @Test
+ public void test_SSL_CTX_ech_enable_server_NULL_SSL_CTX() throws Exception {
+ assertThrows(NullPointerException.class,
+ () -> NativeCrypto.SSL_CTX_ech_enable_server(NULL, null, null, null));
+ }
+
+ @Test
+ public void test_SSL_CTX_ech_enable_server_ssl_withNullsShouldThrow() {
+ long c = NativeCrypto.SSL_CTX_new();
+ try {
+ NativeCrypto.SSL_CTX_ech_enable_server(c, null, null, null);
+ } catch (NullPointerException | AssertionError e) {
+ // AssertionError when running with checkErrorQueue
+ return;
+ }
+ fail();
+ }
+
+ @Test
+ public void test_SSL_CTX_ech_enable_server_ssl_withNullConfigShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ // TODO running this with checkErrorQueue after
+ // test_SSL_CTX_ech_enable_server_ssl_with_bad_config fails here
+ final byte[] serverConfig = readTestFile("boringssl-server-ech-config.bin");
+ try {
+ NativeCrypto.SSL_CTX_ech_enable_server(c, null, null, serverConfig);
+ } catch (NullPointerException | AssertionError e) {
+ // AssertionError when running with checkErrorQueue
+ return;
+ }
+ fail();
+ }
+
+ @Test
+ public void test_SSL_CTX_ech_enable_server_ssl_withNullKeyShouldThrow() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ final byte[] key = readTestFile("boringssl-ech-private-key.bin");
+ try {
+ NativeCrypto.SSL_CTX_ech_enable_server(c, null, key, null);
+ } catch (NullPointerException | AssertionError e) {
+ // AssertionError when running with checkErrorQueue
+ return;
+ }
+ fail();
+ }
+
+ @Test
+ public void test_SSL_CTX_ech_enable_server_ssl_with_bad_key() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ final byte[] badKey = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+ final byte[] serverConfig = readTestFile("boringssl-server-ech-config.bin");
+ assertThrows(InvalidKeyException.class,
+ ()
+ -> assertFalse(NativeCrypto.SSL_CTX_ech_enable_server(
+ c, null, badKey, serverConfig)));
+ }
+
+ @Test
+ public void test_SSL_CTX_ech_enable_server_ssl_with_bad_config() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ final byte[] key = readTestFile("boringssl-ech-private-key.bin");
+ byte[] badConfig = {(byte) 0xfe, (byte) 0x0d, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+ assertThrows(InvalidKeyException.class,
+ () -> assertFalse(NativeCrypto.SSL_CTX_ech_enable_server(c, null, key, badConfig)));
+ }
+
+ @Test
+ public void test_SSL_CTX_ech_enable_server_ssl_with_bad_key_config() throws Exception {
+ long c = NativeCrypto.SSL_CTX_new();
+ final byte[] badKey = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+ byte[] badConfig = {(byte) 0xfe, (byte) 0x0d, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+ assertThrows(InvalidKeyException.class,
+ ()
+ -> assertFalse(NativeCrypto.SSL_CTX_ech_enable_server(
+ c, null, badKey, badConfig)));
+ }
+
@Test
public void SSL_get_options_withNullShouldThrow() throws Exception {
assertThrows(NullPointerException.class, () -> NativeCrypto.SSL_get_options(NULL, null));
@@ -660,6 +955,8 @@ public static class Hooks {
boolean pskEnabled;
byte[] pskKey;
List enabledCipherSuites;
+ byte[] echRetryConfigs;
+ String echNameOverride;
/**
* @throws SSLException if an error occurs creating the context.
@@ -976,6 +1273,7 @@ public void clientCertificateRequested(long s) {
}
}
+ // wrapper method added for ECH testing
public static Future handshake(final ServerSocket listener,
final int timeout, final boolean client, final Hooks hooks, final byte[] alpnProtocols,
final ApplicationProtocolSelectorAdapter alpnSelector) {
@@ -1020,7 +1318,9 @@ public TestSSLHandshakeCallbacks call() throws Exception {
if (!client && alpnSelector != null) {
NativeCrypto.setHasApplicationProtocolSelector(s, null, true);
}
+
NativeCrypto.SSL_do_handshake(s, null, fd, callback, timeout);
+
session = NativeCrypto.SSL_get1_session(s, null);
if (DEBUG) {
System.out.println("ssl=0x" + Long.toString(s, 16) + " handshake"
diff --git a/openjdk/src/test/resources/boringssl-ech-config-list.bin b/openjdk/src/test/resources/boringssl-ech-config-list.bin
new file mode 100644
index 000000000..b2d4c4baf
Binary files /dev/null and b/openjdk/src/test/resources/boringssl-ech-config-list.bin differ
diff --git a/openjdk/src/test/resources/boringssl-ech-private-key.bin b/openjdk/src/test/resources/boringssl-ech-private-key.bin
new file mode 100644
index 000000000..f391fb419
--- /dev/null
+++ b/openjdk/src/test/resources/boringssl-ech-private-key.bin
@@ -0,0 +1 @@
+ EE��W�~�|�@Ũ��G8d����+�|�
\ No newline at end of file
diff --git a/openjdk/src/test/resources/boringssl-server-ech-config.bin b/openjdk/src/test/resources/boringssl-server-ech-config.bin
new file mode 100644
index 000000000..ddf593ba5
Binary files /dev/null and b/openjdk/src/test/resources/boringssl-server-ech-config.bin differ