From cea1a3c34969b5b888f3b7723673db1c8221329f Mon Sep 17 00:00:00 2001 From: Petr Patek Date: Mon, 23 Mar 2026 11:43:37 +0100 Subject: [PATCH 1/2] feat: implement TLS fingerprinting support Added support for TLS fingerprinting in the client configuration, allowing for fine-grained control over TLS parameters to match specific browser fingerprints. This includes new structures for TlsFingerprint and associated configurations, as well as modifications to the ClientConfig and CryptoProvider to utilize these fingerprints. Removed the BrowserEmulator struct and related logic, streamlining the client configuration for improved performance and clarity. --- rustls/src/client/client_emulator.rs | 21 - rustls/src/client/config.rs | 120 ++++- rustls/src/client/hs.rs | 137 ++++-- rustls/src/client/mod.rs | 5 +- rustls/src/client/tls13.rs | 39 ++ rustls/src/crypto/emulation/mod.rs | 662 +++++++++++++++++++++------ rustls/src/crypto/mod.rs | 75 ++- rustls/src/lib.rs | 7 + rustls/src/msgs/enums.rs | 1 + rustls/src/msgs/handshake.rs | 13 + rustls/src/verify.rs | 63 ++- 11 files changed, 835 insertions(+), 308 deletions(-) delete mode 100644 rustls/src/client/client_emulator.rs diff --git a/rustls/src/client/client_emulator.rs b/rustls/src/client/client_emulator.rs deleted file mode 100644 index f588588271b..00000000000 --- a/rustls/src/client/client_emulator.rs +++ /dev/null @@ -1,21 +0,0 @@ -#[derive(Clone, Debug)] -#[allow(missing_docs, clippy::exhaustive_structs)] -pub struct BrowserEmulator { - pub browser_type: BrowserType, - pub version: BrowserVersion, -} - -#[derive(Clone, Debug)] -#[allow(missing_docs, clippy::exhaustive_structs)] -pub struct BrowserVersion { - pub major: u8, - pub minor: u8, - pub patch: u8, -} - -#[derive(Clone, Debug)] -#[allow(clippy::exhaustive_enums, missing_docs)] -pub enum BrowserType { - Chrome, - Firefox, -} diff --git a/rustls/src/client/config.rs b/rustls/src/client/config.rs index d20632b98c6..753909540d0 100644 --- a/rustls/src/client/config.rs +++ b/rustls/src/client/config.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "impit")] +use alloc::vec; use alloc::vec::Vec; use core::fmt; use core::marker::PhantomData; @@ -10,7 +12,7 @@ use super::handy::ClientSessionMemoryCache; use super::handy::{FailResolveClientCert, NoClientSessionStorage}; use crate::builder::{ConfigBuilder, WantsVerifier}; #[cfg(feature = "impit")] -use crate::client::client_emulator::BrowserEmulator; +use crate::crypto::emulation::TlsFingerprint; #[cfg(doc)] use crate::crypto; use crate::crypto::kx::NamedGroup; @@ -60,13 +62,12 @@ use crate::{DistinguishedName, KeyLog, compress, verify}; /// [`RootCertStore`]: crate::RootCertStore #[derive(Clone, Debug)] pub struct ClientConfig { - /// Whether this client is using browser-emulated settings. - /// This is used by the retch_rust project to emulate browsers' JA4 fingerprints. + /// TLS fingerprint configuration for browser emulation. + /// This allows fine-grained control over TLS parameters to match specific browser fingerprints. /// - /// Note that this can be only set by the builder's `with_browser_emulation` method. - /// Setting this field directly won't work correctly and might cause inconsistencies in your JA4 fingerprints. + /// Note that this should be set via the builder's `with_tls_fingerprint` method. #[cfg(feature = "impit")] - pub browser_emulation: Option, + pub tls_fingerprint: Option, /// Which ALPN protocols we include in our client hello. /// If empty, no ALPN extension is sent. @@ -557,6 +558,24 @@ pub struct WantsClientCert { } impl ConfigBuilder { + /// Enable TLS fingerprinting with a custom fingerprint. + #[cfg(feature = "impit")] + pub fn with_tls_fingerprint( + self, + fingerprint: TlsFingerprint, + ) -> ConfigBuilder { + ConfigBuilder { + state: WantsClientCertWithTlsFingerprint { + verifier: self.state.verifier, + client_ech_mode: self.state.client_ech_mode, + tls_fingerprint: fingerprint, + }, + provider: self.provider, + time_provider: self.time_provider, + side: PhantomData, + } + } + /// Sets a single certificate chain and matching private key for use /// in client authentication. /// @@ -607,7 +626,7 @@ impl ConfigBuilder { alpn_protocols: Vec::new(), resumption: Resumption::default(), #[cfg(feature = "impit")] - browser_emulation: None, + tls_fingerprint: None, max_fragment_size: None, client_auth_cert_resolver, enable_sni: true, @@ -625,6 +644,93 @@ impl ConfigBuilder { } } +/// A config builder state where the caller needs to supply whether and how to provide a client +/// certificate, with TLS fingerprint enabled. +/// +/// For more information, see the [`ConfigBuilder`] documentation. +#[cfg(feature = "impit")] +#[derive(Clone)] +pub struct WantsClientCertWithTlsFingerprint { + verifier: Arc, + client_ech_mode: Option, + tls_fingerprint: TlsFingerprint, +} + +#[cfg(feature = "impit")] +impl ConfigBuilder { + /// Sets a single certificate chain and matching private key for use + /// in client authentication. + pub fn with_client_auth_cert( + self, + identity: Arc>, + key_der: PrivateKeyDer<'static>, + ) -> Result { + let credentials = Credentials::from_der(identity, key_der, &self.provider)?; + self.with_client_credential_resolver(Arc::new(SingleCredential::from(credentials))) + } + + /// Do not support client auth. + pub fn with_no_client_auth(self) -> Result { + self.with_client_credential_resolver(Arc::new(FailResolveClientCert {})) + } + + /// Sets a custom [`ClientCredentialResolver`]. + pub fn with_client_credential_resolver( + self, + client_auth_cert_resolver: Arc, + ) -> Result { + use crate::crypto::emulation::FingerprintCertCompressionAlgorithm; + + self.provider.consistency_check()?; + + // Determine cert compression based on fingerprint + let (cert_compressors, cert_decompressors) = + if let Some(ref compression) = self.state.tls_fingerprint.cert_compression { + let compressors: Vec<_> = compression + .iter() + .filter_map(|alg| match alg { + FingerprintCertCompressionAlgorithm::Brotli => { + Some(compress::BROTLI_COMPRESSOR) + } + _ => None, // Only Brotli is supported for now + }) + .collect(); + let decompressors: Vec<_> = compression + .iter() + .filter_map(|alg| match alg { + FingerprintCertCompressionAlgorithm::Brotli => { + Some(compress::BROTLI_DECOMPRESSOR) + } + _ => None, + }) + .collect(); + (compressors, decompressors) + } else { + (vec![], vec![]) + }; + + Ok(ClientConfig { + tls_fingerprint: Some(self.state.tls_fingerprint), + provider: self.provider, + alpn_protocols: Vec::new(), + resumption: Resumption::default(), + max_fragment_size: None, + client_auth_cert_resolver, + enable_sni: true, + verifier: self.state.verifier, + key_log: Arc::new(crate::KeyLogFile::new()), + enable_secret_extraction: false, + enable_early_data: false, + require_ems: cfg!(feature = "fips"), + time_provider: self.time_provider, + cert_compressors, + cert_compression_cache: Arc::new(compress::CompressionCache::default()), + cert_decompressors, + ech_mode: self.state.client_ech_mode, + }) + } +} + /// Container for unsafe APIs pub(super) mod danger { use core::marker::PhantomData; diff --git a/rustls/src/client/hs.rs b/rustls/src/client/hs.rs index a6b28bca158..489b13efc0c 100644 --- a/rustls/src/client/hs.rs +++ b/rustls/src/client/hs.rs @@ -13,8 +13,6 @@ use super::connection::ClientConnectionData; use super::ech::{EchMode, EchState, EchStatus}; use super::{ClientHelloDetails, tls13}; use crate::check::inappropriate_handshake_message; -#[cfg(feature = "impit")] -use crate::client::client_emulator::{BrowserEmulator, BrowserType}; use crate::common_state::{CommonState, HandshakeKind, KxState, State}; use crate::crypto::cipher::Payload; use crate::crypto::kx::{KeyExchangeAlgorithm, NamedGroup, StartedKeyExchange}; @@ -583,14 +581,16 @@ fn emit_client_hello_for_retry( .collect(); #[cfg(feature = "impit")] - if let Some(BrowserEmulator { - browser_type: BrowserType::Chrome, - version: _, - }) = config.browser_emulation - { - offered_groups.push(NamedGroup::GREASE); - // offered_groups.push(NamedGroup::X25519Kyber768Draft00); - } + let offered_groups: Vec = if let Some(ref fingerprint) = config.tls_fingerprint { + // Use key exchange groups from TLS fingerprint + fingerprint + .key_exchange_groups + .iter() + .map(|g| g.to_named_group()) + .collect() + } else { + offered_groups + }; let mut exts = Box::new(ClientExtensions { supported_versions: Some(supported_versions), @@ -602,43 +602,52 @@ fn emit_client_hello_for_retry( .supported_verify_schemes(), ), extended_master_secret_request: Some(()), - certificate_status_request: match config.verifier.request_ocsp_response() { - true => Some(CertificateStatusRequest::build_ocsp()), - false => None, - }, + certificate_status_request: Some(CertificateStatusRequest::build_ocsp()), protocols: extra_exts.protocols.clone(), ..Default::default() }); #[cfg(feature = "impit")] - match config.browser_emulation { - Some(BrowserEmulator { - browser_type: BrowserType::Chrome, - version: _, - }) => { + if let Some(ref fingerprint) = config.tls_fingerprint { + // Apply TLS fingerprint extensions configuration + let ext_config = &fingerprint.extensions; + + if ext_config.grease { + exts.reserved_grease = Some(()); + } + + if ext_config.signed_certificate_timestamp { + exts.signed_certificate_timestamp = Some(()); + } + + if ext_config.application_settings { // hack - to avoid `Unexpected Message` when communicating with BoringSSL-based servers, // we cannot send an actual ALPN protocol name list let application_settings: PayloadU16 = PayloadU16::new(vec![0x05, 0x69, 0x6d, 0x70, 0x69, 0x74]); - - exts.reserved_grease = Some(()); - exts.signed_certificate_timestamp = Some(()); - exts.application_settings = Some(application_settings); - exts.renegotiation_info = Some(PayloadU8::empty()); + if ext_config.use_new_alps_codepoint { + // Use new ALPS codepoint (17613 / 0x44cd) for Chrome 136+ + exts.application_settings_new = Some(application_settings); + } else { + // Use old ALPS codepoint (17513 / 0x4469) + exts.application_settings = Some(application_settings); + } } - Some(BrowserEmulator { - browser_type: BrowserType::Firefox, - version: _, - }) => { + + if ext_config.delegated_credentials { // TODO: We don't really support the delegated credentials extension yet, just sending it in the client hello message let delegated_credentials_signature_algos = PayloadU16::new(vec![0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02, 0x03]); - exts.delegated_credentials = Some(delegated_credentials_signature_algos); + } + + if ext_config.renegotiation_info { exts.renegotiation_info = Some(PayloadU8::empty()); - exts.record_size_limit = Some(16385); } - _ => {} + + if let Some(record_size_limit) = ext_config.record_size_limit { + exts.record_size_limit = Some(record_size_limit); + } } if let Some(TransportParameters::Quic(v)) = &extra_exts.transport_parameters { @@ -766,6 +775,25 @@ fn emit_client_hello_for_retry( // but they also need to keep the same order as the previous ClientHello exts.order_seed = input.hello.extension_order_seed; + #[cfg(feature = "impit")] + if let Some(ref fingerprint) = config.tls_fingerprint { + if !fingerprint + .extensions + .extension_order + .is_empty() + { + exts.contiguous_extensions = fingerprint + .extensions + .extension_order + .clone(); + } + + if !fingerprint.extensions.supported_versions { + exts.supported_versions = None; + } + } + + #[cfg(not(feature = "impit"))] let mut cipher_suites: Vec<_> = config .provider .iter_cipher_suites() @@ -775,31 +803,42 @@ fn emit_client_hello_for_retry( }) .collect(); + #[cfg(feature = "impit")] + let mut cipher_suites: Vec<_> = if let Some(ref fingerprint) = config.tls_fingerprint { + // Use cipher suites from TLS fingerprint with correct codes for advertising + fingerprint + .cipher_suites + .iter() + .map(|cs| cs.to_cipher_suite()) + .collect() + } else { + config + .provider + .iter_cipher_suites() + .filter_map(|cs| match cs.usable_for_protocol(cx.common.protocol) { + true => Some(cs.suite()), + false => None, + }) + .collect() + }; + #[cfg(not(feature = "impit"))] // We don't do renegotiation at all, in fact. if supported_versions.tls12 { - // We don't do renegotiation at all, in fact. cipher_suites.push(CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV); } #[cfg(feature = "impit")] - match config.browser_emulation { - // Chrome doesn't send this cipher suite. - Some(BrowserEmulator { - browser_type: BrowserType::Chrome, - version: _, - }) => {} - // Firefox also doesn't seem to send this cipher suite? - Some(BrowserEmulator { - browser_type: BrowserType::Firefox, - version: _, - }) => {} - _ => { - // We don't do renegotiation at all, in fact. - if supported_versions.tls12 { - // We don't do renegotiation at all, in fact. - cipher_suites.push(CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV); - } + // Skip adding TLS_EMPTY_RENEGOTIATION_INFO_SCSV when using tls_fingerprint + if config.tls_fingerprint.is_none() && supported_versions.tls12 { + cipher_suites.push(CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + } + + // Add padding extension if configured (RFC7685) + #[cfg(feature = "impit")] + if let Some(ref fingerprint) = config.tls_fingerprint { + if fingerprint.extensions.padding { + exts.padding = Some(Payload::Borrowed(&[])); } } diff --git a/rustls/src/client/mod.rs b/rustls/src/client/mod.rs index a796eb9dc63..49f51ccf185 100644 --- a/rustls/src/client/mod.rs +++ b/rustls/src/client/mod.rs @@ -18,6 +18,8 @@ pub use config::{ ClientConfig, ClientCredentialResolver, ClientSessionStore, CredentialRequest, Resumption, Tls12Resumption, WantsClientCert, }; +#[cfg(feature = "impit")] +pub use config::WantsClientCertWithTlsFingerprint; mod connection; #[cfg(feature = "std")] @@ -42,9 +44,6 @@ pub(crate) use tls12::TLS12_HANDLER; mod tls13; pub(crate) use tls13::TLS13_HANDLER; -#[allow(missing_docs)] -pub mod client_emulator; - /// Dangerous configuration that should be audited and used with extreme care. pub mod danger { pub use super::config::danger::{DangerousClientConfig, DangerousClientConfigBuilder}; diff --git a/rustls/src/client/tls13.rs b/rustls/src/client/tls13.rs index 085420a5b9d..a0580f27b9b 100644 --- a/rustls/src/client/tls13.rs +++ b/rustls/src/client/tls13.rs @@ -329,6 +329,45 @@ pub(super) fn initial_key_share( server_name: &ServerName<'_>, kx_state: &mut KxState, ) -> Result { + // When fingerprinting is enabled, use the first non-GREASE group from the fingerprint + #[cfg(feature = "impit")] + let group = if let Some(ref fingerprint) = config.tls_fingerprint { + use crate::crypto::emulation::FingerprintKeyExchangeGroup; + + // Find the first non-GREASE group from the fingerprint + let first_real_group = fingerprint + .key_exchange_groups + .iter() + .find(|g| !matches!(g, FingerprintKeyExchangeGroup::Grease)) + .map(|g| g.to_named_group()) + .expect("No non-GREASE key exchange groups in fingerprint"); + + config + .provider + .find_kx_group(first_real_group, ProtocolVersion::TLSv1_3) + .expect("Fingerprint key exchange group not supported by provider") + } else { + config + .resumption + .store + .kx_hint(server_name) + .and_then(|group_name| { + config + .provider + .find_kx_group(group_name, ProtocolVersion::TLSv1_3) + }) + .unwrap_or_else(|| { + config + .provider + .kx_groups + .iter() + .copied() + .next() + .expect("No kx groups configured") + }) + }; + + #[cfg(not(feature = "impit"))] let group = config .resumption .store diff --git a/rustls/src/crypto/emulation/mod.rs b/rustls/src/crypto/emulation/mod.rs index 2ba07319fbf..6ff635bdb4b 100644 --- a/rustls/src/crypto/emulation/mod.rs +++ b/rustls/src/crypto/emulation/mod.rs @@ -1,63 +1,307 @@ +#![allow(missing_docs, non_camel_case_types)] #![cfg(feature = "impit")] -use webpki::aws_lc_rs as webpki_algs_aws; +use alloc::vec::Vec; + +use crate::crypto::kx::NamedGroup; +use crate::crypto::{SignatureScheme, SupportedCipherSuite}; +use crate::msgs::enums::ExtensionType; use super::{WebPkiSupportedAlgorithms, aws_lc_rs}; -use crate::crypto::SignatureScheme; -use crate::{Tls12CipherSuite, Tls13CipherSuite}; - -/// The cipher suites supported by Google Chrome. -/// Note that some of these are not real cipher suites and their implementation doesn't match the specification. -pub static CHROME_TLS13_CIPHER_SUITES: [&Tls13CipherSuite; 10] = [ - aws_lc_rs::cipher_suite::TLS13_RESERVED_GREASE, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS13_AES_128_GCM_SHA256, - aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384, - aws_lc_rs::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, - aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_128_GCM_SHA256, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_256_GCM_SHA384, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_128_CBC_SHA, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_256_CBC_SHA, // fake cipher suite from the patch -]; - -/// The TLS 1.2 cipher suites supported by Google Chrome. -pub static CHROME_TLS12_CIPHER_SUITES: [&Tls12CipherSuite; 6] = [ - aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, -]; - -/// The cipher suites supported by Firefox. -/// Note that some of these are not real cipher suites and their implementation doesn't match the specification. -pub static FIREFOX_TLS13_CIPHER_SUITES: [&Tls13CipherSuite; 11] = [ - aws_lc_rs::cipher_suite::TLS13_AES_128_GCM_SHA256, - aws_lc_rs::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, - aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384, - aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_128_GCM_SHA256, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_256_GCM_SHA384, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_128_CBC_SHA, // fake cipher suite from the patch - aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_256_CBC_SHA, // fake cipher suite from the patch -]; - -/// The TLS 1.2 cipher suites supported by Firefox. -pub static FIREFOX_TLS12_CIPHER_SUITES: [&Tls12CipherSuite; 6] = [ - aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, -]; - -/// The signature verification algorithms supported by Google Chrome. -pub static CHROME_SIGNATURE_VERIFICATION_ALGOS: WebPkiSupportedAlgorithms = +use webpki::aws_lc_rs as webpki_algs_aws; + +/// TLS fingerprint configuration for browser emulation. +/// +/// This struct allows fine-grained control over TLS parameters +/// to match specific browser fingerprints. +#[derive(Clone, Debug)] +pub struct TlsFingerprint { + /// Cipher suites in preference order + pub cipher_suites: Vec, + /// Key exchange groups in preference order + pub key_exchange_groups: Vec, + /// Signature algorithms in preference order + pub signature_algorithms: Vec, + /// TLS extensions configuration + pub extensions: TlsExtensionsConfig, + /// ALPN protocols in preference order + pub alpn_protocols: Vec>, + /// Certificate compression algorithms + pub cert_compression: Option>, +} + +impl TlsFingerprint { + /// Creates a new TLS fingerprint with the given configuration. + pub fn new( + cipher_suites: Vec, + key_exchange_groups: Vec, + signature_algorithms: Vec, + extensions: TlsExtensionsConfig, + alpn_protocols: Vec>, + cert_compression: Option>, + ) -> Self { + Self { + cipher_suites, + key_exchange_groups, + signature_algorithms, + extensions, + alpn_protocols, + cert_compression, + } + } +} + +/// TLS cipher suites for fingerprinting. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FingerprintCipherSuite { + // TLS 1.3 cipher suites + TLS13_AES_128_GCM_SHA256, + TLS13_AES_256_GCM_SHA384, + TLS13_CHACHA20_POLY1305_SHA256, + // TLS 1.2 cipher suites + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + /// GREASE cipher suite + Grease, +} + +impl FingerprintCipherSuite { + /// Returns the CipherSuite code to advertise in the ClientHello. + /// This returns the actual cipher suite code, even for cipher suites + /// that are not implemented (like 3DES). + pub fn to_cipher_suite(&self) -> crate::crypto::CipherSuite { + use crate::crypto::CipherSuite; + match self { + Self::TLS13_AES_128_GCM_SHA256 => CipherSuite::TLS13_AES_128_GCM_SHA256, + Self::TLS13_AES_256_GCM_SHA384 => CipherSuite::TLS13_AES_256_GCM_SHA384, + Self::TLS13_CHACHA20_POLY1305_SHA256 => CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + Self::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 => { + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + } + Self::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 => { + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + } + Self::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 => { + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + } + Self::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 => { + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + } + Self::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 => { + CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + } + Self::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => { + CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + } + Self::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA => { + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + } + Self::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA => { + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + } + Self::TLS_RSA_WITH_AES_128_GCM_SHA256 => CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, + Self::TLS_RSA_WITH_AES_256_GCM_SHA384 => CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, + Self::TLS_RSA_WITH_AES_128_CBC_SHA => CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, + Self::TLS_RSA_WITH_AES_256_CBC_SHA => CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, + Self::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA => { + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + } + Self::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA => { + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + } + Self::Grease => CipherSuite::TLS_RESERVED_GREASE, + } + } + + /// Converts the fingerprint cipher suite to rustls's SupportedCipherSuite. + pub fn to_supported_cipher_suite(&self) -> SupportedCipherSuite { + match self { + Self::TLS13_AES_128_GCM_SHA256 => SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_AES_128_GCM_SHA256), + Self::TLS13_AES_256_GCM_SHA384 => SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384), + Self::TLS13_CHACHA20_POLY1305_SHA256 => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256) + } + Self::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 => { + SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) + } + Self::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 => { + SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) + } + Self::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 => { + SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) + } + Self::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 => { + SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) + } + Self::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 => { + SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) + } + Self::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => { + SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) + } + // These CBC/RSA cipher suites are fake TLS 1.3 cipher suites from the impit patch + Self::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) + } + Self::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) + } + Self::TLS_RSA_WITH_AES_128_GCM_SHA256 => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_128_GCM_SHA256) + } + Self::TLS_RSA_WITH_AES_256_GCM_SHA384 => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_256_GCM_SHA384) + } + Self::TLS_RSA_WITH_AES_128_CBC_SHA => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_128_CBC_SHA) + } + Self::TLS_RSA_WITH_AES_256_CBC_SHA => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_256_CBC_SHA) + } + Self::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) + } + Self::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) + } + Self::Grease => SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_RESERVED_GREASE), + } + } +} + +/// Key exchange groups for fingerprinting. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FingerprintKeyExchangeGroup { + X25519, + /// X25519 with MLKEM768 (post-quantum hybrid) + X25519MLKEM768, + Secp256r1, + Secp384r1, + Secp521r1, + Ffdhe2048, + Ffdhe3072, + Ffdhe4096, + Ffdhe6144, + Ffdhe8192, + /// GREASE key exchange group + Grease, +} + +impl FingerprintKeyExchangeGroup { + /// Converts the fingerprint key exchange group to rustls's NamedGroup. + pub fn to_named_group(&self) -> NamedGroup { + match self { + Self::X25519 => NamedGroup::X25519, + Self::X25519MLKEM768 => NamedGroup::X25519MLKEM768, + Self::Secp256r1 => NamedGroup::secp256r1, + Self::Secp384r1 => NamedGroup::secp384r1, + Self::Secp521r1 => NamedGroup::secp521r1, + Self::Ffdhe2048 => NamedGroup::FFDHE2048, + Self::Ffdhe3072 => NamedGroup::FFDHE3072, + Self::Ffdhe4096 => NamedGroup::FFDHE4096, + Self::Ffdhe6144 => NamedGroup::FFDHE6144, + Self::Ffdhe8192 => NamedGroup::FFDHE8192, + Self::Grease => NamedGroup::GREASE, + } + } +} + +/// Signature algorithms for fingerprinting. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum FingerprintSignatureAlgorithm { + // ECDSA algorithms + EcdsaSecp256r1Sha256, + EcdsaSecp384r1Sha384, + EcdsaSecp521r1Sha512, + // RSA PSS algorithms + RsaPssRsaSha256, + RsaPssRsaSha384, + RsaPssRsaSha512, + // RSA PKCS#1 algorithms + RsaPkcs1Sha256, + RsaPkcs1Sha384, + RsaPkcs1Sha512, + RsaPkcs1Sha1, + // EdDSA algorithms + Ed25519, + Ed448, + // Legacy + EcdsaSha1Legacy, +} + +impl FingerprintSignatureAlgorithm { + /// Converts the fingerprint signature algorithm to rustls's SignatureScheme. + pub fn to_signature_scheme(&self) -> SignatureScheme { + match self { + Self::EcdsaSecp256r1Sha256 => SignatureScheme::ECDSA_NISTP256_SHA256, + Self::EcdsaSecp384r1Sha384 => SignatureScheme::ECDSA_NISTP384_SHA384, + Self::EcdsaSecp521r1Sha512 => SignatureScheme::ECDSA_NISTP521_SHA512, + Self::RsaPssRsaSha256 => SignatureScheme::RSA_PSS_SHA256, + Self::RsaPssRsaSha384 => SignatureScheme::RSA_PSS_SHA384, + Self::RsaPssRsaSha512 => SignatureScheme::RSA_PSS_SHA512, + Self::RsaPkcs1Sha256 => SignatureScheme::RSA_PKCS1_SHA256, + Self::RsaPkcs1Sha384 => SignatureScheme::RSA_PKCS1_SHA384, + Self::RsaPkcs1Sha512 => SignatureScheme::RSA_PKCS1_SHA512, + Self::RsaPkcs1Sha1 => SignatureScheme::RSA_PKCS1_SHA1, + Self::Ed25519 => SignatureScheme::ED25519, + Self::Ed448 => SignatureScheme::ED448, + Self::EcdsaSha1Legacy => SignatureScheme::ECDSA_SHA1_Legacy, + } + } +} + +/// Certificate compression algorithms for fingerprinting. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FingerprintCertCompressionAlgorithm { + Zlib, + Brotli, + Zstd, +} + +/// TLS extensions configuration for fingerprinting. +#[derive(Clone, Debug, Default)] +pub struct TlsExtensionsConfig { + /// Whether to send GREASE extensions + pub grease: bool, + /// Whether to send signed_certificate_timestamp extension + pub signed_certificate_timestamp: bool, + /// Whether to send application_settings extension + pub application_settings: bool, + /// Whether to use new ALPS codepoint (17613) instead of old (17513) + /// Chrome 136+ uses the new codepoint + pub use_new_alps_codepoint: bool, + /// Whether to send delegated_credentials extension + pub delegated_credentials: bool, + /// Whether to send record_size_limit extension + pub record_size_limit: Option, + /// Whether to send renegotiation_info extension + pub renegotiation_info: bool, + /// Whether to send padding extension (RFC7685) + pub padding: bool, + /// Whether to send supported_versions extension. + /// Defaults to true. Set to false for TLS 1.2-only fingerprints (e.g. + /// OkHttp 3) where the real client never advertises TLS 1.3 support. + pub supported_versions: bool, + /// Explicit extension order for fingerprinting. + /// When non-empty, all listed extensions are emitted in this exact order + /// via contiguous_extensions, bypassing randomization. + pub extension_order: Vec, +} + +/// Default signature verification algorithms. +/// Based on common browser implementations. +pub static DEFAULT_SIGNATURE_VERIFICATION_ALGOS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { all: &[ webpki_algs_aws::ECDSA_P256_SHA256, @@ -105,106 +349,224 @@ pub static CHROME_SIGNATURE_VERIFICATION_ALGOS: WebPkiSupportedAlgorithms = ], }; -/// The signature schemes supported by Google Chrome. -pub static CHROME_SIGNATURE_SCHEMES: &[SignatureScheme; 8] = &[ - SignatureScheme::ECDSA_NISTP256_SHA256, - SignatureScheme::RSA_PSS_SHA256, - SignatureScheme::RSA_PKCS1_SHA256, - SignatureScheme::ECDSA_NISTP384_SHA384, - SignatureScheme::RSA_PSS_SHA384, - SignatureScheme::RSA_PKCS1_SHA384, - SignatureScheme::RSA_PSS_SHA512, - SignatureScheme::RSA_PKCS1_SHA512, -]; - -/// The signature verification algorithms supported by Firefox. -pub static FIREFOX_SIGNATURE_VERIFICATION_ALGOS: WebPkiSupportedAlgorithms = - WebPkiSupportedAlgorithms { - all: &[ +impl FingerprintSignatureAlgorithm { + /// Returns the webpki signature verification algorithms for this fingerprint algorithm. + /// Returns an empty slice for algorithms that are not supported for verification (e.g., Ed448). + fn to_webpki_algs(&self) -> &'static [&'static dyn pki_types::SignatureVerificationAlgorithm] { + // Static arrays for algorithms used in the 'all' list + static ECDSA_P256_ALGS: &[&dyn pki_types::SignatureVerificationAlgorithm] = &[ webpki_algs_aws::ECDSA_P256_SHA256, webpki_algs_aws::ECDSA_P256_SHA384, + ]; + static ECDSA_P384_ALGS: &[&dyn pki_types::SignatureVerificationAlgorithm] = &[ webpki_algs_aws::ECDSA_P384_SHA256, webpki_algs_aws::ECDSA_P384_SHA384, - webpki_algs_aws::ECDSA_P384_SHA384, + ]; + static ECDSA_P521_ALGS: &[&dyn pki_types::SignatureVerificationAlgorithm] = &[ webpki_algs_aws::ECDSA_P521_SHA256, webpki_algs_aws::ECDSA_P521_SHA384, webpki_algs_aws::ECDSA_P521_SHA512, - webpki_algs_aws::ED25519, - webpki_algs_aws::RSA_PSS_2048_8192_SHA256_LEGACY_KEY, - webpki_algs_aws::RSA_PSS_2048_8192_SHA384_LEGACY_KEY, - webpki_algs_aws::RSA_PSS_2048_8192_SHA512_LEGACY_KEY, - webpki_algs_aws::RSA_PKCS1_2048_8192_SHA256, + ]; + static RSA_PSS_256_ALGS: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PSS_2048_8192_SHA256_LEGACY_KEY]; + static RSA_PSS_384_ALGS: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PSS_2048_8192_SHA384_LEGACY_KEY]; + static RSA_PSS_512_ALGS: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PSS_2048_8192_SHA512_LEGACY_KEY]; + static RSA_PKCS1_256_ALGS: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PKCS1_2048_8192_SHA256]; + static RSA_PKCS1_384_ALGS: &[&dyn pki_types::SignatureVerificationAlgorithm] = &[ webpki_algs_aws::RSA_PKCS1_2048_8192_SHA384, - webpki_algs_aws::RSA_PKCS1_2048_8192_SHA512, webpki_algs_aws::RSA_PKCS1_3072_8192_SHA384, - ], - mapping: &[ - ( - SignatureScheme::ECDSA_NISTP256_SHA256, - &[ - webpki_algs_aws::ECDSA_P256_SHA256, - webpki_algs_aws::ECDSA_P384_SHA256, - webpki_algs_aws::ECDSA_P521_SHA256, - ], - ), - ( - SignatureScheme::ECDSA_NISTP384_SHA384, - &[ - webpki_algs_aws::ECDSA_P384_SHA384, - webpki_algs_aws::ECDSA_P256_SHA384, - webpki_algs_aws::ECDSA_P521_SHA384, - ], - ), - ( - SignatureScheme::ECDSA_NISTP521_SHA512, - &[webpki_algs_aws::ECDSA_P521_SHA512], - ), - ( - SignatureScheme::RSA_PSS_SHA256, - &[webpki_algs_aws::RSA_PSS_2048_8192_SHA256_LEGACY_KEY], - ), - ( - SignatureScheme::RSA_PSS_SHA384, - &[webpki_algs_aws::RSA_PSS_2048_8192_SHA384_LEGACY_KEY], - ), - ( - SignatureScheme::RSA_PSS_SHA512, - &[webpki_algs_aws::RSA_PSS_2048_8192_SHA512_LEGACY_KEY], - ), - ( - SignatureScheme::RSA_PKCS1_SHA256, - &[webpki_algs_aws::RSA_PKCS1_2048_8192_SHA256], - ), - ( - SignatureScheme::RSA_PKCS1_SHA384, - &[webpki_algs_aws::RSA_PKCS1_2048_8192_SHA384], - ), - ( - SignatureScheme::RSA_PKCS1_SHA512, - &[webpki_algs_aws::RSA_PKCS1_2048_8192_SHA512], - ), - ( - SignatureScheme::ECDSA_SHA1_Legacy, - &[webpki_algs_aws::ECDSA_P256_SHA256], // fake signature scheme from the patch - ), - ( - SignatureScheme::RSA_PKCS1_SHA1, - &[webpki_algs_aws::RSA_PKCS1_2048_8192_SHA256], // fake signature scheme from the patch - ), - ], - }; + ]; + static RSA_PKCS1_512_ALGS: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PKCS1_2048_8192_SHA512]; + static ED25519_ALGS: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::ED25519]; + static EMPTY: &[&dyn pki_types::SignatureVerificationAlgorithm] = &[]; + + match self { + Self::EcdsaSecp256r1Sha256 => ECDSA_P256_ALGS, + Self::EcdsaSecp384r1Sha384 => ECDSA_P384_ALGS, + Self::EcdsaSecp521r1Sha512 => ECDSA_P521_ALGS, + Self::RsaPssRsaSha256 => RSA_PSS_256_ALGS, + Self::RsaPssRsaSha384 => RSA_PSS_384_ALGS, + Self::RsaPssRsaSha512 => RSA_PSS_512_ALGS, + Self::RsaPkcs1Sha256 => RSA_PKCS1_256_ALGS, + Self::RsaPkcs1Sha384 => RSA_PKCS1_384_ALGS, + Self::RsaPkcs1Sha512 => RSA_PKCS1_512_ALGS, + Self::Ed25519 => ED25519_ALGS, + // Ed448 is not supported by webpki, SHA1 legacy uses fallback in mapping + Self::Ed448 | Self::RsaPkcs1Sha1 | Self::EcdsaSha1Legacy => EMPTY, + } + } + + /// Returns the mapping entry for this algorithm (SignatureScheme -> webpki algs). + fn to_mapping_entry( + &self, + ) -> Option<( + SignatureScheme, + &'static [&'static dyn pki_types::SignatureVerificationAlgorithm], + )> { + // Static arrays for each algorithm type - includes multiple curves for ECDSA + static ECDSA_P256_MAPPING: &[&dyn pki_types::SignatureVerificationAlgorithm] = &[ + webpki_algs_aws::ECDSA_P256_SHA256, + webpki_algs_aws::ECDSA_P384_SHA256, + webpki_algs_aws::ECDSA_P521_SHA256, + ]; + static ECDSA_P384_MAPPING: &[&dyn pki_types::SignatureVerificationAlgorithm] = &[ + webpki_algs_aws::ECDSA_P384_SHA384, + webpki_algs_aws::ECDSA_P256_SHA384, + webpki_algs_aws::ECDSA_P521_SHA384, + ]; + static ECDSA_P521_MAPPING: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::ECDSA_P521_SHA512]; + static RSA_PSS_256: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PSS_2048_8192_SHA256_LEGACY_KEY]; + static RSA_PSS_384: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PSS_2048_8192_SHA384_LEGACY_KEY]; + static RSA_PSS_512: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PSS_2048_8192_SHA512_LEGACY_KEY]; + static RSA_PKCS1_256: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PKCS1_2048_8192_SHA256]; + static RSA_PKCS1_384: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PKCS1_2048_8192_SHA384]; + static RSA_PKCS1_512: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PKCS1_2048_8192_SHA512]; + // Legacy SHA1 algorithms fall back to SHA256 (fake signature scheme from the patch) + static RSA_PKCS1_SHA1_FALLBACK: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::RSA_PKCS1_2048_8192_SHA256]; + static ECDSA_SHA1_FALLBACK: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::ECDSA_P256_SHA256]; + static ED25519: &[&dyn pki_types::SignatureVerificationAlgorithm] = + &[webpki_algs_aws::ED25519]; + + match self { + Self::EcdsaSecp256r1Sha256 => { + Some((SignatureScheme::ECDSA_NISTP256_SHA256, ECDSA_P256_MAPPING)) + } + Self::EcdsaSecp384r1Sha384 => { + Some((SignatureScheme::ECDSA_NISTP384_SHA384, ECDSA_P384_MAPPING)) + } + Self::EcdsaSecp521r1Sha512 => { + Some((SignatureScheme::ECDSA_NISTP521_SHA512, ECDSA_P521_MAPPING)) + } + Self::RsaPssRsaSha256 => Some((SignatureScheme::RSA_PSS_SHA256, RSA_PSS_256)), + Self::RsaPssRsaSha384 => Some((SignatureScheme::RSA_PSS_SHA384, RSA_PSS_384)), + Self::RsaPssRsaSha512 => Some((SignatureScheme::RSA_PSS_SHA512, RSA_PSS_512)), + Self::RsaPkcs1Sha256 => Some((SignatureScheme::RSA_PKCS1_SHA256, RSA_PKCS1_256)), + Self::RsaPkcs1Sha384 => Some((SignatureScheme::RSA_PKCS1_SHA384, RSA_PKCS1_384)), + Self::RsaPkcs1Sha512 => Some((SignatureScheme::RSA_PKCS1_SHA512, RSA_PKCS1_512)), + Self::RsaPkcs1Sha1 => { + Some((SignatureScheme::RSA_PKCS1_SHA1, RSA_PKCS1_SHA1_FALLBACK)) + } + Self::EcdsaSha1Legacy => { + Some((SignatureScheme::ECDSA_SHA1_Legacy, ECDSA_SHA1_FALLBACK)) + } + Self::Ed25519 => Some((SignatureScheme::ED25519, ED25519)), + // Ed448 is not supported + Self::Ed448 => None, + } + } +} + +/// Global cache for `WebPkiSupportedAlgorithms` to avoid memory leaks from repeated `Box::leak` calls. +/// Each unique signature algorithm configuration is only leaked once. +mod sig_alg_cache { + use super::{FingerprintSignatureAlgorithm, WebPkiSupportedAlgorithms}; + use alloc::boxed::Box; + use alloc::collections::BTreeSet; + use alloc::vec::Vec; + use std::collections::HashMap; + use std::sync::{Mutex, OnceLock}; + + static CACHE: OnceLock< + Mutex, WebPkiSupportedAlgorithms>>, + > = OnceLock::new(); + + fn get_cache() + -> &'static Mutex, WebPkiSupportedAlgorithms>> { + CACHE.get_or_init(|| Mutex::new(HashMap::new())) + } + + pub(super) fn get_or_create( + signature_algorithms: &[FingerprintSignatureAlgorithm], + ) -> WebPkiSupportedAlgorithms { + let cache = get_cache(); + + // Check if we already have this configuration cached + { + let guard = cache.lock().unwrap(); + if let Some(cached) = guard.get(signature_algorithms) { + return *cached; + } + } + + // Build the algorithms (will leak, but only once per unique configuration) + let algorithms = build_algorithms(signature_algorithms); + + // Store in cache + { + let mut guard = cache.lock().unwrap(); + // Double-check in case another thread added it while we were building + if let Some(cached) = guard.get(signature_algorithms) { + return *cached; + } + guard.insert(signature_algorithms.to_vec(), algorithms); + } + + algorithms + } + + fn build_algorithms( + signature_algorithms: &[FingerprintSignatureAlgorithm], + ) -> WebPkiSupportedAlgorithms { + // Collect all unique webpki algorithms (using pointer address for dedup) + let mut seen: BTreeSet = BTreeSet::new(); + let all_algs: Vec<&'static dyn pki_types::SignatureVerificationAlgorithm> = + signature_algorithms + .iter() + .flat_map(|sa| sa.to_webpki_algs().iter().copied()) + .filter(|alg| { + let ptr: *const dyn pki_types::SignatureVerificationAlgorithm = *alg; + seen.insert(ptr as *const () as usize) + }) + .collect(); + + // Collect mapping entries in fingerprint order + let mapping_entries: Vec<( + crate::crypto::SignatureScheme, + &'static [&'static dyn pki_types::SignatureVerificationAlgorithm], + )> = signature_algorithms + .iter() + .filter_map(|sa| sa.to_mapping_entry()) + .collect(); + + // Leak the vectors to get 'static references + // This only happens once per unique configuration due to caching + let all_static: &'static [&'static dyn pki_types::SignatureVerificationAlgorithm] = + Box::leak(all_algs.into_boxed_slice()); + let mapping_static: &'static [( + crate::crypto::SignatureScheme, + &'static [&'static dyn pki_types::SignatureVerificationAlgorithm], + )] = Box::leak(mapping_entries.into_boxed_slice()); + + WebPkiSupportedAlgorithms { + all: all_static, + mapping: mapping_static, + } + } +} -/// The signature schemes supported by Firefox. -pub static FIREFOX_SIGNATURE_SCHEMES: &[SignatureScheme; 11] = &[ - SignatureScheme::ECDSA_NISTP256_SHA256, - SignatureScheme::ECDSA_NISTP384_SHA384, - SignatureScheme::ECDSA_NISTP521_SHA512, - SignatureScheme::RSA_PSS_SHA256, - SignatureScheme::RSA_PSS_SHA384, - SignatureScheme::RSA_PSS_SHA512, - SignatureScheme::RSA_PKCS1_SHA256, - SignatureScheme::RSA_PKCS1_SHA384, - SignatureScheme::RSA_PKCS1_SHA512, - SignatureScheme::ECDSA_SHA1_Legacy, - SignatureScheme::RSA_PKCS1_SHA1, -]; +impl TlsFingerprint { + /// Builds a `WebPkiSupportedAlgorithms` from this fingerprint's signature algorithms. + /// + /// The order of algorithms in the mapping reflects the fingerprint's preference order, + /// which is important for TLS fingerprinting. + /// + /// Results are cached globally to avoid memory leaks from repeated allocations. + /// Each unique signature algorithm configuration is only allocated once. + pub fn to_signature_verification_algorithms(&self) -> WebPkiSupportedAlgorithms { + sig_alg_cache::get_or_create(&self.signature_algorithms) + } +} diff --git a/rustls/src/crypto/mod.rs b/rustls/src/crypto/mod.rs index 526390402db..43da3df6b5d 100644 --- a/rustls/src/crypto/mod.rs +++ b/rustls/src/crypto/mod.rs @@ -7,8 +7,6 @@ use core::time::Duration; use pki_types::PrivateKeyDer; -#[cfg(feature = "impit")] -use crate::client::client_emulator::BrowserEmulator; use crate::enums::ProtocolVersion; use crate::error::{ApiMisuse, Error}; use crate::msgs::handshake::ALL_KEY_EXCHANGE_ALGORITHMS; @@ -235,60 +233,47 @@ pub struct CryptoProvider { /// Convenience builder for `CryptoProvider`. #[cfg(feature = "impit")] pub struct CryptoProviderBuilder { - browser_emulator: Option, + tls_fingerprint: Option, } #[cfg(feature = "impit")] impl CryptoProviderBuilder { - /// Sets the browser emulator to use for this provider. - pub fn with_browser_emulator(mut self, browser_emulator: &BrowserEmulator) -> Self { - self.browser_emulator = Some(browser_emulator.clone()); + /// Sets the TLS fingerprint to use for this provider. + pub fn with_tls_fingerprint(mut self, fingerprint: &emulation::TlsFingerprint) -> Self { + self.tls_fingerprint = Some(fingerprint.clone()); self } /// Builds the `CryptoProvider`. pub fn build(self) -> CryptoProvider { - use crate::client::client_emulator::{BrowserEmulator, BrowserType}; use crate::crypto::aws_lc_rs::DEFAULT_PROVIDER; - match self.browser_emulator { - Some(BrowserEmulator { - browser_type: BrowserType::Chrome, - version: _, - }) => { - use crate::crypto::aws_lc_rs::DEFAULT_PROVIDER; - use crate::crypto::emulation::{ - CHROME_SIGNATURE_VERIFICATION_ALGOS, CHROME_TLS12_CIPHER_SUITES, - CHROME_TLS13_CIPHER_SUITES, - }; - - let provider = CryptoProvider { - tls13_cipher_suites: Cow::Borrowed(&CHROME_TLS13_CIPHER_SUITES), - tls12_cipher_suites: Cow::Borrowed(&CHROME_TLS12_CIPHER_SUITES), - signature_verification_algorithms: CHROME_SIGNATURE_VERIFICATION_ALGOS, - ..DEFAULT_PROVIDER - }; - - provider - } - Some(BrowserEmulator { - browser_type: BrowserType::Firefox, - version: _, - }) => { - use crate::crypto::aws_lc_rs::DEFAULT_PROVIDER; - use crate::crypto::emulation::{ - FIREFOX_SIGNATURE_VERIFICATION_ALGOS, FIREFOX_TLS12_CIPHER_SUITES, - FIREFOX_TLS13_CIPHER_SUITES, - }; - - let provider = CryptoProvider { - tls13_cipher_suites: Cow::Borrowed(&FIREFOX_TLS13_CIPHER_SUITES), - tls12_cipher_suites: Cow::Borrowed(&FIREFOX_TLS12_CIPHER_SUITES), - signature_verification_algorithms: FIREFOX_SIGNATURE_VERIFICATION_ALGOS, + match self.tls_fingerprint { + Some(ref fingerprint) => { + let tls13: Vec<_> = fingerprint + .cipher_suites + .iter() + .filter_map(|cs| match cs.to_supported_cipher_suite() { + SupportedCipherSuite::Tls13(s) => Some(s), + _ => None, + }) + .collect(); + let tls12: Vec<_> = fingerprint + .cipher_suites + .iter() + .filter_map(|cs| match cs.to_supported_cipher_suite() { + SupportedCipherSuite::Tls12(s) => Some(s), + _ => None, + }) + .collect(); + let sig_algs = fingerprint.to_signature_verification_algorithms(); + + CryptoProvider { + tls13_cipher_suites: Cow::Owned(tls13), + tls12_cipher_suites: Cow::Owned(tls12), + signature_verification_algorithms: sig_algs, ..DEFAULT_PROVIDER - }; - - provider + } } None => DEFAULT_PROVIDER, } @@ -300,7 +285,7 @@ impl CryptoProvider { #[cfg(feature = "impit")] pub fn builder() -> CryptoProviderBuilder { CryptoProviderBuilder { - browser_emulator: None, + tls_fingerprint: None, } } diff --git a/rustls/src/lib.rs b/rustls/src/lib.rs index 1d84f7fec2c..eb2234aa9e1 100644 --- a/rustls/src/lib.rs +++ b/rustls/src/lib.rs @@ -492,6 +492,13 @@ pub use crate::tls12::Tls12CipherSuite; pub use crate::tls13::Tls13CipherSuite; #[cfg(feature = "impit")] pub use crate::verify::NoVerifier; +#[cfg(feature = "impit")] +pub use crate::client::WantsClientCertWithTlsFingerprint; +#[cfg(feature = "impit")] +pub use crate::crypto::emulation::{ + FingerprintCertCompressionAlgorithm, FingerprintCipherSuite, FingerprintKeyExchangeGroup, + FingerprintSignatureAlgorithm, TlsExtensionsConfig, TlsFingerprint, +}; pub use crate::verify::{DigitallySignedStruct, DistinguishedName, SignerPublicKey}; pub use crate::versions::{ALL_VERSIONS, DEFAULT_VERSIONS, SupportedProtocolVersion}; pub use crate::webpki::RootCertStore; diff --git a/rustls/src/msgs/enums.rs b/rustls/src/msgs/enums.rs index bdb57454811..0aa563bcf02 100644 --- a/rustls/src/msgs/enums.rs +++ b/rustls/src/msgs/enums.rs @@ -105,6 +105,7 @@ enum_builder! { DelegatedCredentials => 0x0022, RecordSizeLimit => 0x001c, ApplicationSettings => 0x4469, + ApplicationSettingsNew => 0x44cd, } } diff --git a/rustls/src/msgs/handshake.rs b/rustls/src/msgs/handshake.rs index 6bbecf1f3da..d649efa7be3 100644 --- a/rustls/src/msgs/handshake.rs +++ b/rustls/src/msgs/handshake.rs @@ -943,6 +943,15 @@ extension_struct! { ExtensionType::ApplicationSettings => pub(crate) application_settings: Option, + /// ALPS extension (new codepoint 17613, used by Chrome 136+) + ExtensionType::ApplicationSettingsNew => + pub(crate) application_settings_new: Option, + + /// Padding extension (RFC7685) - used to pad ClientHello to avoid triggering + /// bugs in some middleboxes. Contains zero bytes. + ExtensionType::Padding => + pub(crate) padding: Option>, + /// Encrypted client hello outer extensions (draft-ietf-tls-esni) ExtensionType::EncryptedClientHelloOuterExtensions => pub(crate) encrypted_client_hello_outer: Option>, @@ -982,6 +991,8 @@ impl ClientExtensions<'_> { encrypted_client_hello_outer, order_seed, application_settings, + application_settings_new, + padding, reserved_grease, signed_certificate_timestamp, delegated_credentials, @@ -1014,6 +1025,8 @@ impl ClientExtensions<'_> { order_seed, contiguous_extensions, application_settings, + application_settings_new, + padding: padding.map(|x| x.into_owned()), reserved_grease, signed_certificate_timestamp, delegated_credentials, diff --git a/rustls/src/verify.rs b/rustls/src/verify.rs index a2cfcead85d..2c565ca2f9a 100644 --- a/rustls/src/verify.rs +++ b/rustls/src/verify.rs @@ -1,12 +1,9 @@ +#[cfg(feature = "impit")] +use alloc::vec; use alloc::vec::Vec; use core::fmt::Debug; -#[cfg(feature = "impit")] -use std::vec; - use pki_types::{CertificateDer, ServerName, SubjectPublicKeyInfoDer, UnixTime}; -#[cfg(feature = "impit")] -use crate::client::client_emulator::BrowserEmulator; use crate::crypto::{Identity, SignatureScheme}; use crate::enums::CertificateType; use crate::error::{Error, InvalidMessage}; @@ -31,13 +28,36 @@ use crate::x509::wrap_in_sequence; /// Used for the `ignore_tls_errors` option in `impit`. #[cfg(feature = "impit")] #[derive(Debug)] -pub struct NoVerifier(Option); +pub struct NoVerifier { + signature_schemes: Vec, +} #[cfg(feature = "impit")] impl NoVerifier { - /// Create a new `NoVerifier` instance. - pub fn new(browser_emulator: Option) -> Self { - Self(browser_emulator) + /// Create a new `NoVerifier` instance with the given signature schemes. + pub fn new(signature_schemes: Vec) -> Self { + Self { signature_schemes } + } + + /// Create a new `NoVerifier` instance with default signature schemes. + pub fn with_default_schemes() -> Self { + Self { + signature_schemes: vec![ + SignatureScheme::RSA_PKCS1_SHA1, + SignatureScheme::ECDSA_SHA1_Legacy, + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::ECDSA_NISTP256_SHA256, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::ECDSA_NISTP521_SHA512, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::ED25519, + SignatureScheme::ED448, + ], + } } } @@ -62,30 +82,7 @@ impl ServerVerifier for NoVerifier { } fn supported_verify_schemes(&self) -> Vec { - use crate::client::client_emulator::BrowserType; - use crate::crypto::emulation::{CHROME_SIGNATURE_SCHEMES, FIREFOX_SIGNATURE_SCHEMES}; - - match &self.0 { - Some(browser_emulator) => match browser_emulator.browser_type { - BrowserType::Chrome => CHROME_SIGNATURE_SCHEMES.to_vec(), - BrowserType::Firefox => FIREFOX_SIGNATURE_SCHEMES.to_vec(), - }, - None => vec![ - SignatureScheme::RSA_PKCS1_SHA1, - SignatureScheme::ECDSA_SHA1_Legacy, - SignatureScheme::RSA_PKCS1_SHA256, - SignatureScheme::ECDSA_NISTP256_SHA256, - SignatureScheme::RSA_PKCS1_SHA384, - SignatureScheme::ECDSA_NISTP384_SHA384, - SignatureScheme::RSA_PKCS1_SHA512, - SignatureScheme::ECDSA_NISTP521_SHA512, - SignatureScheme::RSA_PSS_SHA256, - SignatureScheme::RSA_PSS_SHA384, - SignatureScheme::RSA_PSS_SHA512, - SignatureScheme::ED25519, - SignatureScheme::ED448, - ], - } + self.signature_schemes.clone() } fn request_ocsp_response(&self) -> bool { From 439602253d6bd119b80443a99ecce125e85aafa4 Mon Sep 17 00:00:00 2001 From: Petr Patek Date: Mon, 23 Mar 2026 11:46:29 +0100 Subject: [PATCH 2/2] chore: fix cargo fmt formatting --- rustls/src/client/config.rs | 55 ++++++++++--------- rustls/src/client/hs.rs | 5 +- rustls/src/client/mod.rs | 4 +- rustls/src/crypto/emulation/mod.rs | 86 ++++++++++++++++-------------- rustls/src/lib.rs | 14 ++--- 5 files changed, 87 insertions(+), 77 deletions(-) diff --git a/rustls/src/client/config.rs b/rustls/src/client/config.rs index 753909540d0..87555b55e86 100644 --- a/rustls/src/client/config.rs +++ b/rustls/src/client/config.rs @@ -11,10 +11,10 @@ use super::ech::EchMode; use super::handy::ClientSessionMemoryCache; use super::handy::{FailResolveClientCert, NoClientSessionStorage}; use crate::builder::{ConfigBuilder, WantsVerifier}; -#[cfg(feature = "impit")] -use crate::crypto::emulation::TlsFingerprint; #[cfg(doc)] use crate::crypto; +#[cfg(feature = "impit")] +use crate::crypto::emulation::TlsFingerprint; use crate::crypto::kx::NamedGroup; use crate::crypto::{ CipherSuite, Credentials, CryptoProvider, Identity, SelectedCredential, SignatureScheme, @@ -684,30 +684,33 @@ impl ConfigBuilder { self.provider.consistency_check()?; // Determine cert compression based on fingerprint - let (cert_compressors, cert_decompressors) = - if let Some(ref compression) = self.state.tls_fingerprint.cert_compression { - let compressors: Vec<_> = compression - .iter() - .filter_map(|alg| match alg { - FingerprintCertCompressionAlgorithm::Brotli => { - Some(compress::BROTLI_COMPRESSOR) - } - _ => None, // Only Brotli is supported for now - }) - .collect(); - let decompressors: Vec<_> = compression - .iter() - .filter_map(|alg| match alg { - FingerprintCertCompressionAlgorithm::Brotli => { - Some(compress::BROTLI_DECOMPRESSOR) - } - _ => None, - }) - .collect(); - (compressors, decompressors) - } else { - (vec![], vec![]) - }; + let (cert_compressors, cert_decompressors) = if let Some(ref compression) = self + .state + .tls_fingerprint + .cert_compression + { + let compressors: Vec<_> = compression + .iter() + .filter_map(|alg| match alg { + FingerprintCertCompressionAlgorithm::Brotli => { + Some(compress::BROTLI_COMPRESSOR) + } + _ => None, // Only Brotli is supported for now + }) + .collect(); + let decompressors: Vec<_> = compression + .iter() + .filter_map(|alg| match alg { + FingerprintCertCompressionAlgorithm::Brotli => { + Some(compress::BROTLI_DECOMPRESSOR) + } + _ => None, + }) + .collect(); + (compressors, decompressors) + } else { + (vec![], vec![]) + }; Ok(ClientConfig { tls_fingerprint: Some(self.state.tls_fingerprint), diff --git a/rustls/src/client/hs.rs b/rustls/src/client/hs.rs index 489b13efc0c..f62db9764aa 100644 --- a/rustls/src/client/hs.rs +++ b/rustls/src/client/hs.rs @@ -788,7 +788,10 @@ fn emit_client_hello_for_retry( .clone(); } - if !fingerprint.extensions.supported_versions { + if !fingerprint + .extensions + .supported_versions + { exts.supported_versions = None; } } diff --git a/rustls/src/client/mod.rs b/rustls/src/client/mod.rs index 49f51ccf185..2e4eb6cf221 100644 --- a/rustls/src/client/mod.rs +++ b/rustls/src/client/mod.rs @@ -14,12 +14,12 @@ pub use crate::webpki::{ }; mod config; +#[cfg(feature = "impit")] +pub use config::WantsClientCertWithTlsFingerprint; pub use config::{ ClientConfig, ClientCredentialResolver, ClientSessionStore, CredentialRequest, Resumption, Tls12Resumption, WantsClientCert, }; -#[cfg(feature = "impit")] -pub use config::WantsClientCertWithTlsFingerprint; mod connection; #[cfg(feature = "std")] diff --git a/rustls/src/crypto/emulation/mod.rs b/rustls/src/crypto/emulation/mod.rs index 6ff635bdb4b..74f1744b6b5 100644 --- a/rustls/src/crypto/emulation/mod.rs +++ b/rustls/src/crypto/emulation/mod.rs @@ -127,55 +127,61 @@ impl FingerprintCipherSuite { /// Converts the fingerprint cipher suite to rustls's SupportedCipherSuite. pub fn to_supported_cipher_suite(&self) -> SupportedCipherSuite { match self { - Self::TLS13_AES_128_GCM_SHA256 => SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_AES_128_GCM_SHA256), - Self::TLS13_AES_256_GCM_SHA384 => SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384), - Self::TLS13_CHACHA20_POLY1305_SHA256 => { - SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256) - } - Self::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 => { - SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) - } - Self::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 => { - SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) - } - Self::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 => { - SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) - } - Self::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 => { - SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) + Self::TLS13_AES_128_GCM_SHA256 => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_AES_128_GCM_SHA256) } - Self::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 => { - SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) + Self::TLS13_AES_256_GCM_SHA384 => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384) } - Self::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => { - SupportedCipherSuite::Tls12(aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) + Self::TLS13_CHACHA20_POLY1305_SHA256 => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256) } + Self::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 => SupportedCipherSuite::Tls12( + aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + ), + Self::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 => SupportedCipherSuite::Tls12( + aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + ), + Self::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 => SupportedCipherSuite::Tls12( + aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + ), + Self::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 => SupportedCipherSuite::Tls12( + aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + ), + Self::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 => SupportedCipherSuite::Tls12( + aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + ), + Self::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => SupportedCipherSuite::Tls12( + aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + ), // These CBC/RSA cipher suites are fake TLS 1.3 cipher suites from the impit patch - Self::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA => { - SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) - } - Self::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA => { - SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA) - } - Self::TLS_RSA_WITH_AES_128_GCM_SHA256 => { - SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_128_GCM_SHA256) - } - Self::TLS_RSA_WITH_AES_256_GCM_SHA384 => { - SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_256_GCM_SHA384) - } + Self::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA => SupportedCipherSuite::Tls13( + aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + ), + Self::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA => SupportedCipherSuite::Tls13( + aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + ), + Self::TLS_RSA_WITH_AES_128_GCM_SHA256 => SupportedCipherSuite::Tls13( + aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_128_GCM_SHA256, + ), + Self::TLS_RSA_WITH_AES_256_GCM_SHA384 => SupportedCipherSuite::Tls13( + aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_256_GCM_SHA384, + ), Self::TLS_RSA_WITH_AES_128_CBC_SHA => { SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_128_CBC_SHA) } Self::TLS_RSA_WITH_AES_256_CBC_SHA => { SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_RSA_WITH_AES_256_CBC_SHA) } - Self::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA => { - SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA) - } - Self::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA => { - SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA) + Self::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA => SupportedCipherSuite::Tls13( + aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + ), + Self::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA => SupportedCipherSuite::Tls13( + aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + ), + Self::Grease => { + SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_RESERVED_GREASE) } - Self::Grease => SupportedCipherSuite::Tls13(aws_lc_rs::cipher_suite::TLS13_RESERVED_GREASE), } } } @@ -457,9 +463,7 @@ impl FingerprintSignatureAlgorithm { Self::RsaPkcs1Sha256 => Some((SignatureScheme::RSA_PKCS1_SHA256, RSA_PKCS1_256)), Self::RsaPkcs1Sha384 => Some((SignatureScheme::RSA_PKCS1_SHA384, RSA_PKCS1_384)), Self::RsaPkcs1Sha512 => Some((SignatureScheme::RSA_PKCS1_SHA512, RSA_PKCS1_512)), - Self::RsaPkcs1Sha1 => { - Some((SignatureScheme::RSA_PKCS1_SHA1, RSA_PKCS1_SHA1_FALLBACK)) - } + Self::RsaPkcs1Sha1 => Some((SignatureScheme::RSA_PKCS1_SHA1, RSA_PKCS1_SHA1_FALLBACK)), Self::EcdsaSha1Legacy => { Some((SignatureScheme::ECDSA_SHA1_Legacy, ECDSA_SHA1_FALLBACK)) } diff --git a/rustls/src/lib.rs b/rustls/src/lib.rs index eb2234aa9e1..39194b6597c 100644 --- a/rustls/src/lib.rs +++ b/rustls/src/lib.rs @@ -473,10 +473,17 @@ pub mod unbuffered { // The public interface is: pub use crate::builder::{ConfigBuilder, ConfigSide, WantsVerifier}; +#[cfg(feature = "impit")] +pub use crate::client::WantsClientCertWithTlsFingerprint; pub use crate::common_state::{CommonState, HandshakeKind, IoState, Side}; #[cfg(feature = "std")] pub use crate::conn::{Connection, Reader, Writer}; pub use crate::conn::{ConnectionCommon, KeyingMaterialExporter, SideData, kernel}; +#[cfg(feature = "impit")] +pub use crate::crypto::emulation::{ + FingerprintCertCompressionAlgorithm, FingerprintCipherSuite, FingerprintKeyExchangeGroup, + FingerprintSignatureAlgorithm, TlsExtensionsConfig, TlsFingerprint, +}; pub use crate::error::Error; pub use crate::key_log::{KeyLog, NoKeyLog}; #[cfg(feature = "std")] @@ -492,13 +499,6 @@ pub use crate::tls12::Tls12CipherSuite; pub use crate::tls13::Tls13CipherSuite; #[cfg(feature = "impit")] pub use crate::verify::NoVerifier; -#[cfg(feature = "impit")] -pub use crate::client::WantsClientCertWithTlsFingerprint; -#[cfg(feature = "impit")] -pub use crate::crypto::emulation::{ - FingerprintCertCompressionAlgorithm, FingerprintCipherSuite, FingerprintKeyExchangeGroup, - FingerprintSignatureAlgorithm, TlsExtensionsConfig, TlsFingerprint, -}; pub use crate::verify::{DigitallySignedStruct, DistinguishedName, SignerPublicKey}; pub use crate::versions::{ALL_VERSIONS, DEFAULT_VERSIONS, SupportedProtocolVersion}; pub use crate::webpki::RootCertStore;