From a7c55e1f4a90fbb9b3a6da2a214679f9494cf734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Wed, 28 Jan 2026 11:37:13 +0100 Subject: [PATCH 01/10] feat: add more Chrome and Firefox fingerprints --- Cargo.lock | 1 - Cargo.toml | 2 +- README.md | 2 +- impit-cli/src/main.rs | 2 +- impit-node/index.d.ts | 12 +- impit-node/src/impit_builder.rs | 27 +- impit-node/test/fingerprints.test.ts | 5 +- impit-python/src/async_client.rs | 2 +- impit-python/src/client.rs | 2 +- impit/examples/basic.rs | 2 +- impit/src/fingerprint/database.rs | 4 +- impit/src/fingerprint/database/chrome.rs | 675 ++++++++++++++++++++++ impit/src/fingerprint/database/firefox.rs | 360 ++++++++++-- impit/src/fingerprint/mod.rs | 31 + impit/src/fingerprint/types.rs | 8 + impit/src/impit.rs | 2 +- impit/src/lib.rs | 2 +- impit/src/tls/mod.rs | 25 +- 18 files changed, 1110 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 235b7f56..782c5032 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2556,7 +2556,6 @@ dependencies = [ [[package]] name = "rustls" version = "0.23.36" -source = "git+https://github.com/apify/rustls?rev=8c46c4744be711e87946d967301c8dac648f4049#8c46c4744be711e87946d967301c8dac648f4049" dependencies = [ "aws-lc-rs", "brotli", diff --git a/Cargo.toml b/Cargo.toml index 0c211faa..8f4505bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ [patch.crates-io] h2 = { git = "https://github.com/apify/h2", rev = "7f393a728a8db07cabb1b78d2094772b33943b9a" } -rustls = { git = "https://github.com/apify/rustls", rev="8c46c4744be711e87946d967301c8dac648f4049" } +rustls = { path = "../rustls/rustls" } [profile.release] strip = true # Automatically strip symbols from the binary. diff --git a/README.md b/README.md index aa70ea92..f4c2ce57 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ use impit::{impit::Impit, fingerprint::database as fingerprints}; #[tokio::main] async fn main() { let impit = Impit::::builder() - .with_fingerprint(fingerprints::firefox_128::fingerprint()) + .with_fingerprint(fingerprints::firefox_144::fingerprint()) .with_http3() .build() .unwrap(); diff --git a/impit-cli/src/main.rs b/impit-cli/src/main.rs index 15ffdb14..8e601066 100644 --- a/impit-cli/src/main.rs +++ b/impit-cli/src/main.rs @@ -97,7 +97,7 @@ async fn main() { client.with_fingerprint(impit::fingerprint::database::chrome_125::fingerprint()) } Browser::Firefox => { - client.with_fingerprint(impit::fingerprint::database::firefox_128::fingerprint()) + client.with_fingerprint(impit::fingerprint::database::firefox_144::fingerprint()) } Browser::Impit => client, }; diff --git a/impit-node/index.d.ts b/impit-node/index.d.ts index ff2ff200..d09e16d2 100644 --- a/impit-node/index.d.ts +++ b/impit-node/index.d.ts @@ -206,7 +206,17 @@ export declare class ImpitResponse { * See {@link ImpitOptions.browser} for more details and usage. */ export type Browser = 'chrome'| -'firefox'; +'chrome124'| +'chrome125'| +'chrome131'| +'chrome136'| +'chrome142'| +'firefox'| +'firefox133'| +'firefox135'| +'firefox144'| +'safari'| +'safari180'; export type HttpMethod = 'GET'| 'POST'| diff --git a/impit-node/src/impit_builder.rs b/impit-node/src/impit_builder.rs index 525a569b..5ebcb079 100644 --- a/impit-node/src/impit_builder.rs +++ b/impit-node/src/impit_builder.rs @@ -15,7 +15,17 @@ use crate::cookies::NodeCookieJar; #[napi(string_enum = "lowercase")] pub enum Browser { Chrome, + Chrome124, + Chrome125, + Chrome131, + Chrome136, + Chrome142, Firefox, + Firefox133, + Firefox135, + Firefox144, + Safari, + Safari180, } /// Options for configuring an {@link Impit} instance. @@ -90,8 +100,21 @@ pub struct ImpitOptions<'a> { impl From for BrowserFingerprint { fn from(val: Browser) -> Self { match val { - Browser::Chrome => impit::fingerprint::database::chrome_125::fingerprint(), - Browser::Firefox => impit::fingerprint::database::firefox_128::fingerprint(), + Browser::Chrome | Browser::Chrome124 => { + impit::fingerprint::database::chrome_124::fingerprint() + } + Browser::Chrome125 => impit::fingerprint::database::chrome_125::fingerprint(), + Browser::Chrome131 => impit::fingerprint::database::chrome_131::fingerprint(), + Browser::Chrome136 => impit::fingerprint::database::chrome_136::fingerprint(), + Browser::Chrome142 => impit::fingerprint::database::chrome_142::fingerprint(), + Browser::Firefox | Browser::Firefox133 => { + impit::fingerprint::database::firefox_133::fingerprint() + } + Browser::Firefox135 => impit::fingerprint::database::firefox_135::fingerprint(), + Browser::Firefox144 => impit::fingerprint::database::firefox_144::fingerprint(), + Browser::Safari | Browser::Safari180 => { + impit::fingerprint::database::safari_18_0::fingerprint() + } } } } diff --git a/impit-node/test/fingerprints.test.ts b/impit-node/test/fingerprints.test.ts index 5d4de32b..a738063c 100644 --- a/impit-node/test/fingerprints.test.ts +++ b/impit-node/test/fingerprints.test.ts @@ -4,7 +4,10 @@ import { Impit, Browser } from '../index.wrapper.js'; describe.each([ [Browser.Chrome, "t13d1516h2_8daaf6152771_02713d6af862"], - [Browser.Firefox, "t13d1715h2_5b57614c22b0_5c2c66f702b0"], + [Browser.Chrome125, "t13d1516h2_8daaf6152771_02713d6af862"], + [Browser.Firefox, "t13d1717h2_5b57614c22b0_3cbfd9057e0d"], + [Browser.Firefox128, "t13d1715h2_5b57614c22b0_5c2c66f702b0"], + [Browser.Safari, "t13d2014h2_d5497b48ffb0_7baf387fc6ff"], ])(`Browser emulation [%s]`, (browser, ja4) => { test(`[${browser}] emulates JA4 fingerprint`, async () => { diff --git a/impit-python/src/async_client.rs b/impit-python/src/async_client.rs index bb860fb4..5ad027c4 100644 --- a/impit-python/src/async_client.rs +++ b/impit-python/src/async_client.rs @@ -61,7 +61,7 @@ impl AsyncClient { "chrome" => builder .with_fingerprint(impit::fingerprint::database::chrome_125::fingerprint()), "firefox" => builder - .with_fingerprint(impit::fingerprint::database::firefox_128::fingerprint()), + .with_fingerprint(impit::fingerprint::database::firefox_144::fingerprint()), _ => { return Err(PyErr::new::( "Unsupported browser", diff --git a/impit-python/src/client.rs b/impit-python/src/client.rs index d2c10b65..c3e20252 100644 --- a/impit-python/src/client.rs +++ b/impit-python/src/client.rs @@ -58,7 +58,7 @@ impl Client { "chrome" => builder .with_fingerprint(impit::fingerprint::database::chrome_125::fingerprint()), "firefox" => builder - .with_fingerprint(impit::fingerprint::database::firefox_128::fingerprint()), + .with_fingerprint(impit::fingerprint::database::firefox_144::fingerprint()), _ => panic!("Unsupported browser"), }, None => builder, diff --git a/impit/examples/basic.rs b/impit/examples/basic.rs index cfdce6ed..6b010114 100644 --- a/impit/examples/basic.rs +++ b/impit/examples/basic.rs @@ -4,7 +4,7 @@ use impit::{fingerprint::database as fingerprints, impit::Impit}; #[tokio::main] async fn main() { let impit = Impit::::builder() - .with_fingerprint(fingerprints::firefox_128::fingerprint()) + .with_fingerprint(fingerprints::firefox_144::fingerprint()) .with_http3() .build() .unwrap(); diff --git a/impit/src/fingerprint/database.rs b/impit/src/fingerprint/database.rs index 9b3ebf97..42a9e295 100644 --- a/impit/src/fingerprint/database.rs +++ b/impit/src/fingerprint/database.rs @@ -5,5 +5,5 @@ mod chrome; mod firefox; -pub use chrome::chrome_125; -pub use firefox::firefox_128; +pub use chrome::{chrome_124, chrome_125, chrome_131, chrome_133, chrome_136, chrome_142}; +pub use firefox::{firefox_133, firefox_135, firefox_144}; diff --git a/impit/src/fingerprint/database/chrome.rs b/impit/src/fingerprint/database/chrome.rs index 41042967..effd701f 100644 --- a/impit/src/fingerprint/database/chrome.rs +++ b/impit/src/fingerprint/database/chrome.rs @@ -2,6 +2,681 @@ use crate::fingerprint::*; +/// Chrome 142 fingerprint module +pub mod chrome_142 { + use super::*; + + /// Returns the complete Chrome 142 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Chrome", + "142", + tls_fingerprint(), + http2_fingerprint(), + headers(), + ) + } + + /// Chrome 142 TLS fingerprint + fn tls_fingerprint() -> TlsFingerprint { + TlsFingerprint::new( + // Cipher suites in Chrome 142 preference order + // GREASE cipher at position 1 (first) - same as Chrome 136 + vec![ + CipherSuite::Grease, + CipherSuite::TLS13_AES_128_GCM_SHA256, + CipherSuite::TLS13_AES_256_GCM_SHA384, + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, + ], + // Key exchange groups (includes post-quantum hybrid X25519MLKEM768) + // GREASE at position 1 (first) - same as Chrome 136 + vec![ + KeyExchangeGroup::Grease, + KeyExchangeGroup::X25519MLKEM768, + KeyExchangeGroup::X25519, + KeyExchangeGroup::Secp256r1, + KeyExchangeGroup::Secp384r1, + ], + // Signature algorithms + vec![ + SignatureAlgorithm::EcdsaSecp256r1Sha256, + SignatureAlgorithm::RsaPssRsaSha256, + SignatureAlgorithm::RsaPkcs1Sha256, + SignatureAlgorithm::EcdsaSecp384r1Sha384, + SignatureAlgorithm::RsaPssRsaSha384, + SignatureAlgorithm::RsaPkcs1Sha384, + SignatureAlgorithm::RsaPssRsaSha512, + SignatureAlgorithm::RsaPkcs1Sha512, + ], + // TLS extensions configuration + // Chrome 142 uses new ALPS codepoint (17613) + TlsExtensions::new( + true, // server_name + true, // status_request + true, // supported_groups + true, // signature_algorithms + true, // application_layer_protocol_negotiation + true, // signed_certificate_timestamp + true, // key_share + true, // psk_key_exchange_modes + true, // supported_versions + Some(vec![CertificateCompressionAlgorithm::Brotli]), // compress_certificate + true, // application_settings + false, // delegated_credentials (Chrome doesn't use) + None, // record_size_limit (Chrome doesn't use) + // Extension order (critical for fingerprinting) + vec![ + ExtensionType::ServerName, + ExtensionType::ExtendedMasterSecret, + ExtensionType::SessionTicket, + ExtensionType::SignatureAlgorithms, + ExtensionType::StatusRequest, + ExtensionType::SupportedGroups, + ExtensionType::ApplicationLayerProtocolNegotiation, + ExtensionType::SignedCertificateTimestamp, + ExtensionType::KeyShare, + ExtensionType::PskKeyExchangeModes, + ExtensionType::SupportedVersions, + ExtensionType::CompressCertificate, + ExtensionType::ApplicationSettings, + ], + ) + .with_new_alps_codepoint(true), + // ECH configuration (GREASE mode) + Some(EchConfig::new( + EchMode::Grease { + hpke_suite: HpkeKemId::DhKemX25519HkdfSha256, + }, + None, + )), + // ALPN protocols + vec![b"h2".to_vec(), b"http/1.1".to_vec()], + ) + } + + /// Chrome 142 HTTP/2 fingerprint + fn http2_fingerprint() -> Http2Fingerprint { + Http2Fingerprint { + pseudo_header_order: vec![ + ":method".to_string(), + ":authority".to_string(), + ":scheme".to_string(), + ":path".to_string(), + ":protocol".to_string(), + ":status".to_string(), + ], + } + } + + /// Chrome 142 HTTP headers + fn headers() -> Vec<(String, String)> { + vec![ + ("sec-ch-ua".to_string(), "\"Chromium\";v=\"142\", \"Google Chrome\";v=\"142\", \"Not_A Brand\";v=\"99\"".to_string()), + ("sec-ch-ua-mobile".to_string(), "?0".to_string()), + ("sec-ch-ua-platform".to_string(), "\"macOS\"".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36".to_string()), + ("accept".to_string(), "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".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br, zstd".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.9".to_string()), + ("priority".to_string(), "u=0, i".to_string()), + ] + } +} + +/// Chrome 136 fingerprint module +pub mod chrome_136 { + use super::*; + + /// Returns the complete Chrome 136 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Chrome", + "136", + tls_fingerprint(), + http2_fingerprint(), + headers(), + ) + } + + /// Chrome 136 TLS fingerprint + fn tls_fingerprint() -> TlsFingerprint { + TlsFingerprint::new( + // Cipher suites in Chrome 136 preference order + // GREASE cipher at position 1 (first) based on Wireshark capture + vec![ + CipherSuite::Grease, + CipherSuite::TLS13_AES_128_GCM_SHA256, + CipherSuite::TLS13_AES_256_GCM_SHA384, + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, + ], + // Key exchange groups (includes post-quantum hybrid X25519MLKEM768) + // GREASE at position 1 (first) based on Wireshark capture + vec![ + KeyExchangeGroup::Grease, + KeyExchangeGroup::X25519MLKEM768, + KeyExchangeGroup::X25519, + KeyExchangeGroup::Secp256r1, + KeyExchangeGroup::Secp384r1, + ], + // Signature algorithms + vec![ + SignatureAlgorithm::EcdsaSecp256r1Sha256, + SignatureAlgorithm::RsaPssRsaSha256, + SignatureAlgorithm::RsaPkcs1Sha256, + SignatureAlgorithm::EcdsaSecp384r1Sha384, + SignatureAlgorithm::RsaPssRsaSha384, + SignatureAlgorithm::RsaPkcs1Sha384, + SignatureAlgorithm::RsaPssRsaSha512, + SignatureAlgorithm::RsaPkcs1Sha512, + ], + // TLS extensions configuration + // Chrome 136 uses new ALPS codepoint (17613) + TlsExtensions::new( + true, // server_name + true, // status_request + true, // supported_groups + true, // signature_algorithms + true, // application_layer_protocol_negotiation + true, // signed_certificate_timestamp + true, // key_share + true, // psk_key_exchange_modes + true, // supported_versions + Some(vec![CertificateCompressionAlgorithm::Brotli]), // compress_certificate + true, // application_settings + false, // delegated_credentials (Chrome doesn't use) + None, // record_size_limit (Chrome doesn't use) + // Extension order (critical for fingerprinting) + vec![ + ExtensionType::ServerName, + ExtensionType::ExtendedMasterSecret, + ExtensionType::SessionTicket, + ExtensionType::SignatureAlgorithms, + ExtensionType::StatusRequest, + ExtensionType::SupportedGroups, + ExtensionType::ApplicationLayerProtocolNegotiation, + ExtensionType::SignedCertificateTimestamp, + ExtensionType::KeyShare, + ExtensionType::PskKeyExchangeModes, + ExtensionType::SupportedVersions, + ExtensionType::CompressCertificate, + ExtensionType::ApplicationSettings, + ], + ) + .with_new_alps_codepoint(true), + // ECH configuration (GREASE mode) + Some(EchConfig::new( + EchMode::Grease { + hpke_suite: HpkeKemId::DhKemX25519HkdfSha256, + }, + None, + )), + // ALPN protocols + vec![b"h2".to_vec(), b"http/1.1".to_vec()], + ) + } + + /// Chrome 136 HTTP/2 fingerprint + fn http2_fingerprint() -> Http2Fingerprint { + Http2Fingerprint { + pseudo_header_order: vec![ + ":method".to_string(), + ":authority".to_string(), + ":scheme".to_string(), + ":path".to_string(), + ":protocol".to_string(), + ":status".to_string(), + ], + } + } + + /// Chrome 136 HTTP headers + fn headers() -> Vec<(String, String)> { + vec![ + ("sec-ch-ua".to_string(), "\"Chromium\";v=\"136\", \"Google Chrome\";v=\"136\", \"Not.A/Brand\";v=\"99\"".to_string()), + ("sec-ch-ua-mobile".to_string(), "?0".to_string()), + ("sec-ch-ua-platform".to_string(), "\"macOS\"".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36".to_string()), + ("accept".to_string(), "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".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br, zstd".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.9".to_string()), + ("priority".to_string(), "u=0, i".to_string()), + ] + } +} + +/// Chrome 133 fingerprint module +pub mod chrome_133 { + use super::*; + + /// Returns the complete Chrome 133 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Chrome", + "133", + tls_fingerprint(), + http2_fingerprint(), + headers(), + ) + } + + /// Chrome 133 TLS fingerprint + fn tls_fingerprint() -> TlsFingerprint { + TlsFingerprint::new( + // Cipher suites in Chrome 133 preference order + vec![ + CipherSuite::TLS13_AES_128_GCM_SHA256, + CipherSuite::TLS13_AES_256_GCM_SHA384, + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, + ], + // Key exchange groups (includes post-quantum hybrid X25519MLKEM768) + vec![ + KeyExchangeGroup::X25519MLKEM768, + KeyExchangeGroup::X25519, + KeyExchangeGroup::Secp256r1, + KeyExchangeGroup::Secp384r1, + ], + // Signature algorithms + vec![ + SignatureAlgorithm::EcdsaSecp256r1Sha256, + SignatureAlgorithm::RsaPssRsaSha256, + SignatureAlgorithm::RsaPkcs1Sha256, + SignatureAlgorithm::EcdsaSecp384r1Sha384, + SignatureAlgorithm::RsaPssRsaSha384, + SignatureAlgorithm::RsaPkcs1Sha384, + SignatureAlgorithm::RsaPssRsaSha512, + SignatureAlgorithm::RsaPkcs1Sha512, + ], + // TLS extensions configuration + TlsExtensions::new( + true, // server_name + true, // status_request + true, // supported_groups + true, // signature_algorithms + true, // application_layer_protocol_negotiation + true, // signed_certificate_timestamp + true, // key_share + true, // psk_key_exchange_modes + true, // supported_versions + Some(vec![CertificateCompressionAlgorithm::Brotli]), // compress_certificate + true, // application_settings + false, // delegated_credentials (Chrome doesn't use) + None, // record_size_limit (Chrome doesn't use) + // Extension order (critical for fingerprinting) + vec![ + ExtensionType::ServerName, + ExtensionType::ExtendedMasterSecret, + ExtensionType::SessionTicket, + ExtensionType::SignatureAlgorithms, + ExtensionType::StatusRequest, + ExtensionType::SupportedGroups, + ExtensionType::ApplicationLayerProtocolNegotiation, + ExtensionType::SignedCertificateTimestamp, + ExtensionType::KeyShare, + ExtensionType::PskKeyExchangeModes, + ExtensionType::SupportedVersions, + ExtensionType::CompressCertificate, + ExtensionType::ApplicationSettings, + ], + ), + // ECH configuration (GREASE mode) + Some(EchConfig::new( + EchMode::Grease { + hpke_suite: HpkeKemId::DhKemX25519HkdfSha256, + }, + None, + )), + // ALPN protocols + vec![b"h2".to_vec(), b"http/1.1".to_vec()], + ) + } + + /// Chrome 133 HTTP/2 fingerprint + fn http2_fingerprint() -> Http2Fingerprint { + Http2Fingerprint { + pseudo_header_order: vec![ + ":method".to_string(), + ":authority".to_string(), + ":scheme".to_string(), + ":path".to_string(), + ":protocol".to_string(), + ":status".to_string(), + ], + } + } + + /// Chrome 133 HTTP headers + fn headers() -> Vec<(String, String)> { + vec![ + ("sec-ch-ua".to_string(), "\"Not(A:Brand\";v=\"99\", \"Google Chrome\";v=\"133\", \"Chromium\";v=\"133\"".to_string()), + ("sec-ch-ua-mobile".to_string(), "?0".to_string()), + ("sec-ch-ua-platform".to_string(), "\"macOS\"".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36".to_string()), + ("accept".to_string(), "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".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br, zstd".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.9".to_string()), + ("priority".to_string(), "u=0, i".to_string()), + ] + } +} + +/// Chrome 124 fingerprint module +pub mod chrome_124 { + use super::*; + + /// Returns the complete Chrome 124 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Chrome", + "124", + tls_fingerprint(), + http2_fingerprint(), + headers(), + ) + } + + /// Chrome 124 TLS fingerprint + fn tls_fingerprint() -> TlsFingerprint { + TlsFingerprint::new( + // Cipher suites in Chrome 124 preference order + vec![ + CipherSuite::TLS13_AES_128_GCM_SHA256, + CipherSuite::TLS13_AES_256_GCM_SHA384, + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, + ], + // Key exchange groups (X25519Kyber768Draft00 was the pre-MLKEM version) + vec![ + KeyExchangeGroup::X25519MLKEM768, // Maps to X25519Kyber768Draft00 in practice + KeyExchangeGroup::X25519, + KeyExchangeGroup::Secp256r1, + KeyExchangeGroup::Secp384r1, + ], + // Signature algorithms + vec![ + SignatureAlgorithm::EcdsaSecp256r1Sha256, + SignatureAlgorithm::RsaPssRsaSha256, + SignatureAlgorithm::RsaPkcs1Sha256, + SignatureAlgorithm::EcdsaSecp384r1Sha384, + SignatureAlgorithm::RsaPssRsaSha384, + SignatureAlgorithm::RsaPkcs1Sha384, + SignatureAlgorithm::RsaPssRsaSha512, + SignatureAlgorithm::RsaPkcs1Sha512, + ], + // TLS extensions configuration + TlsExtensions::new( + true, // server_name + true, // status_request + true, // supported_groups + true, // signature_algorithms + true, // application_layer_protocol_negotiation + true, // signed_certificate_timestamp + true, // key_share + true, // psk_key_exchange_modes + true, // supported_versions + Some(vec![CertificateCompressionAlgorithm::Brotli]), // compress_certificate + true, // application_settings + false, // delegated_credentials (Chrome doesn't use) + None, // record_size_limit (Chrome doesn't use) + // Extension order (critical for fingerprinting) + vec![ + ExtensionType::ServerName, + ExtensionType::ExtendedMasterSecret, + ExtensionType::SessionTicket, + ExtensionType::SignatureAlgorithms, + ExtensionType::StatusRequest, + ExtensionType::SupportedGroups, + ExtensionType::ApplicationLayerProtocolNegotiation, + ExtensionType::SignedCertificateTimestamp, + ExtensionType::KeyShare, + ExtensionType::PskKeyExchangeModes, + ExtensionType::SupportedVersions, + ExtensionType::CompressCertificate, + ExtensionType::ApplicationSettings, + ], + ), + // ECH configuration (GREASE mode) + Some(EchConfig::new( + EchMode::Grease { + hpke_suite: HpkeKemId::DhKemX25519HkdfSha256, + }, + None, + )), + // ALPN protocols + vec![b"h2".to_vec(), b"http/1.1".to_vec()], + ) + } + + /// Chrome 124 HTTP/2 fingerprint + fn http2_fingerprint() -> Http2Fingerprint { + Http2Fingerprint { + pseudo_header_order: vec![ + ":method".to_string(), + ":authority".to_string(), + ":scheme".to_string(), + ":path".to_string(), + ":protocol".to_string(), + ":status".to_string(), + ], + } + } + + /// Chrome 124 HTTP headers + fn headers() -> Vec<(String, String)> { + vec![ + ("sec-ch-ua".to_string(), "\"Chromium\";v=\"124\", \"Google Chrome\";v=\"124\", \"Not-A.Brand\";v=\"99\"".to_string()), + ("sec-ch-ua-mobile".to_string(), "?0".to_string()), + ("sec-ch-ua-platform".to_string(), "\"macOS\"".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36".to_string()), + ("accept".to_string(), "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".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br, zstd".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.9".to_string()), + ("priority".to_string(), "u=0, i".to_string()), + ] + } +} + +/// Chrome 131 fingerprint module +pub mod chrome_131 { + use super::*; + + /// Returns the complete Chrome 131 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Chrome", + "131", + tls_fingerprint(), + http2_fingerprint(), + headers(), + ) + } + + /// Chrome 131 TLS fingerprint + fn tls_fingerprint() -> TlsFingerprint { + TlsFingerprint::new( + // Cipher suites in Chrome 131 preference order (matching CHROME_CIPHER_SUITES) + vec![ + CipherSuite::TLS13_AES_128_GCM_SHA256, + CipherSuite::TLS13_AES_256_GCM_SHA384, + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite::Grease, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, + ], + // Key exchange groups (includes post-quantum hybrid X25519MLKEM768) + vec![ + KeyExchangeGroup::X25519MLKEM768, + KeyExchangeGroup::X25519, + KeyExchangeGroup::Secp256r1, + KeyExchangeGroup::Secp384r1, + KeyExchangeGroup::Grease, + ], + // Signature algorithms - order must match DEFAULT_SIGNATURE_VERIFICATION_ALGOS + // Note: No SHA1 algorithms for Chrome (matches original implementation) + vec![ + SignatureAlgorithm::EcdsaSecp256r1Sha256, + SignatureAlgorithm::RsaPssRsaSha256, + SignatureAlgorithm::RsaPkcs1Sha256, + SignatureAlgorithm::EcdsaSecp384r1Sha384, + SignatureAlgorithm::RsaPssRsaSha384, + SignatureAlgorithm::RsaPkcs1Sha384, + SignatureAlgorithm::RsaPssRsaSha512, + SignatureAlgorithm::RsaPkcs1Sha512, + ], + // TLS extensions configuration + TlsExtensions::new( + true, // server_name + true, // status_request + true, // supported_groups + true, // signature_algorithms + true, // application_layer_protocol_negotiation + true, // signed_certificate_timestamp + true, // key_share + true, // psk_key_exchange_modes + true, // supported_versions + Some(vec![CertificateCompressionAlgorithm::Brotli]), // compress_certificate + true, // application_settings + false, // delegated_credentials (Chrome doesn't use) + None, // record_size_limit (Chrome doesn't use) + // Extension order (critical for fingerprinting) + vec![ + ExtensionType::ServerName, + ExtensionType::ExtendedMasterSecret, + ExtensionType::SessionTicket, + ExtensionType::SignatureAlgorithms, + ExtensionType::StatusRequest, + ExtensionType::SupportedGroups, + ExtensionType::ApplicationLayerProtocolNegotiation, + ExtensionType::SignedCertificateTimestamp, + ExtensionType::KeyShare, + ExtensionType::PskKeyExchangeModes, + ExtensionType::SupportedVersions, + ExtensionType::CompressCertificate, + ExtensionType::ApplicationSettings, + ], + ), + // ECH configuration (GREASE mode) + Some(EchConfig::new( + EchMode::Grease { + hpke_suite: HpkeKemId::DhKemX25519HkdfSha256, + }, + None, + )), + // ALPN protocols + vec![b"h2".to_vec(), b"http/1.1".to_vec()], + ) + } + + /// Chrome 131 HTTP/2 fingerprint + fn http2_fingerprint() -> Http2Fingerprint { + Http2Fingerprint { + pseudo_header_order: vec![ + ":method".to_string(), + ":authority".to_string(), + ":scheme".to_string(), + ":path".to_string(), + ":protocol".to_string(), + ":status".to_string(), + ], + } + } + + /// Chrome 131 HTTP headers + fn headers() -> Vec<(String, String)> { + vec![ + ("sec-ch-ua".to_string(), "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"".to_string()), + ("sec-ch-ua-mobile".to_string(), "?0".to_string()), + ("sec-ch-ua-platform".to_string(), "\"macOS\"".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36".to_string()), + ("accept".to_string(), "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".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br, zstd".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.9".to_string()), + ("priority".to_string(), "u=0, i".to_string()), + ] + } +} + /// Chrome 125 fingerprint module pub mod chrome_125 { use super::*; diff --git a/impit/src/fingerprint/database/firefox.rs b/impit/src/fingerprint/database/firefox.rs index 17d2a800..46e070f3 100644 --- a/impit/src/fingerprint/database/firefox.rs +++ b/impit/src/fingerprint/database/firefox.rs @@ -2,32 +2,38 @@ use crate::fingerprint::*; -/// Firefox 128 fingerprint module -pub mod firefox_128 { +/// Firefox 135 fingerprint module +pub mod firefox_135 { use super::*; - /// Returns the complete Firefox 128 fingerprint + /// Returns the complete Firefox 135 fingerprint pub fn fingerprint() -> BrowserFingerprint { BrowserFingerprint::new( "Firefox", - "128", + "135", tls_fingerprint(), http2_fingerprint(), headers(), ) } - /// Firefox 128 TLS fingerprint + /// Firefox 135 TLS fingerprint fn tls_fingerprint() -> TlsFingerprint { TlsFingerprint::new( - // Cipher suites in Firefox 128 preference order (17 suites) - // TLS 1.3 cipher suites first (including fake ones for fingerprinting), then TLS 1.2 + // Cipher suites in Firefox 135 preference order + // TLS 1.3 cipher suites first, then TLS 1.2 vec![ // Real TLS 1.3 cipher suites CipherSuite::TLS13_AES_128_GCM_SHA256, CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, CipherSuite::TLS13_AES_256_GCM_SHA384, - // Fake cipher suites for TLS 1.3 fingerprinting (advertised but not used) + // Real TLS 1.2 cipher suites + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, @@ -36,24 +42,158 @@ pub mod firefox_128 { CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, - // Real TLS 1.2 cipher suites + ], + // Key exchange groups (Firefox includes FFDHE groups and post-quantum hybrid) + vec![ + KeyExchangeGroup::X25519MLKEM768, + KeyExchangeGroup::X25519, + KeyExchangeGroup::Secp256r1, + KeyExchangeGroup::Secp384r1, + KeyExchangeGroup::Secp521r1, + KeyExchangeGroup::Ffdhe2048, + KeyExchangeGroup::Ffdhe3072, + ], + // Signature algorithms + vec![ + SignatureAlgorithm::EcdsaSecp256r1Sha256, + SignatureAlgorithm::EcdsaSecp384r1Sha384, + SignatureAlgorithm::EcdsaSecp521r1Sha512, + SignatureAlgorithm::RsaPssRsaSha256, + SignatureAlgorithm::RsaPssRsaSha384, + SignatureAlgorithm::RsaPssRsaSha512, + SignatureAlgorithm::RsaPkcs1Sha256, + SignatureAlgorithm::RsaPkcs1Sha384, + SignatureAlgorithm::RsaPkcs1Sha512, + SignatureAlgorithm::EcdsaSha1Legacy, + SignatureAlgorithm::RsaPkcs1Sha1, + ], + // TLS extensions configuration + TlsExtensions::new( + true, // server_name + true, // status_request + true, // supported_groups + true, // signature_algorithms + true, // application_layer_protocol_negotiation + true, // signed_certificate_timestamp (enabled in Firefox 135) + true, // key_share + true, // psk_key_exchange_modes + true, // supported_versions + Some(vec![ + CertificateCompressionAlgorithm::Zlib, + CertificateCompressionAlgorithm::Brotli, + CertificateCompressionAlgorithm::Zstd, + ]), // compress_certificate (all three algorithms) + false, // application_settings + true, // delegated_credentials (Firefox uses this) + Some(4001), // record_size_limit (Firefox 135 uses 4001) + // Extension order (critical for fingerprinting) + vec![ + ExtensionType::ServerName, + ExtensionType::ExtendedMasterSecret, + ExtensionType::SessionTicket, + ExtensionType::SupportedGroups, + ExtensionType::ApplicationLayerProtocolNegotiation, + ExtensionType::StatusRequest, + ExtensionType::SignedCertificateTimestamp, + ExtensionType::KeyShare, + ExtensionType::SupportedVersions, + ExtensionType::SignatureAlgorithms, + ExtensionType::PskKeyExchangeModes, + ], + ), + // ECH configuration (GREASE mode) + Some(EchConfig::new( + EchMode::Grease { + hpke_suite: HpkeKemId::DhKemX25519HkdfSha256, + }, + None, + )), + // ALPN protocols + vec![b"h2".to_vec(), b"http/1.1".to_vec()], + ) + } + + /// Firefox 135 HTTP/2 fingerprint + fn http2_fingerprint() -> Http2Fingerprint { + Http2Fingerprint { + pseudo_header_order: vec![ + ":method".to_string(), + ":path".to_string(), + ":authority".to_string(), + ":scheme".to_string(), + ":protocol".to_string(), + ":status".to_string(), + ], + } + } + + /// Firefox 135 HTTP headers + fn headers() -> Vec<(String, String)> { + vec![ + ("user-agent".to_string(), "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:135.0) Gecko/20100101 Firefox/135.0".to_string()), + ("accept".to_string(), "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.5".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br, zstd".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("priority".to_string(), "u=0, i".to_string()), + ("te".to_string(), "trailers".to_string()), + ] + } +} + +/// Firefox 133 fingerprint module +pub mod firefox_133 { + use super::*; + + /// Returns the complete Firefox 133 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Firefox", + "133", + tls_fingerprint(), + http2_fingerprint(), + headers(), + ) + } + + /// Firefox 133 TLS fingerprint + fn tls_fingerprint() -> TlsFingerprint { + TlsFingerprint::new( + // Cipher suites in Firefox 133 preference order + vec![ + CipherSuite::TLS13_AES_128_GCM_SHA256, + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS13_AES_256_GCM_SHA384, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, ], - // Key exchange groups (Firefox includes FFDHE groups) + // Key exchange groups (Firefox includes FFDHE groups and post-quantum hybrid) vec![ + KeyExchangeGroup::X25519MLKEM768, KeyExchangeGroup::X25519, KeyExchangeGroup::Secp256r1, KeyExchangeGroup::Secp384r1, + KeyExchangeGroup::Secp521r1, KeyExchangeGroup::Ffdhe2048, KeyExchangeGroup::Ffdhe3072, ], - // Signature algorithms - order must match FIREFOX_SIGNATURE_VERIFICATION_ALGOS mapping - // Note: Ed25519 is included in verification but not in the ClientHello extension + // Signature algorithms vec![ SignatureAlgorithm::EcdsaSecp256r1Sha256, SignatureAlgorithm::EcdsaSecp384r1Sha384, @@ -69,32 +209,179 @@ pub mod firefox_128 { ], // TLS extensions configuration TlsExtensions::new( - true, // server_name - true, // status_request - true, // supported_groups - true, // signature_algorithms - true, // application_layer_protocol_negotiation - false, // signed_certificate_timestamp (Firefox doesn't use this, only Chrome) - true, // key_share - true, // psk_key_exchange_modes - true, // supported_versions - None, // compress_certificate (Firefox doesn't use this) - false, // application_settings - true, // delegated_credentials (Firefox uses this) - Some(16385), // record_size_limit (Firefox uses this) + true, // server_name + true, // status_request + true, // supported_groups + true, // signature_algorithms + true, // application_layer_protocol_negotiation + false, // signed_certificate_timestamp (Firefox 133 doesn't use) + true, // key_share + true, // psk_key_exchange_modes + true, // supported_versions + Some(vec![ + CertificateCompressionAlgorithm::Zlib, + CertificateCompressionAlgorithm::Brotli, + CertificateCompressionAlgorithm::Zstd, + ]), // compress_certificate (all three algorithms) + false, // application_settings + true, // delegated_credentials (Firefox uses this) + Some(4001), // record_size_limit (Firefox 133 uses 4001) // Extension order (critical for fingerprinting) - // Note: Firefox doesn't send SignedCertificateTimestamp vec![ ExtensionType::ServerName, ExtensionType::ExtendedMasterSecret, ExtensionType::SessionTicket, - ExtensionType::SignatureAlgorithms, - ExtensionType::StatusRequest, ExtensionType::SupportedGroups, ExtensionType::ApplicationLayerProtocolNegotiation, + ExtensionType::StatusRequest, ExtensionType::KeyShare, + ExtensionType::SupportedVersions, + ExtensionType::SignatureAlgorithms, ExtensionType::PskKeyExchangeModes, + ], + ), + // ECH configuration (GREASE mode) + Some(EchConfig::new( + EchMode::Grease { + hpke_suite: HpkeKemId::DhKemX25519HkdfSha256, + }, + None, + )), + // ALPN protocols + vec![b"h2".to_vec(), b"http/1.1".to_vec()], + ) + } + + /// Firefox 133 HTTP/2 fingerprint + fn http2_fingerprint() -> Http2Fingerprint { + Http2Fingerprint { + pseudo_header_order: vec![ + ":method".to_string(), + ":path".to_string(), + ":authority".to_string(), + ":scheme".to_string(), + ":protocol".to_string(), + ":status".to_string(), + ], + } + } + + /// Firefox 133 HTTP headers + fn headers() -> Vec<(String, String)> { + vec![ + ("user-agent".to_string(), "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0".to_string()), + ("accept".to_string(), "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.5".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br, zstd".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("priority".to_string(), "u=0, i".to_string()), + ("te".to_string(), "trailers".to_string()), + ] + } +} + +/// Firefox 144 fingerprint module (matches curl_firefox144) +/// Based on Firefox 135 pattern which has the same TLS fingerprint +pub mod firefox_144 { + use super::*; + + /// Returns the complete Firefox 144 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Firefox", + "144", + tls_fingerprint(), + http2_fingerprint(), + headers(), + ) + } + + /// Firefox 144 TLS fingerprint + fn tls_fingerprint() -> TlsFingerprint { + TlsFingerprint::new( + // Cipher suites in Firefox 144 preference order (17 suites) + // TLS 1.3 cipher suites first, then TLS 1.2 + vec![ + // TLS 1.3 cipher suites + CipherSuite::TLS13_AES_128_GCM_SHA256, + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS13_AES_256_GCM_SHA384, + // TLS 1.2 cipher suites + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, + ], + // Key exchange groups (Firefox includes FFDHE groups and post-quantum hybrid) + vec![ + KeyExchangeGroup::X25519MLKEM768, + KeyExchangeGroup::X25519, + KeyExchangeGroup::Secp256r1, + KeyExchangeGroup::Secp384r1, + KeyExchangeGroup::Secp521r1, + KeyExchangeGroup::Ffdhe2048, + KeyExchangeGroup::Ffdhe3072, + ], + // Signature algorithms + vec![ + SignatureAlgorithm::EcdsaSecp256r1Sha256, + SignatureAlgorithm::EcdsaSecp384r1Sha384, + SignatureAlgorithm::EcdsaSecp521r1Sha512, + SignatureAlgorithm::RsaPssRsaSha256, + SignatureAlgorithm::RsaPssRsaSha384, + SignatureAlgorithm::RsaPssRsaSha512, + SignatureAlgorithm::RsaPkcs1Sha256, + SignatureAlgorithm::RsaPkcs1Sha384, + SignatureAlgorithm::RsaPkcs1Sha512, + SignatureAlgorithm::EcdsaSha1Legacy, + SignatureAlgorithm::RsaPkcs1Sha1, + ], + // TLS extensions configuration + TlsExtensions::new( + true, // server_name + true, // status_request + true, // supported_groups + true, // signature_algorithms + true, // application_layer_protocol_negotiation + true, // signed_certificate_timestamp (Firefox 144 uses this) + true, // key_share + true, // psk_key_exchange_modes + true, // supported_versions + Some(vec![ + CertificateCompressionAlgorithm::Zlib, + CertificateCompressionAlgorithm::Brotli, + CertificateCompressionAlgorithm::Zstd, + ]), // compress_certificate (all three algorithms) + false, // application_settings + true, // delegated_credentials (Firefox uses this) + Some(4001), // record_size_limit (Firefox 144 uses 4001) + // Extension order (critical for fingerprinting) + vec![ + ExtensionType::ServerName, + ExtensionType::ExtendedMasterSecret, + ExtensionType::SessionTicket, + ExtensionType::SupportedGroups, + ExtensionType::ApplicationLayerProtocolNegotiation, + ExtensionType::StatusRequest, + ExtensionType::SignedCertificateTimestamp, + ExtensionType::KeyShare, ExtensionType::SupportedVersions, + ExtensionType::SignatureAlgorithms, + ExtensionType::PskKeyExchangeModes, ], ), // ECH configuration (GREASE mode) @@ -109,7 +396,7 @@ pub mod firefox_128 { ) } - /// Firefox 128 HTTP/2 fingerprint + /// Firefox 144 HTTP/2 fingerprint fn http2_fingerprint() -> Http2Fingerprint { Http2Fingerprint { pseudo_header_order: vec![ @@ -123,19 +410,20 @@ pub mod firefox_128 { } } - /// Firefox 128 HTTP headers + /// Firefox 144 HTTP headers fn headers() -> Vec<(String, String)> { vec![ - ("User-Agent".to_string(), "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0".to_string()), - ("Accept".to_string(), "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8".to_string()), - ("Accept-Language".to_string(), "en,cs;q=0.7,en-US;q=0.3".to_string()), - ("Accept-Encoding".to_string(), "gzip, deflate, br, zstd".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:144.0) Gecko/20100101 Firefox/144.0".to_string()), + ("accept".to_string(), "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.5".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br, zstd".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), ("sec-fetch-dest".to_string(), "document".to_string()), ("sec-fetch-mode".to_string(), "navigate".to_string()), ("sec-fetch-site".to_string(), "none".to_string()), ("sec-fetch-user".to_string(), "?1".to_string()), - ("Upgrade-Insecure-Requests".to_string(), "1".to_string()), - ("Priority".to_string(), "u=0, i".to_string()), + ("priority".to_string(), "u=0, i".to_string()), + ("te".to_string(), "trailers".to_string()), ] } } diff --git a/impit/src/fingerprint/mod.rs b/impit/src/fingerprint/mod.rs index 21d5f82f..d5a50bb1 100644 --- a/impit/src/fingerprint/mod.rs +++ b/impit/src/fingerprint/mod.rs @@ -86,9 +86,14 @@ pub struct TlsExtensions { pub supported_versions: bool, pub compress_certificate: Option>, pub application_settings: bool, + /// Use new ALPS codepoint (17613) instead of old (17513). Chrome 136+ uses new codepoint. + pub use_new_alps_codepoint: bool, pub delegated_credentials: bool, pub record_size_limit: Option, pub extension_order: Vec, + /// Whether to enable session tickets (TLS 1.2). Defaults to true. + /// Set to false for browsers like Safari 18.0 that don't send session_ticket extension. + pub session_ticket: bool, } impl TlsExtensions { @@ -122,11 +127,26 @@ impl TlsExtensions { supported_versions, compress_certificate, application_settings, + use_new_alps_codepoint: false, // Default to old codepoint for backward compatibility delegated_credentials, record_size_limit, extension_order, + session_ticket: true, // Default to true for backward compatibility } } + + /// Sets whether to enable session tickets. + pub fn with_session_ticket(mut self, enabled: bool) -> Self { + self.session_ticket = enabled; + self + } + + /// Sets whether to use the new ALPS codepoint (17613) instead of old (17513). + /// Chrome 136+ uses the new codepoint. + pub fn with_new_alps_codepoint(mut self, use_new: bool) -> Self { + self.use_new_alps_codepoint = use_new; + self + } } /// ECH (Encrypted Client Hello) configuration. @@ -227,6 +247,15 @@ impl TlsFingerprint { CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA => { FingerprintCipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA } + CipherSuite::TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA => { + FingerprintCipherSuite::TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + } + CipherSuite::TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA => { + FingerprintCipherSuite::TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + } + CipherSuite::TLS_RSA_WITH_3DES_EDE_CBC_SHA => { + FingerprintCipherSuite::TLS_RSA_WITH_3DES_EDE_CBC_SHA + } CipherSuite::Grease => FingerprintCipherSuite::Grease, }) .collect(); @@ -236,6 +265,7 @@ impl TlsFingerprint { .iter() .map(|kg| match kg { KeyExchangeGroup::X25519 => FingerprintKeyExchangeGroup::X25519, + KeyExchangeGroup::X25519MLKEM768 => FingerprintKeyExchangeGroup::X25519MLKEM768, KeyExchangeGroup::Secp256r1 => FingerprintKeyExchangeGroup::Secp256r1, KeyExchangeGroup::Secp384r1 => FingerprintKeyExchangeGroup::Secp384r1, KeyExchangeGroup::Secp521r1 => FingerprintKeyExchangeGroup::Secp521r1, @@ -293,6 +323,7 @@ impl TlsFingerprint { grease: has_grease, signed_certificate_timestamp: self.extensions.signed_certificate_timestamp, application_settings: self.extensions.application_settings, + use_new_alps_codepoint: self.extensions.use_new_alps_codepoint, delegated_credentials: self.extensions.delegated_credentials, record_size_limit: self.extensions.record_size_limit, renegotiation_info: true, // Common for both browsers diff --git a/impit/src/fingerprint/types.rs b/impit/src/fingerprint/types.rs index 6f1953aa..88d6636c 100644 --- a/impit/src/fingerprint/types.rs +++ b/impit/src/fingerprint/types.rs @@ -26,6 +26,10 @@ pub enum CipherSuite { TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, + // Legacy 3DES cipher suites (Safari) + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, /// GREASE cipher suite for fingerprinting Grease, } @@ -34,6 +38,8 @@ pub enum CipherSuite { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum KeyExchangeGroup { X25519, + /// X25519 with MLKEM768 (post-quantum hybrid) + X25519MLKEM768, Secp256r1, Secp384r1, Secp521r1, @@ -76,6 +82,7 @@ pub enum ExtensionType { MaxFragmentLength, StatusRequest, SupportedGroups, + EcPointFormats, SignatureAlgorithms, UseSrtp, Heartbeat, @@ -95,6 +102,7 @@ pub enum ExtensionType { SignatureAlgorithmsCert, KeyShare, ExtendedMasterSecret, + RenegotiationInfo, SessionTicket, CompressCertificate, ApplicationSettings, diff --git a/impit/src/impit.rs b/impit/src/impit.rs index 8a7437d5..f52f796b 100644 --- a/impit/src/impit.rs +++ b/impit/src/impit.rs @@ -60,7 +60,7 @@ pub enum RedirectBehavior { /// # #[tokio::main] /// # async fn main() { /// let impit = Impit::::builder() -/// .with_fingerprint(fingerprints::firefox_128::fingerprint()) +/// .with_fingerprint(fingerprints::firefox_144::fingerprint()) /// .with_ignore_tls_errors(true) /// .with_proxy("http://localhost:8080".to_string()) /// .with_default_timeout(Duration::from_secs(10)) diff --git a/impit/src/lib.rs b/impit/src/lib.rs index 66ac3829..611faaf4 100644 --- a/impit/src/lib.rs +++ b/impit/src/lib.rs @@ -11,7 +11,7 @@ //! #[tokio::main] //! async fn main() { //! let impit = Impit::::builder() -//! .with_fingerprint(fingerprints::firefox_128::fingerprint()) +//! .with_fingerprint(fingerprints::firefox_144::fingerprint()) //! .with_http3() //! .build() //! .unwrap(); diff --git a/impit/src/tls/mod.rs b/impit/src/tls/mod.rs index 63638b4e..9db22972 100644 --- a/impit/src/tls/mod.rs +++ b/impit/src/tls/mod.rs @@ -6,7 +6,7 @@ use std::sync::{Arc, Mutex, OnceLock}; use crate::fingerprint::TlsFingerprint; use reqwest::Version; use rustls::client::danger::NoVerifier; -use rustls::client::EchGreaseConfig; +use rustls::client::{EchGreaseConfig, Resumption, Tls12Resumption}; use rustls::crypto::CryptoProvider; use rustls_platform_verifier::Verifier; @@ -134,6 +134,8 @@ impl TlsConfigBuilder { let rustls_fingerprint = fp.to_rustls_fingerprint(); let alpn_protocols = fp.alpn_protocols.to_vec(); + let enable_session_ticket = fp.extensions.session_ticket; + let ech_enabled = fp.ech_config.is_some(); let (crypto_provider_arc, verifier) = if let Some(b) = cache_browser { get_or_create_browser_provider_and_verifier(b) @@ -154,17 +156,34 @@ impl TlsConfigBuilder { (provider, verifier) }; - let mut config: rustls::ClientConfig = + // Only enable ECH if the fingerprint requests it + let mut config: rustls::ClientConfig = if ech_enabled { rustls::ClientConfig::builder_with_provider(crypto_provider_arc) .with_ech(get_ech_mode()) .unwrap() .dangerous() .with_custom_certificate_verifier(verifier) .with_tls_fingerprint(rustls_fingerprint) - .with_no_client_auth(); + .with_no_client_auth() + } else { + rustls::ClientConfig::builder_with_provider(crypto_provider_arc) + .with_safe_default_protocol_versions() + .unwrap() + .dangerous() + .with_custom_certificate_verifier(verifier) + .with_tls_fingerprint(rustls_fingerprint) + .with_no_client_auth() + }; config.alpn_protocols = alpn_protocols; + // Configure session resumption based on fingerprint + if !enable_session_ticket { + // Disable session tickets but keep session ID resumption + config.resumption = Resumption::in_memory_sessions(256) + .tls12_resumption(Tls12Resumption::SessionIdOnly); + } + if ignore_tls_errors { config .dangerous() From 40155f0bdbfdeb9c37c84eabb7dd8e426fbf8172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Wed, 28 Jan 2026 11:43:20 +0100 Subject: [PATCH 02/10] chore: drop Safari stubs --- impit-node/index.d.ts | 4 +--- impit-node/src/impit_builder.rs | 5 ----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/impit-node/index.d.ts b/impit-node/index.d.ts index d09e16d2..ef7c1553 100644 --- a/impit-node/index.d.ts +++ b/impit-node/index.d.ts @@ -214,9 +214,7 @@ export type Browser = 'chrome'| 'firefox'| 'firefox133'| 'firefox135'| -'firefox144'| -'safari'| -'safari180'; +'firefox144'; export type HttpMethod = 'GET'| 'POST'| diff --git a/impit-node/src/impit_builder.rs b/impit-node/src/impit_builder.rs index 5ebcb079..f11b86ae 100644 --- a/impit-node/src/impit_builder.rs +++ b/impit-node/src/impit_builder.rs @@ -24,8 +24,6 @@ pub enum Browser { Firefox133, Firefox135, Firefox144, - Safari, - Safari180, } /// Options for configuring an {@link Impit} instance. @@ -112,9 +110,6 @@ impl From for BrowserFingerprint { } Browser::Firefox135 => impit::fingerprint::database::firefox_135::fingerprint(), Browser::Firefox144 => impit::fingerprint::database::firefox_144::fingerprint(), - Browser::Safari | Browser::Safari180 => { - impit::fingerprint::database::safari_18_0::fingerprint() - } } } } From dc5bc6edc25e82d3cf821ed60ed6b3e595c09e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Wed, 28 Jan 2026 13:27:46 +0100 Subject: [PATCH 03/10] feat: add Chrome 100 fingerprint, add TLS padding field support --- impit-node/index.d.ts | 1 + impit-node/src/impit_builder.rs | 2 + impit/src/fingerprint/database.rs | 4 +- impit/src/fingerprint/database/chrome.rs | 135 +++++++++++++++++++++++ impit/src/fingerprint/mod.rs | 10 ++ 5 files changed, 151 insertions(+), 1 deletion(-) diff --git a/impit-node/index.d.ts b/impit-node/index.d.ts index ef7c1553..cab9d751 100644 --- a/impit-node/index.d.ts +++ b/impit-node/index.d.ts @@ -206,6 +206,7 @@ export declare class ImpitResponse { * See {@link ImpitOptions.browser} for more details and usage. */ export type Browser = 'chrome'| +'chrome100'| 'chrome124'| 'chrome125'| 'chrome131'| diff --git a/impit-node/src/impit_builder.rs b/impit-node/src/impit_builder.rs index f11b86ae..9826f213 100644 --- a/impit-node/src/impit_builder.rs +++ b/impit-node/src/impit_builder.rs @@ -15,6 +15,7 @@ use crate::cookies::NodeCookieJar; #[napi(string_enum = "lowercase")] pub enum Browser { Chrome, + Chrome100, Chrome124, Chrome125, Chrome131, @@ -101,6 +102,7 @@ impl From for BrowserFingerprint { Browser::Chrome | Browser::Chrome124 => { impit::fingerprint::database::chrome_124::fingerprint() } + Browser::Chrome100 => impit::fingerprint::database::chrome_100::fingerprint(), Browser::Chrome125 => impit::fingerprint::database::chrome_125::fingerprint(), Browser::Chrome131 => impit::fingerprint::database::chrome_131::fingerprint(), Browser::Chrome136 => impit::fingerprint::database::chrome_136::fingerprint(), diff --git a/impit/src/fingerprint/database.rs b/impit/src/fingerprint/database.rs index 42a9e295..e20b9d25 100644 --- a/impit/src/fingerprint/database.rs +++ b/impit/src/fingerprint/database.rs @@ -5,5 +5,7 @@ mod chrome; mod firefox; -pub use chrome::{chrome_124, chrome_125, chrome_131, chrome_133, chrome_136, chrome_142}; +pub use chrome::{ + chrome_100, chrome_124, chrome_125, chrome_131, chrome_133, chrome_136, chrome_142, +}; pub use firefox::{firefox_133, firefox_135, firefox_144}; diff --git a/impit/src/fingerprint/database/chrome.rs b/impit/src/fingerprint/database/chrome.rs index effd701f..41e2541b 100644 --- a/impit/src/fingerprint/database/chrome.rs +++ b/impit/src/fingerprint/database/chrome.rs @@ -677,6 +677,141 @@ pub mod chrome_131 { } } +/// Chrome 100 fingerprint module +pub mod chrome_100 { + use super::*; + + /// Returns the complete Chrome 100 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Chrome", + "100", + tls_fingerprint(), + http2_fingerprint(), + headers(), + ) + } + + /// Chrome 100 TLS fingerprint + fn tls_fingerprint() -> TlsFingerprint { + TlsFingerprint::new( + // Cipher suites in Chrome 100 preference order + // GREASE cipher at position 1 (first) + vec![ + CipherSuite::Grease, + CipherSuite::TLS13_AES_128_GCM_SHA256, + CipherSuite::TLS13_AES_256_GCM_SHA384, + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, + ], + // Key exchange groups - GREASE first, matching curl_chrome100 + vec![ + KeyExchangeGroup::Grease, + KeyExchangeGroup::X25519, + KeyExchangeGroup::Secp256r1, + KeyExchangeGroup::Secp384r1, + ], + // Signature algorithms + vec![ + SignatureAlgorithm::EcdsaSecp256r1Sha256, + SignatureAlgorithm::RsaPssRsaSha256, + SignatureAlgorithm::RsaPkcs1Sha256, + SignatureAlgorithm::EcdsaSecp384r1Sha384, + SignatureAlgorithm::RsaPssRsaSha384, + SignatureAlgorithm::RsaPkcs1Sha384, + SignatureAlgorithm::RsaPssRsaSha512, + SignatureAlgorithm::RsaPkcs1Sha512, + ], + // TLS extensions configuration + // Chrome 100 uses old ALPS codepoint + TlsExtensions::new( + true, // server_name + true, // status_request + true, // supported_groups + true, // signature_algorithms + true, // application_layer_protocol_negotiation + true, // signed_certificate_timestamp + true, // key_share + true, // psk_key_exchange_modes + true, // supported_versions + Some(vec![CertificateCompressionAlgorithm::Brotli]), // compress_certificate + true, // application_settings (ALPS) + false, // delegated_credentials (Chrome doesn't use) + None, // record_size_limit (Chrome doesn't use) + // Extension order (critical for fingerprinting) - matches curl_chrome100 + vec![ + ExtensionType::Grease, + ExtensionType::ServerName, + ExtensionType::ExtendedMasterSecret, + ExtensionType::RenegotiationInfo, + ExtensionType::SupportedGroups, + ExtensionType::EcPointFormats, + ExtensionType::SessionTicket, + ExtensionType::ApplicationLayerProtocolNegotiation, + ExtensionType::StatusRequest, + ExtensionType::SignatureAlgorithms, + ExtensionType::SignedCertificateTimestamp, + ExtensionType::KeyShare, + ExtensionType::PskKeyExchangeModes, + ExtensionType::SupportedVersions, + ExtensionType::CompressCertificate, + ExtensionType::ApplicationSettings, + ExtensionType::Grease, + ExtensionType::Padding, + ], + ) + .with_padding(true), // Chrome 100 uses padding extension + // No ECH in Chrome 100 + None, + // ALPN protocols + vec![b"h2".to_vec(), b"http/1.1".to_vec()], + ) + } + + /// Chrome 100 HTTP/2 fingerprint + fn http2_fingerprint() -> Http2Fingerprint { + Http2Fingerprint { + pseudo_header_order: vec![ + ":method".to_string(), + ":authority".to_string(), + ":scheme".to_string(), + ":path".to_string(), + ":protocol".to_string(), + ":status".to_string(), + ], + } + } + + /// Chrome 100 HTTP headers + fn headers() -> Vec<(String, String)> { + vec![ + ("sec-ch-ua".to_string(), "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"100\", \"Google Chrome\";v=\"100\"".to_string()), + ("sec-ch-ua-mobile".to_string(), "?0".to_string()), + ("sec-ch-ua-platform".to_string(), "\"Windows\"".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36".to_string()), + ("accept".to_string(), "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.9".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.9".to_string()), + ] + } +} + /// Chrome 125 fingerprint module pub mod chrome_125 { use super::*; diff --git a/impit/src/fingerprint/mod.rs b/impit/src/fingerprint/mod.rs index d5a50bb1..f1681f98 100644 --- a/impit/src/fingerprint/mod.rs +++ b/impit/src/fingerprint/mod.rs @@ -94,6 +94,8 @@ pub struct TlsExtensions { /// Whether to enable session tickets (TLS 1.2). Defaults to true. /// Set to false for browsers like Safari 18.0 that don't send session_ticket extension. pub session_ticket: bool, + /// Whether to send padding extension (RFC7685). + pub padding: bool, } impl TlsExtensions { @@ -132,6 +134,7 @@ impl TlsExtensions { record_size_limit, extension_order, session_ticket: true, // Default to true for backward compatibility + padding: false, // Default to false for backward compatibility } } @@ -147,6 +150,12 @@ impl TlsExtensions { self.use_new_alps_codepoint = use_new; self } + + /// Sets whether to send the padding extension (RFC7685). + pub fn with_padding(mut self, enabled: bool) -> Self { + self.padding = enabled; + self + } } /// ECH (Encrypted Client Hello) configuration. @@ -327,6 +336,7 @@ impl TlsFingerprint { delegated_credentials: self.extensions.delegated_credentials, record_size_limit: self.extensions.record_size_limit, renegotiation_info: true, // Common for both browsers + padding: self.extensions.padding, }; let cert_compression = self.extensions.compress_certificate.clone().map(|algos| { From c154aff17d0d14f029bf6cb1347771f4303d61c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Wed, 28 Jan 2026 13:59:52 +0100 Subject: [PATCH 04/10] feat: add more Chrome fingerprints, make tests work correctly --- impit-node/index.d.ts | 5 + impit-node/src/impit_builder.rs | 10 ++ impit-node/test/fingerprints.test.ts | 5 +- impit/src/fingerprint/database.rs | 3 +- impit/src/fingerprint/database/chrome.rs | 202 ++++++++++++++++++--- impit/src/fingerprint/database/firefox.rs | 204 +++++----------------- impit/src/fingerprint/mod.rs | 10 +- 7 files changed, 248 insertions(+), 191 deletions(-) diff --git a/impit-node/index.d.ts b/impit-node/index.d.ts index cab9d751..ae4b4235 100644 --- a/impit-node/index.d.ts +++ b/impit-node/index.d.ts @@ -207,6 +207,11 @@ export declare class ImpitResponse { */ export type Browser = 'chrome'| 'chrome100'| +'chrome101'| +'chrome104'| +'chrome107'| +'chrome110'| +'chrome116'| 'chrome124'| 'chrome125'| 'chrome131'| diff --git a/impit-node/src/impit_builder.rs b/impit-node/src/impit_builder.rs index 9826f213..a6eef329 100644 --- a/impit-node/src/impit_builder.rs +++ b/impit-node/src/impit_builder.rs @@ -16,6 +16,11 @@ use crate::cookies::NodeCookieJar; pub enum Browser { Chrome, Chrome100, + Chrome101, + Chrome104, + Chrome107, + Chrome110, + Chrome116, Chrome124, Chrome125, Chrome131, @@ -103,6 +108,11 @@ impl From for BrowserFingerprint { impit::fingerprint::database::chrome_124::fingerprint() } Browser::Chrome100 => impit::fingerprint::database::chrome_100::fingerprint(), + Browser::Chrome101 => impit::fingerprint::database::chrome_101::fingerprint(), + Browser::Chrome104 => impit::fingerprint::database::chrome_104::fingerprint(), + Browser::Chrome107 => impit::fingerprint::database::chrome_107::fingerprint(), + Browser::Chrome110 => impit::fingerprint::database::chrome_110::fingerprint(), + Browser::Chrome116 => impit::fingerprint::database::chrome_116::fingerprint(), Browser::Chrome125 => impit::fingerprint::database::chrome_125::fingerprint(), Browser::Chrome131 => impit::fingerprint::database::chrome_131::fingerprint(), Browser::Chrome136 => impit::fingerprint::database::chrome_136::fingerprint(), diff --git a/impit-node/test/fingerprints.test.ts b/impit-node/test/fingerprints.test.ts index a738063c..79a147c9 100644 --- a/impit-node/test/fingerprints.test.ts +++ b/impit-node/test/fingerprints.test.ts @@ -4,10 +4,7 @@ import { Impit, Browser } from '../index.wrapper.js'; describe.each([ [Browser.Chrome, "t13d1516h2_8daaf6152771_02713d6af862"], - [Browser.Chrome125, "t13d1516h2_8daaf6152771_02713d6af862"], - [Browser.Firefox, "t13d1717h2_5b57614c22b0_3cbfd9057e0d"], - [Browser.Firefox128, "t13d1715h2_5b57614c22b0_5c2c66f702b0"], - [Browser.Safari, "t13d2014h2_d5497b48ffb0_7baf387fc6ff"], + [Browser.Firefox, "t13d1716h2_5b57614c22b0_eeeea6562960"], ])(`Browser emulation [%s]`, (browser, ja4) => { test(`[${browser}] emulates JA4 fingerprint`, async () => { diff --git a/impit/src/fingerprint/database.rs b/impit/src/fingerprint/database.rs index e20b9d25..98123494 100644 --- a/impit/src/fingerprint/database.rs +++ b/impit/src/fingerprint/database.rs @@ -6,6 +6,7 @@ mod chrome; mod firefox; pub use chrome::{ - chrome_100, chrome_124, chrome_125, chrome_131, chrome_133, chrome_136, chrome_142, + chrome_100, chrome_101, chrome_104, chrome_107, chrome_110, chrome_116, chrome_124, chrome_125, + chrome_131, chrome_133, chrome_136, chrome_142, }; pub use firefox::{firefox_133, firefox_135, firefox_144}; diff --git a/impit/src/fingerprint/database/chrome.rs b/impit/src/fingerprint/database/chrome.rs index 41e2541b..c523c3b1 100644 --- a/impit/src/fingerprint/database/chrome.rs +++ b/impit/src/fingerprint/database/chrome.rs @@ -693,10 +693,8 @@ pub mod chrome_100 { } /// Chrome 100 TLS fingerprint - fn tls_fingerprint() -> TlsFingerprint { + pub(crate) fn tls_fingerprint() -> TlsFingerprint { TlsFingerprint::new( - // Cipher suites in Chrome 100 preference order - // GREASE cipher at position 1 (first) vec![ CipherSuite::Grease, CipherSuite::TLS13_AES_128_GCM_SHA256, @@ -715,14 +713,12 @@ pub mod chrome_100 { CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, ], - // Key exchange groups - GREASE first, matching curl_chrome100 vec![ KeyExchangeGroup::Grease, KeyExchangeGroup::X25519, KeyExchangeGroup::Secp256r1, KeyExchangeGroup::Secp384r1, ], - // Signature algorithms vec![ SignatureAlgorithm::EcdsaSecp256r1Sha256, SignatureAlgorithm::RsaPssRsaSha256, @@ -733,23 +729,20 @@ pub mod chrome_100 { SignatureAlgorithm::RsaPssRsaSha512, SignatureAlgorithm::RsaPkcs1Sha512, ], - // TLS extensions configuration - // Chrome 100 uses old ALPS codepoint TlsExtensions::new( - true, // server_name - true, // status_request - true, // supported_groups - true, // signature_algorithms - true, // application_layer_protocol_negotiation - true, // signed_certificate_timestamp - true, // key_share - true, // psk_key_exchange_modes - true, // supported_versions - Some(vec![CertificateCompressionAlgorithm::Brotli]), // compress_certificate - true, // application_settings (ALPS) - false, // delegated_credentials (Chrome doesn't use) - None, // record_size_limit (Chrome doesn't use) - // Extension order (critical for fingerprinting) - matches curl_chrome100 + true, + true, + true, + true, + true, + true, + true, + true, + true, + Some(vec![CertificateCompressionAlgorithm::Brotli]), + true, + false, + None, vec![ ExtensionType::Grease, ExtensionType::ServerName, @@ -780,7 +773,7 @@ pub mod chrome_100 { } /// Chrome 100 HTTP/2 fingerprint - fn http2_fingerprint() -> Http2Fingerprint { + pub(crate) fn http2_fingerprint() -> Http2Fingerprint { Http2Fingerprint { pseudo_header_order: vec![ ":method".to_string(), @@ -812,6 +805,171 @@ pub mod chrome_100 { } } +/// Chrome 101 fingerprint module +pub mod chrome_101 { + use super::*; + + /// Returns the complete Chrome 101 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Chrome", + "101", + chrome_100::tls_fingerprint(), + chrome_100::http2_fingerprint(), + headers(), + ) + } + + fn headers() -> Vec<(String, String)> { + vec![ + ("sec-ch-ua".to_string(), "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"101\", \"Google Chrome\";v=\"101\"".to_string()), + ("sec-ch-ua-mobile".to_string(), "?0".to_string()), + ("sec-ch-ua-platform".to_string(), "\"Windows\"".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36".to_string()), + ("accept".to_string(), "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.9".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.9".to_string()), + ] + } +} + +/// Chrome 104 fingerprint module +pub mod chrome_104 { + use super::*; + + /// Returns the complete Chrome 104 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Chrome", + "104", + chrome_100::tls_fingerprint(), + chrome_100::http2_fingerprint(), + headers(), + ) + } + + fn headers() -> Vec<(String, String)> { + vec![ + ("sec-ch-ua".to_string(), "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"104\", \"Google Chrome\";v=\"104\"".to_string()), + ("sec-ch-ua-mobile".to_string(), "?0".to_string()), + ("sec-ch-ua-platform".to_string(), "\"Windows\"".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36".to_string()), + ("accept".to_string(), "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.9".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.9".to_string()), + ] + } +} + +/// Chrome 107 fingerprint module +pub mod chrome_107 { + use super::*; + + /// Returns the complete Chrome 107 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Chrome", + "107", + chrome_100::tls_fingerprint(), + chrome_100::http2_fingerprint(), // TODO Chrome 107 uses different HTTP/2 settings + headers(), + ) + } + + fn headers() -> Vec<(String, String)> { + vec![ + ("sec-ch-ua".to_string(), "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"107\", \"Google Chrome\";v=\"107\"".to_string()), + ("sec-ch-ua-mobile".to_string(), "?0".to_string()), + ("sec-ch-ua-platform".to_string(), "\"Windows\"".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36".to_string()), + ("accept".to_string(), "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.9".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.9".to_string()), + ] + } +} + +/// Chrome 110 fingerprint module +pub mod chrome_110 { + use super::*; + + /// Returns the complete Chrome 110 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Chrome", + "110", + chrome_100::tls_fingerprint(), + chrome_100::http2_fingerprint(), // TODO Chrome 110 uses different HTTP/2 settings + headers(), + ) + } + + fn headers() -> Vec<(String, String)> { + vec![ + ("sec-ch-ua".to_string(), "\"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"".to_string()), + ("sec-ch-ua-mobile".to_string(), "?0".to_string()), + ("sec-ch-ua-platform".to_string(), "\"Windows\"".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36".to_string()), + ("accept".to_string(), "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.9".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.9".to_string()), + ] + } +} + +/// Chrome 116 fingerprint module +pub mod chrome_116 { + use super::*; + + /// Returns the complete Chrome 116 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Chrome", + "116", + chrome_100::tls_fingerprint(), + chrome_100::http2_fingerprint(), // TODO Chrome 116 uses different HTTP/2 settings + headers(), + ) + } + + fn headers() -> Vec<(String, String)> { + vec![ + ("sec-ch-ua".to_string(), "\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"".to_string()), + ("sec-ch-ua-mobile".to_string(), "?0".to_string()), + ("sec-ch-ua-platform".to_string(), "\"Windows\"".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("user-agent".to_string(), "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36".to_string()), + ("accept".to_string(), "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.9".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.9".to_string()), + ] + } +} + /// Chrome 125 fingerprint module pub mod chrome_125 { use super::*; diff --git a/impit/src/fingerprint/database/firefox.rs b/impit/src/fingerprint/database/firefox.rs index 46e070f3..496b4630 100644 --- a/impit/src/fingerprint/database/firefox.rs +++ b/impit/src/fingerprint/database/firefox.rs @@ -2,149 +2,6 @@ use crate::fingerprint::*; -/// Firefox 135 fingerprint module -pub mod firefox_135 { - use super::*; - - /// Returns the complete Firefox 135 fingerprint - pub fn fingerprint() -> BrowserFingerprint { - BrowserFingerprint::new( - "Firefox", - "135", - tls_fingerprint(), - http2_fingerprint(), - headers(), - ) - } - - /// Firefox 135 TLS fingerprint - fn tls_fingerprint() -> TlsFingerprint { - TlsFingerprint::new( - // Cipher suites in Firefox 135 preference order - // TLS 1.3 cipher suites first, then TLS 1.2 - vec![ - // Real TLS 1.3 cipher suites - CipherSuite::TLS13_AES_128_GCM_SHA256, - CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, - CipherSuite::TLS13_AES_256_GCM_SHA384, - // Real TLS 1.2 cipher suites - CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, - CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, - CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, - ], - // Key exchange groups (Firefox includes FFDHE groups and post-quantum hybrid) - vec![ - KeyExchangeGroup::X25519MLKEM768, - KeyExchangeGroup::X25519, - KeyExchangeGroup::Secp256r1, - KeyExchangeGroup::Secp384r1, - KeyExchangeGroup::Secp521r1, - KeyExchangeGroup::Ffdhe2048, - KeyExchangeGroup::Ffdhe3072, - ], - // Signature algorithms - vec![ - SignatureAlgorithm::EcdsaSecp256r1Sha256, - SignatureAlgorithm::EcdsaSecp384r1Sha384, - SignatureAlgorithm::EcdsaSecp521r1Sha512, - SignatureAlgorithm::RsaPssRsaSha256, - SignatureAlgorithm::RsaPssRsaSha384, - SignatureAlgorithm::RsaPssRsaSha512, - SignatureAlgorithm::RsaPkcs1Sha256, - SignatureAlgorithm::RsaPkcs1Sha384, - SignatureAlgorithm::RsaPkcs1Sha512, - SignatureAlgorithm::EcdsaSha1Legacy, - SignatureAlgorithm::RsaPkcs1Sha1, - ], - // TLS extensions configuration - TlsExtensions::new( - true, // server_name - true, // status_request - true, // supported_groups - true, // signature_algorithms - true, // application_layer_protocol_negotiation - true, // signed_certificate_timestamp (enabled in Firefox 135) - true, // key_share - true, // psk_key_exchange_modes - true, // supported_versions - Some(vec![ - CertificateCompressionAlgorithm::Zlib, - CertificateCompressionAlgorithm::Brotli, - CertificateCompressionAlgorithm::Zstd, - ]), // compress_certificate (all three algorithms) - false, // application_settings - true, // delegated_credentials (Firefox uses this) - Some(4001), // record_size_limit (Firefox 135 uses 4001) - // Extension order (critical for fingerprinting) - vec![ - ExtensionType::ServerName, - ExtensionType::ExtendedMasterSecret, - ExtensionType::SessionTicket, - ExtensionType::SupportedGroups, - ExtensionType::ApplicationLayerProtocolNegotiation, - ExtensionType::StatusRequest, - ExtensionType::SignedCertificateTimestamp, - ExtensionType::KeyShare, - ExtensionType::SupportedVersions, - ExtensionType::SignatureAlgorithms, - ExtensionType::PskKeyExchangeModes, - ], - ), - // ECH configuration (GREASE mode) - Some(EchConfig::new( - EchMode::Grease { - hpke_suite: HpkeKemId::DhKemX25519HkdfSha256, - }, - None, - )), - // ALPN protocols - vec![b"h2".to_vec(), b"http/1.1".to_vec()], - ) - } - - /// Firefox 135 HTTP/2 fingerprint - fn http2_fingerprint() -> Http2Fingerprint { - Http2Fingerprint { - pseudo_header_order: vec![ - ":method".to_string(), - ":path".to_string(), - ":authority".to_string(), - ":scheme".to_string(), - ":protocol".to_string(), - ":status".to_string(), - ], - } - } - - /// Firefox 135 HTTP headers - fn headers() -> Vec<(String, String)> { - vec![ - ("user-agent".to_string(), "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:135.0) Gecko/20100101 Firefox/135.0".to_string()), - ("accept".to_string(), "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8".to_string()), - ("accept-language".to_string(), "en-US,en;q=0.5".to_string()), - ("accept-encoding".to_string(), "gzip, deflate, br, zstd".to_string()), - ("upgrade-insecure-requests".to_string(), "1".to_string()), - ("sec-fetch-dest".to_string(), "document".to_string()), - ("sec-fetch-mode".to_string(), "navigate".to_string()), - ("sec-fetch-site".to_string(), "none".to_string()), - ("sec-fetch-user".to_string(), "?1".to_string()), - ("priority".to_string(), "u=0, i".to_string()), - ("te".to_string(), "trailers".to_string()), - ] - } -} - /// Firefox 133 fingerprint module pub mod firefox_133 { use super::*; @@ -284,33 +141,32 @@ pub mod firefox_133 { } } -/// Firefox 144 fingerprint module (matches curl_firefox144) -/// Based on Firefox 135 pattern which has the same TLS fingerprint -pub mod firefox_144 { +/// Firefox 135 fingerprint module +pub mod firefox_135 { use super::*; - /// Returns the complete Firefox 144 fingerprint + /// Returns the complete Firefox 135 fingerprint pub fn fingerprint() -> BrowserFingerprint { BrowserFingerprint::new( "Firefox", - "144", + "135", tls_fingerprint(), http2_fingerprint(), headers(), ) } - /// Firefox 144 TLS fingerprint - fn tls_fingerprint() -> TlsFingerprint { + /// Firefox 135 TLS fingerprint + pub(crate) fn tls_fingerprint() -> TlsFingerprint { TlsFingerprint::new( - // Cipher suites in Firefox 144 preference order (17 suites) + // Cipher suites in Firefox 135 preference order // TLS 1.3 cipher suites first, then TLS 1.2 vec![ - // TLS 1.3 cipher suites + // Real TLS 1.3 cipher suites CipherSuite::TLS13_AES_128_GCM_SHA256, CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, CipherSuite::TLS13_AES_256_GCM_SHA384, - // TLS 1.2 cipher suites + // Real TLS 1.2 cipher suites CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, @@ -357,7 +213,7 @@ pub mod firefox_144 { true, // supported_groups true, // signature_algorithms true, // application_layer_protocol_negotiation - true, // signed_certificate_timestamp (Firefox 144 uses this) + true, // signed_certificate_timestamp (enabled in Firefox 135) true, // key_share true, // psk_key_exchange_modes true, // supported_versions @@ -368,7 +224,7 @@ pub mod firefox_144 { ]), // compress_certificate (all three algorithms) false, // application_settings true, // delegated_credentials (Firefox uses this) - Some(4001), // record_size_limit (Firefox 144 uses 4001) + Some(4001), // record_size_limit (Firefox 135 uses 4001) // Extension order (critical for fingerprinting) vec![ ExtensionType::ServerName, @@ -396,8 +252,8 @@ pub mod firefox_144 { ) } - /// Firefox 144 HTTP/2 fingerprint - fn http2_fingerprint() -> Http2Fingerprint { + /// Firefox 135 HTTP/2 fingerprint + pub(crate) fn http2_fingerprint() -> Http2Fingerprint { Http2Fingerprint { pseudo_header_order: vec![ ":method".to_string(), @@ -410,6 +266,40 @@ pub mod firefox_144 { } } + /// Firefox 135 HTTP headers + fn headers() -> Vec<(String, String)> { + vec![ + ("user-agent".to_string(), "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:135.0) Gecko/20100101 Firefox/135.0".to_string()), + ("accept".to_string(), "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8".to_string()), + ("accept-language".to_string(), "en-US,en;q=0.5".to_string()), + ("accept-encoding".to_string(), "gzip, deflate, br, zstd".to_string()), + ("upgrade-insecure-requests".to_string(), "1".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("priority".to_string(), "u=0, i".to_string()), + ("te".to_string(), "trailers".to_string()), + ] + } +} + +/// Firefox 144 fingerprint module (matches curl_firefox144) +/// Based on Firefox 135 pattern which has the same TLS fingerprint +pub mod firefox_144 { + use super::*; + + /// Returns the complete Firefox 144 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Firefox", + "144", + firefox_135::tls_fingerprint(), + firefox_135::http2_fingerprint(), + headers(), + ) + } + /// Firefox 144 HTTP headers fn headers() -> Vec<(String, String)> { vec![ diff --git a/impit/src/fingerprint/mod.rs b/impit/src/fingerprint/mod.rs index f1681f98..4cc1c7b3 100644 --- a/impit/src/fingerprint/mod.rs +++ b/impit/src/fingerprint/mod.rs @@ -129,29 +129,25 @@ impl TlsExtensions { supported_versions, compress_certificate, application_settings, - use_new_alps_codepoint: false, // Default to old codepoint for backward compatibility + use_new_alps_codepoint: false, delegated_credentials, record_size_limit, extension_order, - session_ticket: true, // Default to true for backward compatibility - padding: false, // Default to false for backward compatibility + session_ticket: true, + padding: false, } } - /// Sets whether to enable session tickets. pub fn with_session_ticket(mut self, enabled: bool) -> Self { self.session_ticket = enabled; self } - /// Sets whether to use the new ALPS codepoint (17613) instead of old (17513). - /// Chrome 136+ uses the new codepoint. pub fn with_new_alps_codepoint(mut self, use_new: bool) -> Self { self.use_new_alps_codepoint = use_new; self } - /// Sets whether to send the padding extension (RFC7685). pub fn with_padding(mut self, enabled: bool) -> Self { self.padding = enabled; self From 3b8d556ea0cfdbbd2a4ab1863f485bcde45cedaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Wed, 28 Jan 2026 15:04:51 +0100 Subject: [PATCH 05/10] feat: enable new fingerprints in Python bindings --- impit-python/src/async_client.rs | 28 ++++++++++++++++++++++++++-- impit-python/src/client.rs | 28 ++++++++++++++++++++++++++-- impit/src/fingerprint/mod.rs | 9 --------- impit/src/fingerprint/types.rs | 4 ---- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/impit-python/src/async_client.rs b/impit-python/src/async_client.rs index 5ad027c4..afafaead 100644 --- a/impit-python/src/async_client.rs +++ b/impit-python/src/async_client.rs @@ -58,9 +58,33 @@ impl AsyncClient { let builder = match browser { Some(browser) => match browser.to_lowercase().as_str() { - "chrome" => builder + "chrome" | "chrome124" => builder .with_fingerprint(impit::fingerprint::database::chrome_125::fingerprint()), - "firefox" => builder + "chrome100" => builder + .with_fingerprint(impit::fingerprint::database::chrome_100::fingerprint()), + "chrome101" => builder + .with_fingerprint(impit::fingerprint::database::chrome_101::fingerprint()), + "chrome104" => builder + .with_fingerprint(impit::fingerprint::database::chrome_104::fingerprint()), + "chrome107" => builder + .with_fingerprint(impit::fingerprint::database::chrome_107::fingerprint()), + "chrome110" => builder + .with_fingerprint(impit::fingerprint::database::chrome_110::fingerprint()), + "chrome116" => builder + .with_fingerprint(impit::fingerprint::database::chrome_116::fingerprint()), + "chrome125" => builder + .with_fingerprint(impit::fingerprint::database::chrome_125::fingerprint()), + "chrome131" => builder + .with_fingerprint(impit::fingerprint::database::chrome_131::fingerprint()), + "chrome136" => builder + .with_fingerprint(impit::fingerprint::database::chrome_136::fingerprint()), + "chrome142" => builder + .with_fingerprint(impit::fingerprint::database::chrome_142::fingerprint()), + "firefox133" | "firefox" => builder + .with_fingerprint(impit::fingerprint::database::firefox_133::fingerprint()), + "firefox135" => builder + .with_fingerprint(impit::fingerprint::database::firefox_135::fingerprint()), + "firefox144" => builder .with_fingerprint(impit::fingerprint::database::firefox_144::fingerprint()), _ => { return Err(PyErr::new::( diff --git a/impit-python/src/client.rs b/impit-python/src/client.rs index c3e20252..d73b6844 100644 --- a/impit-python/src/client.rs +++ b/impit-python/src/client.rs @@ -55,9 +55,33 @@ impl Client { let builder = match browser { Some(browser) => match browser.to_lowercase().as_str() { - "chrome" => builder + "chrome" | "chrome124" => builder .with_fingerprint(impit::fingerprint::database::chrome_125::fingerprint()), - "firefox" => builder + "chrome100" => builder + .with_fingerprint(impit::fingerprint::database::chrome_100::fingerprint()), + "chrome101" => builder + .with_fingerprint(impit::fingerprint::database::chrome_101::fingerprint()), + "chrome104" => builder + .with_fingerprint(impit::fingerprint::database::chrome_104::fingerprint()), + "chrome107" => builder + .with_fingerprint(impit::fingerprint::database::chrome_107::fingerprint()), + "chrome110" => builder + .with_fingerprint(impit::fingerprint::database::chrome_110::fingerprint()), + "chrome116" => builder + .with_fingerprint(impit::fingerprint::database::chrome_116::fingerprint()), + "chrome125" => builder + .with_fingerprint(impit::fingerprint::database::chrome_125::fingerprint()), + "chrome131" => builder + .with_fingerprint(impit::fingerprint::database::chrome_131::fingerprint()), + "chrome136" => builder + .with_fingerprint(impit::fingerprint::database::chrome_136::fingerprint()), + "chrome142" => builder + .with_fingerprint(impit::fingerprint::database::chrome_142::fingerprint()), + "firefox133" | "firefox" => builder + .with_fingerprint(impit::fingerprint::database::firefox_133::fingerprint()), + "firefox135" => builder + .with_fingerprint(impit::fingerprint::database::firefox_135::fingerprint()), + "firefox144" => builder .with_fingerprint(impit::fingerprint::database::firefox_144::fingerprint()), _ => panic!("Unsupported browser"), }, diff --git a/impit/src/fingerprint/mod.rs b/impit/src/fingerprint/mod.rs index 4cc1c7b3..54e2d453 100644 --- a/impit/src/fingerprint/mod.rs +++ b/impit/src/fingerprint/mod.rs @@ -252,15 +252,6 @@ impl TlsFingerprint { CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA => { FingerprintCipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA } - CipherSuite::TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA => { - FingerprintCipherSuite::TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA - } - CipherSuite::TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA => { - FingerprintCipherSuite::TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA - } - CipherSuite::TLS_RSA_WITH_3DES_EDE_CBC_SHA => { - FingerprintCipherSuite::TLS_RSA_WITH_3DES_EDE_CBC_SHA - } CipherSuite::Grease => FingerprintCipherSuite::Grease, }) .collect(); diff --git a/impit/src/fingerprint/types.rs b/impit/src/fingerprint/types.rs index 88d6636c..00028f03 100644 --- a/impit/src/fingerprint/types.rs +++ b/impit/src/fingerprint/types.rs @@ -26,10 +26,6 @@ pub enum CipherSuite { TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, - // Legacy 3DES cipher suites (Safari) - TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_RSA_WITH_3DES_EDE_CBC_SHA, /// GREASE cipher suite for fingerprinting Grease, } From ce56231ce9428fe64e22bf9d0cfce61d0f22c126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Wed, 28 Jan 2026 15:37:57 +0100 Subject: [PATCH 06/10] feat: add Firefox 128 fingerprint --- impit-node/index.d.ts | 1 + impit-node/src/impit_builder.rs | 6 +- impit-node/test/fingerprints.test.ts | 2 +- impit-python/src/client.rs | 8 +- impit/src/fingerprint/database.rs | 2 +- impit/src/fingerprint/database/firefox.rs | 139 ++++++++++++++++++++++ 6 files changed, 150 insertions(+), 8 deletions(-) diff --git a/impit-node/index.d.ts b/impit-node/index.d.ts index ae4b4235..a2498482 100644 --- a/impit-node/index.d.ts +++ b/impit-node/index.d.ts @@ -218,6 +218,7 @@ export type Browser = 'chrome'| 'chrome136'| 'chrome142'| 'firefox'| +'firefox128'| 'firefox133'| 'firefox135'| 'firefox144'; diff --git a/impit-node/src/impit_builder.rs b/impit-node/src/impit_builder.rs index a6eef329..9bf9c0b3 100644 --- a/impit-node/src/impit_builder.rs +++ b/impit-node/src/impit_builder.rs @@ -27,6 +27,7 @@ pub enum Browser { Chrome136, Chrome142, Firefox, + Firefox128, Firefox133, Firefox135, Firefox144, @@ -117,9 +118,10 @@ impl From for BrowserFingerprint { Browser::Chrome131 => impit::fingerprint::database::chrome_131::fingerprint(), Browser::Chrome136 => impit::fingerprint::database::chrome_136::fingerprint(), Browser::Chrome142 => impit::fingerprint::database::chrome_142::fingerprint(), - Browser::Firefox | Browser::Firefox133 => { - impit::fingerprint::database::firefox_133::fingerprint() + Browser::Firefox | Browser::Firefox128 => { + impit::fingerprint::database::firefox_128::fingerprint() } + Browser::Firefox133 => impit::fingerprint::database::firefox_133::fingerprint(), Browser::Firefox135 => impit::fingerprint::database::firefox_135::fingerprint(), Browser::Firefox144 => impit::fingerprint::database::firefox_144::fingerprint(), } diff --git a/impit-node/test/fingerprints.test.ts b/impit-node/test/fingerprints.test.ts index 79a147c9..5d4de32b 100644 --- a/impit-node/test/fingerprints.test.ts +++ b/impit-node/test/fingerprints.test.ts @@ -4,7 +4,7 @@ import { Impit, Browser } from '../index.wrapper.js'; describe.each([ [Browser.Chrome, "t13d1516h2_8daaf6152771_02713d6af862"], - [Browser.Firefox, "t13d1716h2_5b57614c22b0_eeeea6562960"], + [Browser.Firefox, "t13d1715h2_5b57614c22b0_5c2c66f702b0"], ])(`Browser emulation [%s]`, (browser, ja4) => { test(`[${browser}] emulates JA4 fingerprint`, async () => { diff --git a/impit-python/src/client.rs b/impit-python/src/client.rs index d73b6844..ef90352a 100644 --- a/impit-python/src/client.rs +++ b/impit-python/src/client.rs @@ -55,7 +55,7 @@ impl Client { let builder = match browser { Some(browser) => match browser.to_lowercase().as_str() { - "chrome" | "chrome124" => builder + "chrome" | "chrome125" => builder .with_fingerprint(impit::fingerprint::database::chrome_125::fingerprint()), "chrome100" => builder .with_fingerprint(impit::fingerprint::database::chrome_100::fingerprint()), @@ -69,15 +69,15 @@ impl Client { .with_fingerprint(impit::fingerprint::database::chrome_110::fingerprint()), "chrome116" => builder .with_fingerprint(impit::fingerprint::database::chrome_116::fingerprint()), - "chrome125" => builder - .with_fingerprint(impit::fingerprint::database::chrome_125::fingerprint()), "chrome131" => builder .with_fingerprint(impit::fingerprint::database::chrome_131::fingerprint()), "chrome136" => builder .with_fingerprint(impit::fingerprint::database::chrome_136::fingerprint()), "chrome142" => builder .with_fingerprint(impit::fingerprint::database::chrome_142::fingerprint()), - "firefox133" | "firefox" => builder + "firefox128" | "firefox" => builder + .with_fingerprint(impit::fingerprint::database::firefox_128::fingerprint()), + "firefox133" => builder .with_fingerprint(impit::fingerprint::database::firefox_133::fingerprint()), "firefox135" => builder .with_fingerprint(impit::fingerprint::database::firefox_135::fingerprint()), diff --git a/impit/src/fingerprint/database.rs b/impit/src/fingerprint/database.rs index 98123494..afd9da6d 100644 --- a/impit/src/fingerprint/database.rs +++ b/impit/src/fingerprint/database.rs @@ -9,4 +9,4 @@ pub use chrome::{ chrome_100, chrome_101, chrome_104, chrome_107, chrome_110, chrome_116, chrome_124, chrome_125, chrome_131, chrome_133, chrome_136, chrome_142, }; -pub use firefox::{firefox_133, firefox_135, firefox_144}; +pub use firefox::{firefox_128, firefox_133, firefox_135, firefox_144}; diff --git a/impit/src/fingerprint/database/firefox.rs b/impit/src/fingerprint/database/firefox.rs index 496b4630..dd7bc475 100644 --- a/impit/src/fingerprint/database/firefox.rs +++ b/impit/src/fingerprint/database/firefox.rs @@ -2,6 +2,145 @@ use crate::fingerprint::*; +/// Firefox 128 fingerprint module +pub mod firefox_128 { + use super::*; + + /// Returns the complete Firefox 128 fingerprint + pub fn fingerprint() -> BrowserFingerprint { + BrowserFingerprint::new( + "Firefox", + "128", + tls_fingerprint(), + http2_fingerprint(), + headers(), + ) + } + + /// Firefox 128 TLS fingerprint + fn tls_fingerprint() -> TlsFingerprint { + TlsFingerprint::new( + // Cipher suites in Firefox 128 preference order (17 suites) + // TLS 1.3 cipher suites first (including fake ones for fingerprinting), then TLS 1.2 + vec![ + // Real TLS 1.3 cipher suites + CipherSuite::TLS13_AES_128_GCM_SHA256, + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS13_AES_256_GCM_SHA384, + // Fake cipher suites for TLS 1.3 fingerprinting (advertised but not used) + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite::TLS_RSA_WITH_AES_256_CBC_SHA, + // Real TLS 1.2 cipher suites + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + ], + // Key exchange groups (Firefox includes FFDHE groups) + vec![ + KeyExchangeGroup::X25519, + KeyExchangeGroup::Secp256r1, + KeyExchangeGroup::Secp384r1, + KeyExchangeGroup::Ffdhe2048, + KeyExchangeGroup::Ffdhe3072, + ], + // Signature algorithms - order must match FIREFOX_SIGNATURE_VERIFICATION_ALGOS mapping + // Note: Ed25519 is included in verification but not in the ClientHello extension + vec![ + SignatureAlgorithm::EcdsaSecp256r1Sha256, + SignatureAlgorithm::EcdsaSecp384r1Sha384, + SignatureAlgorithm::EcdsaSecp521r1Sha512, + SignatureAlgorithm::RsaPssRsaSha256, + SignatureAlgorithm::RsaPssRsaSha384, + SignatureAlgorithm::RsaPssRsaSha512, + SignatureAlgorithm::RsaPkcs1Sha256, + SignatureAlgorithm::RsaPkcs1Sha384, + SignatureAlgorithm::RsaPkcs1Sha512, + SignatureAlgorithm::EcdsaSha1Legacy, + SignatureAlgorithm::RsaPkcs1Sha1, + ], + // TLS extensions configuration + TlsExtensions::new( + true, // server_name + true, // status_request + true, // supported_groups + true, // signature_algorithms + true, // application_layer_protocol_negotiation + false, // signed_certificate_timestamp (Firefox doesn't use this, only Chrome) + true, // key_share + true, // psk_key_exchange_modes + true, // supported_versions + None, // compress_certificate (Firefox doesn't use this) + false, // application_settings + true, // delegated_credentials (Firefox uses this) + Some(16385), // record_size_limit (Firefox uses this) + // Extension order (critical for fingerprinting) + // Note: Firefox doesn't send SignedCertificateTimestamp + vec![ + ExtensionType::ServerName, + ExtensionType::ExtendedMasterSecret, + ExtensionType::SessionTicket, + ExtensionType::SignatureAlgorithms, + ExtensionType::StatusRequest, + ExtensionType::SupportedGroups, + ExtensionType::ApplicationLayerProtocolNegotiation, + ExtensionType::KeyShare, + ExtensionType::PskKeyExchangeModes, + ExtensionType::SupportedVersions, + ], + ), + // ECH configuration (GREASE mode) + Some(EchConfig::new( + EchMode::Grease { + hpke_suite: HpkeKemId::DhKemX25519HkdfSha256, + }, + None, + )), + // ALPN protocols + vec![b"h2".to_vec(), b"http/1.1".to_vec()], + ) + } + + /// Firefox 128 HTTP/2 fingerprint + fn http2_fingerprint() -> Http2Fingerprint { + Http2Fingerprint { + pseudo_header_order: vec![ + ":method".to_string(), + ":path".to_string(), + ":authority".to_string(), + ":scheme".to_string(), + ":protocol".to_string(), + ":status".to_string(), + ], + } + } + + /// Firefox 128 HTTP headers + fn headers() -> Vec<(String, String)> { + vec![ + ("User-Agent".to_string(), "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0".to_string()), + ("Accept".to_string(), "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8".to_string()), + ("Accept-Language".to_string(), "en,cs;q=0.7,en-US;q=0.3".to_string()), + ("Accept-Encoding".to_string(), "gzip, deflate, br, zstd".to_string()), + ("sec-fetch-dest".to_string(), "document".to_string()), + ("sec-fetch-mode".to_string(), "navigate".to_string()), + ("sec-fetch-site".to_string(), "none".to_string()), + ("sec-fetch-user".to_string(), "?1".to_string()), + ("Upgrade-Insecure-Requests".to_string(), "1".to_string()), + ("Priority".to_string(), "u=0, i".to_string()), + ] + } +} + + /// Firefox 133 fingerprint module pub mod firefox_133 { use super::*; From 4e2ff7a90c28e49eeb7c33b86eace534d085a5e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Wed, 28 Jan 2026 15:41:28 +0100 Subject: [PATCH 07/10] chore: fix rustls dependency (link to github) --- Cargo.lock | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 782c5032..dc3119b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2556,6 +2556,7 @@ dependencies = [ [[package]] name = "rustls" version = "0.23.36" +source = "git+https://github.com/apify/rustls?rev=4823cdb16098938eccf91af1329e9d5b681e3554#4823cdb16098938eccf91af1329e9d5b681e3554" dependencies = [ "aws-lc-rs", "brotli", diff --git a/Cargo.toml b/Cargo.toml index 8f4505bf..f7de7c43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ [patch.crates-io] h2 = { git = "https://github.com/apify/h2", rev = "7f393a728a8db07cabb1b78d2094772b33943b9a" } -rustls = { path = "../rustls/rustls" } +rustls = { git = "https://github.com/apify/rustls", rev="4823cdb16098938eccf91af1329e9d5b681e3554" } [profile.release] strip = true # Automatically strip symbols from the binary. From a2f7d0cdf55c428381196c4912bd7234fe41e0b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Wed, 28 Jan 2026 15:45:41 +0100 Subject: [PATCH 08/10] chore: fix formatting errors, add new fingerprints to the typing file --- impit-python/python/impit/impit.pyi | 3 +-- impit-python/test/setup_proxy.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/impit-python/python/impit/impit.pyi b/impit-python/python/impit/impit.pyi index 1e13afce..7b38966e 100644 --- a/impit-python/python/impit/impit.pyi +++ b/impit-python/python/impit/impit.pyi @@ -7,8 +7,7 @@ from collections.abc import Iterator, AsyncIterator from contextlib import AbstractAsyncContextManager, AbstractContextManager -Browser = Literal['chrome', 'firefox'] - +Browser = Literal['chrome', 'firefox', 'chrome125', 'chrome100', 'chrome101', 'chrome104', 'chrome107', 'chrome110', 'chrome116', 'chrome131', 'chrome136', 'chrome142', 'firefox128', 'firefox133', 'firefox135', 'firefox144'] class HTTPError(Exception): """Represents an HTTP-related error.""" diff --git a/impit-python/test/setup_proxy.py b/impit-python/test/setup_proxy.py index c2967b52..5d6bfb91 100644 --- a/impit-python/test/setup_proxy.py +++ b/impit-python/test/setup_proxy.py @@ -9,7 +9,7 @@ def start_proxy_server(port: int = 3002) -> typing.Callable[[], None]: def run_proxy_server(stop_event: threading.Event) -> None: server = pproxy.Server(f'http://0.0.0.0:{port}') - args = dict(rserver=[], verbose=print) + args = {'rserver': [], 'verbose': print} loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) From e7f49078b9e3335b2050e495e6fbaf02d89e1870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Wed, 28 Jan 2026 15:49:39 +0100 Subject: [PATCH 09/10] chore: run formatter --- impit/src/fingerprint/database/firefox.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/impit/src/fingerprint/database/firefox.rs b/impit/src/fingerprint/database/firefox.rs index dd7bc475..dd9e36ac 100644 --- a/impit/src/fingerprint/database/firefox.rs +++ b/impit/src/fingerprint/database/firefox.rs @@ -140,7 +140,6 @@ pub mod firefox_128 { } } - /// Firefox 133 fingerprint module pub mod firefox_133 { use super::*; From f34ab7ce3e7d94b1064ebc50dc78680611195963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jind=C5=99ich=20B=C3=A4r?= Date: Wed, 28 Jan 2026 16:16:25 +0100 Subject: [PATCH 10/10] fix(python): use correct Firefox fingerprints in `AsyncClient` --- impit-python/src/async_client.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/impit-python/src/async_client.rs b/impit-python/src/async_client.rs index afafaead..422f8cd5 100644 --- a/impit-python/src/async_client.rs +++ b/impit-python/src/async_client.rs @@ -80,7 +80,9 @@ impl AsyncClient { .with_fingerprint(impit::fingerprint::database::chrome_136::fingerprint()), "chrome142" => builder .with_fingerprint(impit::fingerprint::database::chrome_142::fingerprint()), - "firefox133" | "firefox" => builder + "firefox128" | "firefox" => builder + .with_fingerprint(impit::fingerprint::database::firefox_128::fingerprint()), + "firefox133" => builder .with_fingerprint(impit::fingerprint::database::firefox_133::fingerprint()), "firefox135" => builder .with_fingerprint(impit::fingerprint::database::firefox_135::fingerprint()),