From d7d67db2c79a6ee95985b427aecb872394c5c64d Mon Sep 17 00:00:00 2001 From: Yifei Kong Date: Mon, 1 Apr 2024 14:03:41 +0800 Subject: [PATCH] Add ssl_set_extension_order(not working though) --- Makefile.in | 49 --- chrome/patches/boringssl-old-ciphers.patch | 341 ++++++++++++--- chrome/patches/curl-impersonate.patch | 475 ++++++++++++--------- 3 files changed, 552 insertions(+), 313 deletions(-) diff --git a/Makefile.in b/Makefile.in index e71e545f..3af14065 100644 --- a/Makefile.in +++ b/Makefile.in @@ -65,55 +65,6 @@ help: ## Show this help message .PHONY: help .DEFAULT_GOAL := help -firefox-build: $(CURL_VERSION)/.firefox ## Build the Firefox version of curl-impersonate - cd $(CURL_VERSION) - # Don't pass this Makefile's MAKEFLAGS - $(MAKE) MAKEFLAGS=-j$(SUBJOBS) -.PHONY: firefox-build - -firefox-checkbuild: ## Run basic checks on the built binary -ifeq ($(host),$(build)) - cd $(CURL_VERSION) - # Make sure all needed features were compiled in - ./src/curl-impersonate-ff -V | grep -q zlib - ./src/curl-impersonate-ff -V | grep -q brotli - ./src/curl-impersonate-ff -V | grep -q nghttp2 - ./src/curl-impersonate-ff -V | grep -q NSS - $(info Build OK) -else - $(info Cross compiling, skipping checkbuild) -endif -.PHONY: firefox-checkbuild - -firefox-install: ## Install the Firefox version of curl-impersonate after build - cd $(CURL_VERSION) - $(MAKE) install-exec MAKEFLAGS= - # Wrapper scripts for the Firefox version (e.g. 'curl_ff98') - install $(srcdir)/firefox/curl_ff* @bindir@ -.PHONY: firefox-install - -firefox-install-strip: ## Like 'firefox-install', but strip binaries for smaller size - cd $(CURL_VERSION) - $(MAKE) install-exec MAKEFLAGS= - # We could have used 'install-strip' but then the docs would be installed as well. - # Instead strip manually. - $(STRIP) @bindir@/curl-impersonate-ff - # Wrapper scripts for the Firefox version (e.g. 'curl_ff98') - install $(srcdir)/firefox/curl_ff* @bindir@ -.PHONY: firefox-install-strip - -firefox-uninstall: ## Uninstall the Firefox version of curl-impersonate after 'make install' - cd $(CURL_VERSION) - $(MAKE) uninstall MAKEFLAGS= - rm -Rf @bindir@/curl_ff* -.PHONY: firefox-uninstall - -firefox-clean: ## Clean build artifacts of the Firefox version. Use after re-running './configure' - cd $(CURL_VERSION) - $(MAKE) clean MAKEFLAGS= - rm -f .firefox -.PHONY: firefox-clean - chrome-build: $(CURL_VERSION)/.chrome ## Build the Chrome version of curl-impersonate cd $(CURL_VERSION) # Don't pass this Makefile's MAKEFLAGS diff --git a/chrome/patches/boringssl-old-ciphers.patch b/chrome/patches/boringssl-old-ciphers.patch index 2b4a755e..4d1cdbed 100644 --- a/chrome/patches/boringssl-old-ciphers.patch +++ b/chrome/patches/boringssl-old-ciphers.patch @@ -1,8 +1,174 @@ +diff --git a/export.sh b/export.sh +new file mode 100755 +index 000000000..3375b870d +--- /dev/null ++++ b/export.sh +@@ -0,0 +1,4 @@ ++#!/bin/bash ++ ++git df d24a382 > boringssl-old-ciphers.patch ++mv boringssl-old-ciphers.patch ../curl-impersonate/chrome/patches/boringssl-old-ciphers.patch +diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h +index e500dd76e..487945969 100644 +--- a/include/openssl/ssl.h ++++ b/include/openssl/ssl.h +@@ -1560,6 +1560,9 @@ OPENSSL_EXPORT int SSL_CTX_set_strict_cipher_list(SSL_CTX *ctx, + // garbage inputs, unless an empty cipher list results. + OPENSSL_EXPORT int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str); + ++// curl-impersonate: set the extension order by given string ++OPENSSL_EXPORT int SSL_CTX_set_extension_order(SSL_CTX *ctx, char *order); ++ + // SSL_set_strict_cipher_list configures the cipher list for |ssl|, evaluating + // |str| as a cipher string and returning error if |str| contains anything + // meaningless. It returns one on success and zero on failure. +@@ -4583,6 +4586,9 @@ OPENSSL_EXPORT void SSL_CTX_set_grease_enabled(SSL_CTX *ctx, int enabled); + // permute extensions. For now, this is only implemented for the ClientHello. + OPENSSL_EXPORT void SSL_CTX_set_permute_extensions(SSL_CTX *ctx, int enabled); + ++// curl-impersonate ++OPENSSL_EXPORT int SSL_CTX_set_extension_order(SSL_CTX *ctx, char *order); ++ + // SSL_set_permute_extensions configures whether sockets on |ssl| should + // permute extensions. For now, this is only implemented for the ClientHello. + OPENSSL_EXPORT void SSL_set_permute_extensions(SSL *ssl, int enabled); +diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h +index c1207a3b7..2e25f767d 100644 +--- a/include/openssl/tls1.h ++++ b/include/openssl/tls1.h +@@ -222,6 +222,9 @@ extern "C" { + // ExtensionType value from RFC 8879 + #define TLSEXT_TYPE_cert_compression 27 + ++// ExtensionType value from RFC 8449 ++#define TLSEXT_TYPE_record_size_limit 28 ++ + // ExtensionType value from RFC 4507 + #define TLSEXT_TYPE_session_ticket 35 + +diff --git a/ssl/extensions.cc b/ssl/extensions.cc +index b13400097..6f301deb1 100644 +--- a/ssl/extensions.cc ++++ b/ssl/extensions.cc +@@ -115,6 +115,7 @@ + + #include + #include ++#include + + #include + #include +@@ -3313,6 +3314,7 @@ bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs) { + !permutation.Init(kNumExtensions)) { + return false; + } ++ // By default, nothing is permuted. + for (size_t i = 0; i < kNumExtensions; i++) { + permutation[i] = i; + } +@@ -3324,6 +3326,69 @@ bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs) { + return true; + } + ++// curl-impersonate: set customized extension order ++// ++// Generate the extension_permutation array from the customized extension order ++// string. ++// ++// The customized extension order string is a dash-separated list of ++// extensions. Each extension is a string of the form "extension_name[:index]". ++// The index is optional and defaults to 0. ++// ++bool ssl_set_extension_order(SSL_HANDSHAKE *hs) { ++ if (hs->config->extension_order == nullptr) { ++ return true; ++ } ++ static std::map type2order = { ++ {TLSEXT_TYPE_server_name, 0}, ++ {TLSEXT_TYPE_encrypted_client_hello, 1}, ++ {TLSEXT_TYPE_extended_master_secret, 2}, ++ {TLSEXT_TYPE_renegotiate, 3}, ++ {TLSEXT_TYPE_supported_groups, 4}, ++ {TLSEXT_TYPE_ec_point_formats, 5}, ++ {TLSEXT_TYPE_session_ticket, 6}, ++ {TLSEXT_TYPE_application_layer_protocol_negotiation, 7}, ++ {TLSEXT_TYPE_status_request, 8}, ++ {TLSEXT_TYPE_signature_algorithms, 9}, ++ {TLSEXT_TYPE_next_proto_neg, 10}, ++ {TLSEXT_TYPE_certificate_timestamp, 11}, ++ {TLSEXT_TYPE_channel_id, 12}, ++ {TLSEXT_TYPE_srtp, 13}, ++ {TLSEXT_TYPE_key_share, 14}, ++ {TLSEXT_TYPE_psk_key_exchange_modes, 15}, ++ {TLSEXT_TYPE_early_data, 16}, ++ {TLSEXT_TYPE_supported_versions, 17}, ++ {TLSEXT_TYPE_cookie, 18}, ++ {TLSEXT_TYPE_quic_transport_parameters, 19}, ++ {TLSEXT_TYPE_quic_transport_parameters_legacy, 20}, ++ {TLSEXT_TYPE_cert_compression, 21}, ++ {TLSEXT_TYPE_delegated_credential, 22}, ++ {TLSEXT_TYPE_application_settings, 23}, ++ {TLSEXT_TYPE_application_settings_old, 24} ++ }; ++ Array order; ++ if (!order.Init(kNumExtensions)) { ++ return false; ++ } ++ // By default, nothing is reordered. ++ for (size_t i = 0; i < kNumExtensions; i++) { ++ order[i] = 255; ++ } ++ // split the order string, and put there order in the table ++ const char *delimiter = "-"; ++ char *tmp = strdup(hs->config->extension_order); ++ char *ext = strtok(tmp, delimiter); ++ size_t idx = 0; ++ while (ext != nullptr) { ++ order[idx] = type2order[atoi(ext)]; ++ ext = strtok(NULL, delimiter); ++ idx++; ++ } ++ ++ hs->extension_permutation = std::move(order); ++ return true; ++} ++ + static const struct tls_extension *tls_extension_find(uint32_t *out_index, + uint16_t value) { + unsigned i; +@@ -3337,6 +3402,17 @@ static const struct tls_extension *tls_extension_find(uint32_t *out_index, + return NULL; + } + ++// static bool add_record_size_limit_extension(CBB *cbb, uint16_t ext, uint16_t limit) { ++// CBB child; ++// if (!CBB_add_u16(cbb, ext) || ++// !CBB_add_u16_length_prefixed(cbb, &child) || ++// !CBB_add_u16(&child, limit)) { ++// OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); ++// return false; ++// } ++// return CBB_flush(cbb); ++// } ++ + static bool add_padding_extension(CBB *cbb, uint16_t ext, size_t len) { + CBB child; + if (!CBB_add_u16(cbb, ext) || // +@@ -3383,6 +3459,9 @@ static bool ssl_add_clienthello_tlsext_inner(SSL_HANDSHAKE *hs, CBB *out, + size_t i = hs->extension_permutation.empty() + ? unpermuted + : hs->extension_permutation[unpermuted]; ++ // curl-impersonate: skip non-exist extensions ++ if (i == 255) { continue; } ++ // end of curl-impersonate + const size_t len_before = CBB_len(&extensions); + const size_t len_compressed_before = CBB_len(compressed.get()); + if (!kExtensions[i].add_clienthello(hs, &extensions, compressed.get(), diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc -index 971ebd0b1..def530b33 100644 +index 971ebd0b1..2877c87ef 100644 --- a/ssl/handshake_client.cc +++ b/ssl/handshake_client.cc -@@ -215,13 +215,13 @@ static void ssl_get_client_disabled(const SSL_HANDSHAKE *hs, +@@ -215,14 +215,6 @@ static void ssl_get_client_disabled(const SSL_HANDSHAKE *hs, } } @@ -13,24 +179,16 @@ index 971ebd0b1..def530b33 100644 - } - return true; -} -+// static bool ssl_add_tls13_cipher(CBB *cbb, uint16_t cipher_id, -+// ssl_compliance_policy_t policy) { -+// if (ssl_tls13_cipher_meets_policy(cipher_id, policy)) { -+// return CBB_add_u16(cbb, cipher_id); -+// } -+// return true; -+// } - +- static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out, ssl_client_hello_type_t type) { -@@ -240,28 +240,37 @@ static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out, + const SSL *const ssl = hs->ssl; +@@ -240,28 +232,7 @@ static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out, return false; } -+ // curl-impersonate: these suites are added uniformly with other suites. -+ // - // Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on - // hardware support. +- // Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on +- // hardware support. - if (hs->max_version >= TLS1_3_VERSION) { - const bool has_aes_hw = ssl->config->aes_hw_override - ? ssl->config->aes_hw_override_value @@ -51,38 +209,12 @@ index 971ebd0b1..def530b33 100644 - return false; - } - } -+ // if (hs->max_version >= TLS1_3_VERSION) { -+ // const bool has_aes_hw = ssl->config->aes_hw_override -+ // ? ssl->config->aes_hw_override_value -+ // : EVP_has_aes_hardware(); -+ // -+ // TLS 1.3 suites are mandatory, see: -+ // https://datatracker.ietf.org/doc/html/rfc8446#section-9.1 -+ // -+ // If there is AES hardware, which is common these days, BoringSSL uses this order: -+ // - TLS1_3_CK_AES_128_GCM_SHA256 -+ // - TLS1_3_CK_AES_256_GCM_SHA384 -+ // - TLS1_3_CK_CHACHA20_POLY1305_SHA256 -+ // if ((!has_aes_hw && // -+ // !ssl_add_tls13_cipher(&child, -+ // TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, -+ // ssl->config->tls13_cipher_policy)) || -+ // !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff, -+ // ssl->config->tls13_cipher_policy) || -+ // !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff, -+ // ssl->config->tls13_cipher_policy) || -+ // (has_aes_hw && // -+ // !ssl_add_tls13_cipher(&child, -+ // TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff, -+ // ssl->config->tls13_cipher_policy))) { -+ // return false; -+ // } -+ // } ++ // curl-impersonate: TLS 1.3 suites are added uniformly with other suites. if (hs->min_version < TLS1_3_VERSION && type != ssl_client_hello_inner) { bool any_enabled = false; diff --git a/ssl/internal.h b/ssl/internal.h -index c9facb699..ec3c21fed 100644 +index c9facb699..3ca0ff505 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -574,9 +574,14 @@ BSSL_NAMESPACE_BEGIN @@ -101,8 +233,58 @@ index c9facb699..ec3c21fed 100644 // Bits for |algorithm_prf| (handshake digest). #define SSL_HANDSHAKE_MAC_DEFAULT 0x1 +@@ -2161,6 +2166,9 @@ bssl::UniquePtr tls13_create_session_with_ticket(SSL *ssl, + // for |hs|, if applicable. It returns true on success and false on error. + bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs); + ++// curl-impersonate ++bool ssl_set_extension_order(SSL_HANDSHAKE *hs); ++ + // ssl_setup_key_shares computes client key shares and saves them in |hs|. It + // returns true on success and false on failure. If |override_group_id| is zero, + // it offers the default groups, including GREASE. If it is non-zero, it offers +@@ -3033,6 +3041,9 @@ struct SSL_CONFIG { + // crypto + UniquePtr cipher_list; + ++ // curl-impersonate ++ char *extension_order = nullptr; ++ + // This is used to hold the local certificate used (i.e. the server + // certificate for a server or the client certificate for a client). + UniquePtr cert; +@@ -3103,6 +3114,9 @@ struct SSL_CONFIG { + // ClientHello. + bool ech_grease_enabled : 1; + ++ // curl-impersonate ++ // uint16_t record_size_limit : 0; ++ + // Enable signed certificate time stamps. Currently client only. + bool signed_cert_timestamps_enabled : 1; + +@@ -3490,6 +3504,9 @@ struct ssl_ctx_st { + + bssl::UniquePtr cipher_list; + ++ // curl-impersonate ++ char *extension_order = nullptr; ++ + X509_STORE *cert_store = nullptr; + LHASH_OF(SSL_SESSION) *sessions = nullptr; + // Most session-ids that will be cached, default is +@@ -3727,6 +3744,9 @@ struct ssl_ctx_st { + // whether OCSP stapling will be requested. + bool ocsp_stapling_enabled : 1; + ++ // curl-impersonate: record size limit ++ // uint16_t record_size_limit : 0; ++ + // If true, a client will request certificate timestamps. + bool signed_cert_timestamps_enabled : 1; + diff --git a/ssl/ssl_cipher.cc b/ssl/ssl_cipher.cc -index fd8cef95d..deb12b0df 100644 +index fd8cef95d..360dc0710 100644 --- a/ssl/ssl_cipher.cc +++ b/ssl/ssl_cipher.cc @@ -197,6 +197,37 @@ static constexpr SSL_CIPHER kCiphers[] = { @@ -244,7 +426,7 @@ index fd8cef95d..deb12b0df 100644 // GCM based TLS v1.2 ciphersuites from RFC 5289 -@@ -467,15 +571,15 @@ Span AllCiphers() { +@@ -467,16 +571,6 @@ Span AllCiphers() { return MakeConstSpan(kCiphers, OPENSSL_ARRAY_SIZE(kCiphers)); } @@ -257,19 +439,11 @@ index fd8cef95d..deb12b0df 100644 - } - return num; -} -+// static constexpr size_t NumTLS13Ciphers() { -+// size_t num = 0; -+// for (const auto &cipher : kCiphers) { -+// if (cipher.algorithm_mkey == SSL_kGENERIC) { -+// num++; -+// } -+// } -+// return num; -+// } - +- #define CIPHER_ADD 1 #define CIPHER_KILL 2 -@@ -554,6 +658,11 @@ static const CIPHER_ALIAS kCipherAliases[] = { + #define CIPHER_DEL 3 +@@ -554,6 +648,11 @@ static const CIPHER_ALIAS kCipherAliases[] = { // MAC aliases {"SHA1", ~0u, ~0u, ~0u, SSL_SHA1, 0}, {"SHA", ~0u, ~0u, ~0u, SSL_SHA1, 0}, @@ -281,15 +455,15 @@ index fd8cef95d..deb12b0df 100644 // Legacy protocol minimum version aliases. "TLSv1" is intentionally the // same as "SSLv3". -@@ -1154,13 +1263,20 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, +@@ -1154,13 +1253,20 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, TLS1_CK_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 & 0xffff, }; static const uint16_t kLegacyCiphers[] = { + TLS1_CK_RSA_WITH_AES_128_SHA256 & 0xffff, + TLS1_CK_RSA_WITH_AES_256_SHA256 & 0xffff, -+ 0x0300C008 & 0xffff, ++ TLS1_CK_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA & 0xffff, TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA & 0xffff, -+ 0x0300C012 & 0xffff, ++ TLS1_CK_ECDHE_RSA_WITH_DES_192_CBC3_SHA & 0xffff, TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA & 0xffff, TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA & 0xffff, TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA & 0xffff, @@ -302,7 +476,7 @@ index fd8cef95d..deb12b0df 100644 TLS1_CK_RSA_WITH_AES_128_GCM_SHA256 & 0xffff, TLS1_CK_RSA_WITH_AES_256_GCM_SHA384 & 0xffff, TLS1_CK_RSA_WITH_AES_128_SHA & 0xffff, -@@ -1169,11 +1285,16 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, +@@ -1169,11 +1275,16 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, TLS1_CK_PSK_WITH_AES_256_CBC_SHA & 0xffff, SSL3_CK_RSA_DES_192_CBC3_SHA & 0xffff, }; @@ -322,7 +496,7 @@ index fd8cef95d..deb12b0df 100644 for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(co_list); i++) { co_list[i].next = i + 1 < OPENSSL_ARRAY_SIZE(co_list) ? &co_list[i + 1] : nullptr; -@@ -1209,8 +1330,13 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, +@@ -1209,8 +1320,13 @@ bool ssl_create_cipher_list(UniquePtr *out_cipher_list, co_list[num++].cipher = SSL_get_cipher_by_value(id); assert(co_list[num - 1].cipher != nullptr); } @@ -337,6 +511,51 @@ index fd8cef95d..deb12b0df 100644 OPENSSL_ARRAY_SIZE(kCiphers), "Not all ciphers are included in the cipher order"); +diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc +index 58b68e675..6f1d170ef 100644 +--- a/ssl/ssl_lib.cc ++++ b/ssl/ssl_lib.cc +@@ -528,6 +528,7 @@ ssl_ctx_st::ssl_ctx_st(const SSL_METHOD *ssl_method) + retain_only_sha256_of_client_certs(false), + quiet_shutdown(false), + ocsp_stapling_enabled(false), ++ // record_size_limit(0), + signed_cert_timestamps_enabled(false), + channel_id_enabled(false), + grease_enabled(false), +@@ -681,6 +682,7 @@ SSL *SSL_new(SSL_CTX *ctx) { + ssl->config->channel_id_enabled = ctx->channel_id_enabled; + ssl->config->channel_id_private = UpRef(ctx->channel_id_private); + ++ // ssl->config->record_size_limit = ctx->record_size_limit; + ssl->config->signed_cert_timestamps_enabled = + ctx->signed_cert_timestamps_enabled; + ssl->config->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled; +@@ -2188,6 +2190,11 @@ void SSL_set_custom_verify( + ssl->config->custom_verify_callback = callback; + } + ++// curl-impersonate: external API for record size limit ++// void SSL_CTX_add_record_size_limit(SSL_CTX, *ctx, uint16_t limit) { ++// ctx->record_size_limit = limit; ++// } ++ + void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx) { + ctx->signed_cert_timestamps_enabled = true; + } +@@ -3015,6 +3022,12 @@ void SSL_CTX_set_permute_extensions(SSL_CTX *ctx, int enabled) { + ctx->permute_extensions = !!enabled; + } + ++// curl-impersonate: set extensions order ++int SSL_CTX_set_extension_order(SSL_CTX *ctx, char *order) { ++ ctx->extension_order = order; ++ return 0; ++} ++ + void SSL_set_permute_extensions(SSL *ssl, int enabled) { + if (!ssl->config) { + return; diff --git a/ssl/ssl_privkey.cc b/ssl/ssl_privkey.cc index 57116cd6c..fa1652832 100644 --- a/ssl/ssl_privkey.cc diff --git a/chrome/patches/curl-impersonate.patch b/chrome/patches/curl-impersonate.patch index b7874213..4ca65105 100644 --- a/chrome/patches/curl-impersonate.patch +++ b/chrome/patches/curl-impersonate.patch @@ -421,7 +421,7 @@ index 3b536000a..d7135698f 100644 /** * Return the n-th header entry or NULL if it does not exist. diff --git a/lib/easy.c b/lib/easy.c -index 322d1a41b..95a6dff18 100644 +index 322d1a41b..e201fbc43 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -74,6 +74,8 @@ @@ -433,7 +433,7 @@ index 322d1a41b..95a6dff18 100644 #include "easy_lock.h" -@@ -341,6 +343,194 @@ CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, +@@ -341,6 +343,202 @@ CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, return rc; } @@ -569,6 +569,14 @@ index 322d1a41b..95a6dff18 100644 + return ret; + } + ++ if(opts->tls_grease) { ++ ret = curl_easy_setopt(data, CURLOPT_TLS_GREASE, opts->tls_grease); ++ } ++ ++ if(opts->tls_extension_order) { ++ ret = curl_easy_setopt(data, CURLOPT_TLS_EXTENSION_ORDER, opts->tls_extension_order); ++ } ++ + /* Always enable all supported compressions. */ + ret = curl_easy_setopt(data, CURLOPT_ACCEPT_ENCODING, ""); + if(ret) @@ -628,7 +636,7 @@ index 322d1a41b..95a6dff18 100644 /* * curl_easy_init() is the external interface to alloc, setup and init an * easy handle that is returned. If anything goes wrong, NULL is returned. -@@ -349,6 +539,8 @@ struct Curl_easy *curl_easy_init(void) +@@ -349,6 +547,8 @@ struct Curl_easy *curl_easy_init(void) { CURLcode result; struct Curl_easy *data; @@ -637,7 +645,7 @@ index 322d1a41b..95a6dff18 100644 /* Make sure we inited the global SSL stuff */ global_init_lock(); -@@ -371,6 +563,29 @@ struct Curl_easy *curl_easy_init(void) +@@ -371,6 +571,29 @@ struct Curl_easy *curl_easy_init(void) return NULL; } @@ -667,7 +675,7 @@ index 322d1a41b..95a6dff18 100644 return data; } -@@ -945,6 +1160,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) +@@ -945,6 +1168,13 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) outcurl->state.referer_alloc = TRUE; } @@ -681,7 +689,7 @@ index 322d1a41b..95a6dff18 100644 /* Reinitialize an SSL engine for the new handle * note: the engine name has already been copied by dupset */ if(outcurl->set.str[STRING_SSL_ENGINE]) { -@@ -1004,6 +1226,9 @@ fail: +@@ -1004,6 +1234,9 @@ fail: */ void curl_easy_reset(struct Curl_easy *data) { @@ -691,7 +699,7 @@ index 322d1a41b..95a6dff18 100644 Curl_free_request_state(data); /* zero out UserDefined data: */ -@@ -1028,6 +1253,23 @@ void curl_easy_reset(struct Curl_easy *data) +@@ -1028,6 +1261,23 @@ void curl_easy_reset(struct Curl_easy *data) #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); #endif @@ -1402,10 +1410,10 @@ index 80e183480..8ee390b7e 100644 * Store nghttp2 version info in this buffer. diff --git a/lib/impersonate.c b/lib/impersonate.c new file mode 100644 -index 000000000..1b6eeb9e9 +index 000000000..4a41daf7c --- /dev/null +++ b/lib/impersonate.c -@@ -0,0 +1,784 @@ +@@ -0,0 +1,818 @@ +#include "curl_setup.h" + +#include @@ -1453,7 +1461,9 @@ index 000000000..1b6eeb9e9 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .extension_order = NULL, ++ .ssl_grease = true + }, + { + .target = "chrome100", @@ -1495,7 +1505,9 @@ index 000000000..1b6eeb9e9 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "chrome101", @@ -1537,7 +1549,9 @@ index 000000000..1b6eeb9e9 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "chrome104", @@ -1579,7 +1593,9 @@ index 000000000..1b6eeb9e9 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "chrome107", @@ -1621,181 +1637,191 @@ index 000000000..1b6eeb9e9 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;2:0;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { -+ .target = "chrome110", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "ECDHE-ECDSA-AES128-GCM-SHA256," -+ "ECDHE-RSA-AES128-GCM-SHA256," -+ "ECDHE-ECDSA-AES256-GCM-SHA384," -+ "ECDHE-RSA-AES256-GCM-SHA384," -+ "ECDHE-ECDSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-AES128-SHA," -+ "ECDHE-RSA-AES256-SHA," -+ "AES128-GCM-SHA256," -+ "AES256-GCM-SHA384," -+ "AES128-SHA," -+ "AES256-SHA", -+ .npn = false, -+ .alpn = true, -+ .alps = true, -+ .tls_permute_extensions = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"", -+ "sec-ch-ua-mobile: ?0", -+ "sec-ch-ua-platform: \"Windows\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", -+ "Sec-Fetch-Site: none", -+ "Sec-Fetch-Mode: navigate", -+ "Sec-Fetch-User: ?1", -+ "Sec-Fetch-Dest: document", -+ "Accept-Encoding: gzip, deflate, br", -+ "Accept-Language: en-US,en;q=0.9" -+ }, -+ .http2_settings = "1:65536;2:0;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .target = "chrome110", ++ .httpversion = CURL_HTTP_VERSION_2_0, ++ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, ++ .ciphers = ++ "TLS_AES_128_GCM_SHA256," ++ "TLS_AES_256_GCM_SHA384," ++ "TLS_CHACHA20_POLY1305_SHA256," ++ "ECDHE-ECDSA-AES128-GCM-SHA256," ++ "ECDHE-RSA-AES128-GCM-SHA256," ++ "ECDHE-ECDSA-AES256-GCM-SHA384," ++ "ECDHE-RSA-AES256-GCM-SHA384," ++ "ECDHE-ECDSA-CHACHA20-POLY1305," ++ "ECDHE-RSA-CHACHA20-POLY1305," ++ "ECDHE-RSA-AES128-SHA," ++ "ECDHE-RSA-AES256-SHA," ++ "AES128-GCM-SHA256," ++ "AES256-GCM-SHA384," ++ "AES128-SHA," ++ "AES256-SHA", ++ .npn = false, ++ .alpn = true, ++ .alps = true, ++ .tls_permute_extensions = true, ++ .tls_session_ticket = true, ++ .cert_compression = "brotli", ++ .http_headers = { ++ "sec-ch-ua: \"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"", ++ "sec-ch-ua-mobile: ?0", ++ "sec-ch-ua-platform: \"Windows\"", ++ "Upgrade-Insecure-Requests: 1", ++ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", ++ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", ++ "Sec-Fetch-Site: none", ++ "Sec-Fetch-Mode: navigate", ++ "Sec-Fetch-User: ?1", ++ "Sec-Fetch-Dest: document", ++ "Accept-Encoding: gzip, deflate, br", ++ "Accept-Language: en-US,en;q=0.9" ++ }, ++ .http2_settings = "1:65536;2:0;3:1000;4:6291456;6:262144", ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { -+ .target = "chrome116", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "ECDHE-ECDSA-AES128-GCM-SHA256," -+ "ECDHE-RSA-AES128-GCM-SHA256," -+ "ECDHE-ECDSA-AES256-GCM-SHA384," -+ "ECDHE-RSA-AES256-GCM-SHA384," -+ "ECDHE-ECDSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-AES128-SHA," -+ "ECDHE-RSA-AES256-SHA," -+ "AES128-GCM-SHA256," -+ "AES256-GCM-SHA384," -+ "AES128-SHA," -+ "AES256-SHA", -+ .npn = false, -+ .alpn = true, -+ .alps = true, -+ .tls_permute_extensions = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"", -+ "sec-ch-ua-mobile: ?0", -+ "sec-ch-ua-platform: \"Windows\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", -+ "Sec-Fetch-Site: none", -+ "Sec-Fetch-Mode: navigate", -+ "Sec-Fetch-User: ?1", -+ "Sec-Fetch-Dest: document", -+ "Accept-Encoding: gzip, deflate, br", -+ "Accept-Language: en-US,en;q=0.9" -+ }, -+ .http2_settings = "1:65536;2:0;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .target = "chrome116", ++ .httpversion = CURL_HTTP_VERSION_2_0, ++ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, ++ .ciphers = ++ "TLS_AES_128_GCM_SHA256," ++ "TLS_AES_256_GCM_SHA384," ++ "TLS_CHACHA20_POLY1305_SHA256," ++ "ECDHE-ECDSA-AES128-GCM-SHA256," ++ "ECDHE-RSA-AES128-GCM-SHA256," ++ "ECDHE-ECDSA-AES256-GCM-SHA384," ++ "ECDHE-RSA-AES256-GCM-SHA384," ++ "ECDHE-ECDSA-CHACHA20-POLY1305," ++ "ECDHE-RSA-CHACHA20-POLY1305," ++ "ECDHE-RSA-AES128-SHA," ++ "ECDHE-RSA-AES256-SHA," ++ "AES128-GCM-SHA256," ++ "AES256-GCM-SHA384," ++ "AES128-SHA," ++ "AES256-SHA", ++ .npn = false, ++ .alpn = true, ++ .alps = true, ++ .tls_permute_extensions = true, ++ .tls_session_ticket = true, ++ .cert_compression = "brotli", ++ .http_headers = { ++ "sec-ch-ua: \"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"", ++ "sec-ch-ua-mobile: ?0", ++ "sec-ch-ua-platform: \"Windows\"", ++ "Upgrade-Insecure-Requests: 1", ++ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36", ++ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", ++ "Sec-Fetch-Site: none", ++ "Sec-Fetch-Mode: navigate", ++ "Sec-Fetch-User: ?1", ++ "Sec-Fetch-Dest: document", ++ "Accept-Encoding: gzip, deflate, br", ++ "Accept-Language: en-US,en;q=0.9" ++ }, ++ .http2_settings = "1:65536;2:0;3:1000;4:6291456;6:262144", ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { -+ .target = "chrome119", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "ECDHE-ECDSA-AES128-GCM-SHA256," -+ "ECDHE-RSA-AES128-GCM-SHA256," -+ "ECDHE-ECDSA-AES256-GCM-SHA384," -+ "ECDHE-RSA-AES256-GCM-SHA384," -+ "ECDHE-ECDSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-AES128-SHA," -+ "ECDHE-RSA-AES256-SHA," -+ "AES128-GCM-SHA256," -+ "AES256-GCM-SHA384," -+ "AES128-SHA," -+ "AES256-SHA", -+ .npn = false, -+ .alpn = true, -+ .alps = true, -+ .tls_permute_extensions = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \"Google Chrome\";v=\"119\", \"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\"", -+ "sec-ch-ua-mobile: ?0", -+ "sec-ch-ua-platform: \"macOS\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", -+ "Sec-Fetch-Site: none", -+ "Sec-Fetch-Mode: navigate", -+ "Sec-Fetch-User: ?1", -+ "Sec-Fetch-Dest: document", -+ "Accept-Encoding: gzip, deflate, br", -+ "Accept-Language: en-US,en;q=0.9" -+ }, -+ .http2_settings = "1:65536;2:0;4:6291456;6:262144", -+ .http2_window_update = 15663105, -+ .ech = "GREASE" ++ .target = "chrome119", ++ .httpversion = CURL_HTTP_VERSION_2_0, ++ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, ++ .ciphers = ++ "TLS_AES_128_GCM_SHA256," ++ "TLS_AES_256_GCM_SHA384," ++ "TLS_CHACHA20_POLY1305_SHA256," ++ "ECDHE-ECDSA-AES128-GCM-SHA256," ++ "ECDHE-RSA-AES128-GCM-SHA256," ++ "ECDHE-ECDSA-AES256-GCM-SHA384," ++ "ECDHE-RSA-AES256-GCM-SHA384," ++ "ECDHE-ECDSA-CHACHA20-POLY1305," ++ "ECDHE-RSA-CHACHA20-POLY1305," ++ "ECDHE-RSA-AES128-SHA," ++ "ECDHE-RSA-AES256-SHA," ++ "AES128-GCM-SHA256," ++ "AES256-GCM-SHA384," ++ "AES128-SHA," ++ "AES256-SHA", ++ .npn = false, ++ .alpn = true, ++ .alps = true, ++ .tls_permute_extensions = true, ++ .tls_session_ticket = true, ++ .cert_compression = "brotli", ++ .http_headers = { ++ "sec-ch-ua: \"Google Chrome\";v=\"119\", \"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\"", ++ "sec-ch-ua-mobile: ?0", ++ "sec-ch-ua-platform: \"macOS\"", ++ "Upgrade-Insecure-Requests: 1", ++ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", ++ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", ++ "Sec-Fetch-Site: none", ++ "Sec-Fetch-Mode: navigate", ++ "Sec-Fetch-User: ?1", ++ "Sec-Fetch-Dest: document", ++ "Accept-Encoding: gzip, deflate, br", ++ "Accept-Language: en-US,en;q=0.9" ++ }, ++ .http2_settings = "1:65536;2:0;4:6291456;6:262144", ++ .http2_window_update = 15663105, ++ .ech = "GREASE", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { -+ .target = "chrome120", -+ .httpversion = CURL_HTTP_VERSION_2_0, -+ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, -+ .ciphers = -+ "TLS_AES_128_GCM_SHA256," -+ "TLS_AES_256_GCM_SHA384," -+ "TLS_CHACHA20_POLY1305_SHA256," -+ "ECDHE-ECDSA-AES128-GCM-SHA256," -+ "ECDHE-RSA-AES128-GCM-SHA256," -+ "ECDHE-ECDSA-AES256-GCM-SHA384," -+ "ECDHE-RSA-AES256-GCM-SHA384," -+ "ECDHE-ECDSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-CHACHA20-POLY1305," -+ "ECDHE-RSA-AES128-SHA," -+ "ECDHE-RSA-AES256-SHA," -+ "AES128-GCM-SHA256," -+ "AES256-GCM-SHA384," -+ "AES128-SHA," -+ "AES256-SHA", -+ .npn = false, -+ .alpn = true, -+ .alps = true, -+ .tls_permute_extensions = true, -+ .tls_session_ticket = true, -+ .cert_compression = "brotli", -+ .http_headers = { -+ "sec-ch-ua: \"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"", -+ "sec-ch-ua-mobile: ?0", -+ "sec-ch-ua-platform: \"macOS\"", -+ "Upgrade-Insecure-Requests: 1", -+ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", -+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", -+ "Sec-Fetch-Site: none", -+ "Sec-Fetch-Mode: navigate", -+ "Sec-Fetch-User: ?1", -+ "Sec-Fetch-Dest: document", -+ "Accept-Encoding: gzip, deflate, br", -+ "Accept-Language: en-US,en;q=0.9" -+ }, -+ .http2_settings = "1:65536;2:0;4:6291456;6:262144", -+ .http2_window_update = 15663105, -+ .ech = "GREASE" ++ .target = "chrome120", ++ .httpversion = CURL_HTTP_VERSION_2_0, ++ .ssl_version = CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT, ++ .ciphers = ++ "TLS_AES_128_GCM_SHA256," ++ "TLS_AES_256_GCM_SHA384," ++ "TLS_CHACHA20_POLY1305_SHA256," ++ "ECDHE-ECDSA-AES128-GCM-SHA256," ++ "ECDHE-RSA-AES128-GCM-SHA256," ++ "ECDHE-ECDSA-AES256-GCM-SHA384," ++ "ECDHE-RSA-AES256-GCM-SHA384," ++ "ECDHE-ECDSA-CHACHA20-POLY1305," ++ "ECDHE-RSA-CHACHA20-POLY1305," ++ "ECDHE-RSA-AES128-SHA," ++ "ECDHE-RSA-AES256-SHA," ++ "AES128-GCM-SHA256," ++ "AES256-GCM-SHA384," ++ "AES128-SHA," ++ "AES256-SHA", ++ .npn = false, ++ .alpn = true, ++ .alps = true, ++ .tls_permute_extensions = true, ++ .tls_session_ticket = true, ++ .cert_compression = "brotli", ++ .http_headers = { ++ "sec-ch-ua: \"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"", ++ "sec-ch-ua-mobile: ?0", ++ "sec-ch-ua-platform: \"macOS\"", ++ "Upgrade-Insecure-Requests: 1", ++ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", ++ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", ++ "Sec-Fetch-Site: none", ++ "Sec-Fetch-Mode: navigate", ++ "Sec-Fetch-User: ?1", ++ "Sec-Fetch-Dest: document", ++ "Accept-Encoding: gzip, deflate, br", ++ "Accept-Language: en-US,en;q=0.9" ++ }, ++ .http2_settings = "1:65536;2:0;4:6291456;6:262144", ++ .http2_window_update = 15663105, ++ .ech = "GREASE", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "chrome99_android", @@ -1837,7 +1863,9 @@ index 000000000..1b6eeb9e9 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "edge99", @@ -1879,7 +1907,9 @@ index 000000000..1b6eeb9e9 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "edge101", @@ -1921,7 +1951,9 @@ index 000000000..1b6eeb9e9 + "Accept-Language: en-US,en;q=0.9" + }, + .http2_settings = "1:65536;3:1000;4:6291456;6:262144", -+ .http2_window_update = 15663105 ++ .http2_window_update = 15663105, ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "safari15_3", @@ -1979,7 +2011,9 @@ index 000000000..1b6eeb9e9 + }, + .http2_settings = "4:4194304;3:100", + .http2_window_update = 10485760, -+ .http2_pseudo_headers_order = "mspa" ++ .http2_pseudo_headers_order = "mspa", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "safari15_5", @@ -2032,7 +2066,9 @@ index 000000000..1b6eeb9e9 + }, + .http2_settings = "4:4194304;3:100", + .http2_window_update = 10485760, -+ .http2_pseudo_headers_order = "mspa" ++ .http2_pseudo_headers_order = "mspa", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "safari17_2_ios", @@ -2088,7 +2124,9 @@ index 000000000..1b6eeb9e9 + }, + .http2_settings = "2:0;4:2097152;3:100", + .http2_window_update = 10485760, -+ .http2_pseudo_headers_order = "mspa" ++ .http2_pseudo_headers_order = "mspa", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "safari17_0", @@ -2144,7 +2182,9 @@ index 000000000..1b6eeb9e9 + }, + .http2_settings = "2:0;4:4194304;3:100", + .http2_window_update = 10485760, -+ .http2_pseudo_headers_order = "mspa" ++ .http2_pseudo_headers_order = "mspa", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + .target = "firefox120", @@ -2183,7 +2223,9 @@ index 000000000..1b6eeb9e9 + .http2_settings = "1:65536;4:131072;5:16384", + .http2_window_update = 12517377, + .http2_pseudo_headers_order = "mpas", -+ .http2_streams = "3:0:0:201,5:0:0:101,7:0:0:1,9:0:7:1,11:0:3:1,13:0:0:241" ++ .http2_streams = "3:0:0:201,5:0:0:101,7:0:0:1,9:0:7:1,11:0:3:1,13:0:0:241", ++ .tls_extension_order = NULL, ++ .tls_grease = true + }, + { + /* Last one must be NULL. */ @@ -2192,10 +2234,10 @@ index 000000000..1b6eeb9e9 +}; diff --git a/lib/impersonate.h b/lib/impersonate.h new file mode 100644 -index 000000000..e0e92fc3c +index 000000000..a9e1e4b45 --- /dev/null +++ b/lib/impersonate.h -@@ -0,0 +1,48 @@ +@@ -0,0 +1,50 @@ +#ifndef HEADER_CURL_IMPERSONATE_H +#define HEADER_CURL_IMPERSONATE_H + @@ -2233,6 +2275,8 @@ index 000000000..e0e92fc3c + const char *http2_streams; + bool tls_permute_extensions; + const char *ech; ++ const char *tls_extension_order; ++ bool tls_grease; + /* Other TLS options will come here in the future once they are + * configurable through curl_easy_setopt() */ +}; @@ -2558,7 +2602,7 @@ index ff661482e..dc95784b0 100644 struct Names { diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c -index 8c8f43e83..1c5566738 100644 +index 8c8f43e83..e6c749315 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -79,9 +79,24 @@ @@ -2868,7 +2912,7 @@ index 8c8f43e83..1c5566738 100644 #endif #ifdef SSL_OP_NO_COMPRESSION -@@ -3638,6 +3912,16 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, +@@ -3638,6 +3912,18 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif @@ -2877,15 +2921,17 @@ index 8c8f43e83..1c5566738 100644 + SSL_CTX_set_mode(backend->ctx, + SSL_MODE_CBC_RECORD_SPLITTING | SSL_MODE_ENABLE_FALSE_START); + -+ /* curl-impersonate: Enable TLS extensions 5 - status_request and -+ * 18 - signed_certificate_timestamp. */ ++ /* curl-impersonate: Enable TLS extensions 18 - signed_certificate_timestamp. */ ++ // XXX: Firefox does not enable this + SSL_CTX_enable_signed_cert_timestamps(backend->ctx); ++ ++ /* curl-impersonate: Enable TLS extensions 5 - status_request */ + SSL_CTX_enable_ocsp_stapling(backend->ctx); + if(ssl_cert || ssl_cert_blob || ssl_cert_type) { if(!result && !cert_stuff(data, backend->ctx, -@@ -3691,6 +3975,35 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, +@@ -3691,6 +3977,35 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif @@ -2921,7 +2967,7 @@ index 8c8f43e83..1c5566738 100644 #ifdef USE_OPENSSL_SRP if(ssl_config->primary.username && Curl_auth_allowed_to_host(data)) { char * const ssl_username = ssl_config->primary.username; -@@ -3716,6 +4029,30 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, +@@ -3716,6 +4031,37 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif @@ -2931,8 +2977,10 @@ index 8c8f43e83..1c5566738 100644 + * and SSLClientSocketImpl::Init() + * in the Chromium's source code. */ + -+ /* Enable TLS GREASE. */ -+ SSL_CTX_set_grease_enabled(backend->ctx, 1); ++ /* curl-impersonate: Enable TLS GREASE. */ ++ if(data->set.tls_grease) { ++ SSL_CTX_set_grease_enabled(backend->ctx, 1); ++ } + + /* + * curl-impersonate: Enable TLS extension permutation, enabled by default @@ -2942,6 +2990,11 @@ index 8c8f43e83..1c5566738 100644 + SSL_CTX_set_permute_extensions(backend->ctx, 1); + } + ++ /* curl-impersonate: Set TLS extensions order. */ ++ if(data->set.tls_extension_order) { ++ SSL_CTX_set_grease_enabled(backend->ctx, data->); ++ } ++ + if(conn_config->cert_compression && + add_cert_compression(data, + backend->ctx, @@ -2952,7 +3005,7 @@ index 8c8f43e83..1c5566738 100644 /* OpenSSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with -@@ -3771,6 +4108,23 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, +@@ -3771,6 +4117,24 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, SSL_set_app_data(backend->handle, cf); @@ -2963,6 +3016,7 @@ index 8c8f43e83..1c5566738 100644 + + for(i = 0; i < connssl->alps->count; ++i) { + /* curl-impersonate: Add the ALPS extension (17513) like Chrome does. */ ++ // XXX: Firefox does not enable this. + SSL_add_application_settings(backend->handle, connssl->alps->entries[i], + strlen(connssl->alps->entries[i]), NULL, + 0); @@ -2976,7 +3030,7 @@ index 8c8f43e83..1c5566738 100644 #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) if(conn_config->verifystatus) -@@ -3794,6 +4148,21 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, +@@ -3794,6 +4158,21 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif @@ -2998,7 +3052,7 @@ index 8c8f43e83..1c5566738 100644 SSL_set_app_data(backend->handle, cf); connssl->reused_session = FALSE; -@@ -4005,6 +4374,60 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, +@@ -4005,6 +4384,60 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, negotiated_group_name? negotiated_group_name : "[blank]", OBJ_nid2sn(psigtype_nid)); @@ -3347,10 +3401,25 @@ index dced53e0f..dee8a2fc3 100644 # if unit tests are enabled, build a static library to link them with diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c -index 906e23e14..3a492996b 100644 +index 906e23e14..7d42e487b 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c -@@ -175,6 +175,14 @@ static void free_config_fields(struct OperationConfig *config) +@@ -95,6 +95,14 @@ static void free_config_fields(struct OperationConfig *config) + Curl_safefree(config->proto_str); + Curl_safefree(config->proto_redir_str); + ++ // Impersonate ++ Curl_safefree(config->ssl_sig_hash_algs); ++ Curl_safefree(config->ssl_cert_compression); ++ Curl_safefree(config->http2_pseudo_headers_order); ++ Curl_safefree(config->http2_settings); ++ Curl_safefree(config->http2_streams); ++ // End Impersonate ++ + urlnode = config->url_list; + while(urlnode) { + struct getout *next = urlnode->next; +@@ -175,6 +183,14 @@ static void free_config_fields(struct OperationConfig *config) Curl_safefree(config->aws_sigv4); Curl_safefree(config->proto_str); Curl_safefree(config->proto_redir_str);