From: Daniel McCarney Date: Mon, 24 Mar 2025 16:01:30 +0000 (-0400) Subject: rustls: support ECH GREASE X-Git-Tag: curl-8_13_0~51 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=233b66890380147f7048691a85945407f7e95cec;p=thirdparty%2Fcurl.git rustls: support ECH GREASE e.g. `curl --tlsv1.3 --ech grease ...` --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e04ec203a..cdc1c8f434 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ # HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS # HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT: `SSL_set_quic_use_legacy_codepoint` present in OpenSSL/wolfSSL # HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in quiche -# HAVE_ECH: ECH API checks for OpenSSL, BoringSSL or wolfSSL +# HAVE_ECH: ECH API checks for OpenSSL, BoringSSL, wolfSSL or rustls-ffi # # For each of the above variables, if the variable is DEFINED (either # to ON or OFF), the symbol detection is skipped. If the variable is @@ -1065,7 +1065,7 @@ endif() option(USE_HTTPSRR "Enable HTTPS RR support" OFF) option(USE_ECH "Enable ECH support" OFF) if(USE_ECH) - if(USE_OPENSSL OR USE_WOLFSSL) + if(USE_OPENSSL OR USE_WOLFSSL OR USE_RUSTLS) # Be sure that the TLS library actually supports ECH. if(USE_WOLFSSL) curl_openssl_check_exists("wolfSSL_CTX_GenerateEchConfig" HAVE_WOLFSSL_CTX_GENERATEECHCONFIG) @@ -1074,11 +1074,12 @@ if(USE_ECH) curl_openssl_check_exists("SSL_set1_ech_config_list" HAVE_SSL_SET1_ECH_CONFIG_LIST) endif() if(HAVE_WOLFSSL_CTX_GENERATEECHCONFIG OR - HAVE_SSL_SET1_ECH_CONFIG_LIST) + HAVE_SSL_SET1_ECH_CONFIG_LIST OR + USE_RUSTLS) set(HAVE_ECH 1) endif() if(NOT HAVE_ECH) - message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/AWS-LC/wolfSSL") + message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/AWS-LC/wolfSSL/rustls-ffi") else() message(STATUS "ECH enabled") # ECH wants HTTPSRR @@ -1086,7 +1087,7 @@ if(USE_ECH) message(STATUS "HTTPSRR enabled") endif() else() - message(FATAL_ERROR "ECH requires ECH-enabled OpenSSL, BoringSSL, AWS-LC or wolfSSL") + message(FATAL_ERROR "ECH requires ECH-enabled OpenSSL, BoringSSL, AWS-LC, wolfSSL or rustls-ffi") endif() endif() diff --git a/configure.ac b/configure.ac index f54f5f8270..0768b902f1 100644 --- a/configure.ac +++ b/configure.ac @@ -4888,6 +4888,7 @@ if test "x$want_ech" != "xno"; then ECH_ENABLED=0 ECH_ENABLED_OPENSSL=0 ECH_ENABLED_WOLFSSL=0 + ECH_ENABLED_RUSTLS=0 ECH_SUPPORT='' dnl check for OpenSSL equivalent @@ -4901,10 +4902,15 @@ if test "x$want_ech" != "xno"; then ECH_SUPPORT="$ECH_SUPPORT wolfSSL" ECH_ENABLED_WOLFSSL=1) fi + if test "x$RUSTLS_ENABLED" = "x1"; then + ECH_SUPPORT="$ECH_SUPPORT rustls-ffi" + ECH_ENABLED_RUSTLS=1 + fi dnl now deal with whatever we found if test "x$ECH_ENABLED_OPENSSL" = "x1" -o \ - "x$ECH_ENABLED_WOLFSSL" = "x1"; then + "x$ECH_ENABLED_WOLFSSL" = "x1" -o \ + "x$ECH_ENABLED_RUSTLS" = "x1"; then AC_DEFINE(USE_ECH, 1, [if ECH support is available]) AC_MSG_RESULT(ECH support available via:$ECH_SUPPORT) experimental="$experimental ECH" diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 11c9a3a662..f1a1c897fd 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -588,6 +588,14 @@ init_config_builder(struct Curl_easy *data, goto cleanup; } +#if defined(USE_ECH) + if(ECH_ENABLED(data)) { + tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3; + tls_versions_len = 1; + infof(data, "rustls: ECH enabled, forcing TLSv1.3"); + } +#endif /* USE_ECH */ + cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len)); if(!cipher_suites) { result = CURLE_OUT_OF_MEMORY; @@ -889,6 +897,38 @@ cleanup: return result; } +#if defined(USE_ECH) +static CURLcode +init_config_builder_ech(struct Curl_easy *data, + struct rustls_client_config_builder *builder) +{ + const rustls_hpke *hpke = rustls_supported_hpke(); + + if(!hpke) { + failf(data, + "rustls: ECH unavailable, rustls-ffi built without " + "HPKE compatible crypto provider"); + return CURLE_SSL_CONNECT_ERROR; + } + + if(data->set.str[STRING_ECH_PUBLIC]) { + failf(data, "rustls: ECH outername not supported"); + return CURLE_SSL_CONNECT_ERROR; + } + + if(data->set.tls_ech == CURLECH_GREASE) { + rustls_result rr; + rr = rustls_client_config_builder_enable_ech_grease(builder, hpke); + if(rr != RUSTLS_RESULT_OK) { + rustls_failf(data, rr, "rustls: failed to configure ECH GREASE"); + return CURLE_SSL_CONNECT_ERROR; + } + } + + return CURLE_OK; +} +#endif /* USE_ECH */ + static CURLcode cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, struct rustls_ssl_backend_data *const backend) @@ -946,6 +986,16 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, } } +#if defined(USE_ECH) + if(ECH_ENABLED(data)) { + result = init_config_builder_ech(data, config_builder); + if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) { + rustls_client_config_builder_free(config_builder); + return result; + } + } +#endif /* USE_ECH */ + result = init_config_builder_keylog(data, config_builder); if(result != CURLE_OK) { rustls_client_config_builder_free(config_builder); @@ -1269,7 +1319,8 @@ const struct Curl_ssl Curl_ssl_rustls = { SSLSUPP_HTTPS_PROXY | SSLSUPP_CIPHER_LIST | SSLSUPP_TLS13_CIPHERSUITES | - SSLSUPP_CERTINFO, + SSLSUPP_CERTINFO | + SSLSUPP_ECH, sizeof(struct rustls_ssl_backend_data), NULL, /* init */