]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
rustls: support ECH GREASE
authorDaniel McCarney <daniel@binaryparadox.net>
Mon, 24 Mar 2025 16:01:30 +0000 (12:01 -0400)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 27 Mar 2025 07:47:51 +0000 (08:47 +0100)
e.g. `curl --tlsv1.3 --ech grease ...`

CMakeLists.txt
configure.ac
lib/vtls/rustls.c

index 8e04ec203afc6b5ad57d39ab35becd1eace2950c..cdc1c8f43409f47546abbde1b99f1bb8a7792ce4 100644 (file)
@@ -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()
 
index f54f5f8270b5b6d8472ebab8368f7db605526ec4..0768b902f115233bb836ef3ac8b8bae37c899010 100644 (file)
@@ -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"
index 11c9a3a662d2ac9e7bf774843e9bda5523c789b9..f1a1c897fd95967a4f991bc10a674de3b0cf11c2 100644 (file)
@@ -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 */