Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions impit-node/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ export type Browser = 'chrome'|
'firefox133'|
'firefox135'|
'firefox144'|
'firefox158'|
'okhttp'|
'okhttp3'|
'okhttp4'|
Expand Down
2 changes: 2 additions & 0 deletions impit-node/src/impit_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub enum Browser {
Firefox133,
Firefox135,
Firefox144,
Firefox158,
OkHttp,
OkHttp3,
OkHttp4,
Expand Down Expand Up @@ -143,6 +144,7 @@ impl From<Browser> for BrowserFingerprint {
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::Firefox158 => impit::fingerprint::database::firefox_158::fingerprint(),
Browser::OkHttp3 => impit::fingerprint::database::okhttp3::fingerprint(),
Browser::OkHttp | Browser::OkHttp4 => impit::fingerprint::database::okhttp4::fingerprint(),
Browser::OkHttp5 => impit::fingerprint::database::okhttp5::fingerprint(),
Expand Down
1 change: 1 addition & 0 deletions impit-python/python/impit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
'firefox133',
'firefox135',
'firefox144',
'firefox158',
'okhttp',
'okhttp3',
'okhttp4',
Expand Down
1 change: 1 addition & 0 deletions impit-python/src/fingerprint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub(crate) fn fingerprint_by_name(browser: &str) -> PyResult<BrowserFingerprint>
"firefox133" => database::firefox_133::fingerprint(),
"firefox135" => database::firefox_135::fingerprint(),
"firefox144" => database::firefox_144::fingerprint(),
"firefox158" => database::firefox_158::fingerprint(),
"okhttp3" => database::okhttp3::fingerprint(),
"okhttp" | "okhttp4" => database::okhttp4::fingerprint(),
"okhttp5" => database::okhttp5::fingerprint(),
Expand Down
3 changes: 2 additions & 1 deletion impit/src/fingerprint/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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, chrome_151,
};
pub use firefox::{firefox_128, firefox_133, firefox_135, firefox_144};
pub use firefox::{firefox_128, firefox_133, firefox_135, firefox_144, firefox_158};
pub use okhttp::{okhttp3, okhttp4, okhttp5};
pub use safari::ios_18;

Expand Down Expand Up @@ -71,6 +71,7 @@ mod tests {
("firefox_133", firefox_133::fingerprint()),
("firefox_135", firefox_135::fingerprint()),
("firefox_144", firefox_144::fingerprint()),
("firefox_158", firefox_158::fingerprint()),
("okhttp3", okhttp3::fingerprint()),
("okhttp4", okhttp4::fingerprint()),
("okhttp5", okhttp5::fingerprint()),
Expand Down
149 changes: 148 additions & 1 deletion impit/src/fingerprint/database/firefox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,17 +482,162 @@ pub mod firefox_144 {
}
}

/// Firefox 158 fingerprint module
///
/// Captured from Firefox 158.0 (official Mozilla build, Windows) on real sites,
/// with the traffic decrypted in Wireshark via `SSLKEYLOGFILE`, and
/// cross-checked against <https://tls.peet.ws>. Compared with Firefox 144, it
/// no longer offers the `0xc009`/`0xc00a` cipher suites or the FFDHE groups,
/// and the second `accept-language` entry has `q=0.9`. The HTTP/2 settings are
/// unchanged. Its user agent is the Windows one; firefox133–144 use macOS's.
pub mod firefox_158 {
use super::*;

/// Returns the complete Firefox 158 fingerprint
pub fn fingerprint() -> BrowserFingerprint {
BrowserFingerprint::new(
"Firefox",
"158",
tls_fingerprint(),
firefox_135::http2_fingerprint(),
headers(),
)
}

/// Firefox 158 TLS fingerprint
fn tls_fingerprint() -> TlsFingerprint {
TlsFingerprint::new(
// Cipher suites in Firefox 158 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_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 (post-quantum hybrid first, no FFDHE)
vec![
KeyExchangeGroup::X25519MLKEM768,
KeyExchangeGroup::X25519,
KeyExchangeGroup::Secp256r1,
KeyExchangeGroup::Secp384r1,
KeyExchangeGroup::Secp521r1,
],
// 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
true, // key_share
true, // psk_key_exchange_modes
true, // supported_versions
Some(vec![
CertificateCompressionAlgorithm::Zlib,
CertificateCompressionAlgorithm::Brotli,
CertificateCompressionAlgorithm::Zstd,
]), // compress_certificate
false, // application_settings
true, // delegated_credentials
Some(16385), // record_size_limit (0x4001)
// Extension order (critical for fingerprinting)
vec![
ExtensionType::ServerName,
ExtensionType::ExtendedMasterSecret,
ExtensionType::RenegotiationInfo,
ExtensionType::SupportedGroups,
ExtensionType::EcPointFormats,
ExtensionType::SessionTicket,
ExtensionType::ApplicationLayerProtocolNegotiation,
ExtensionType::StatusRequest,
ExtensionType::DelegatedCredentials,
ExtensionType::SignedCertificateTimestamp,
ExtensionType::KeyShare,
ExtensionType::SupportedVersions,
ExtensionType::SignatureAlgorithms,
ExtensionType::PskKeyExchangeModes,
ExtensionType::RecordSizeLimit,
ExtensionType::CompressCertificate,
],
),
// 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 158 HTTP headers
fn headers() -> Vec<(String, String)> {
vec![
(
"user-agent".to_string(),
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:158.0) Gecko/20100101 Firefox/158.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.9".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()),
]
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::tls::TlsConfig;
use std::sync::Arc;

// JA3 strings (before hashing) of real Firefox's cold TCP ClientHello, from decrypted Wireshark captures of
// Firefox 128, 133, 135 and 144 on Windows (October 2026). Firefox 144's is the same as 135's.
// Firefox 128, 133, 135, 144 and 158 on Windows (October 2026). Firefox 144's is the same as 135's.
const JA3_128: &str = "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-34-51-43-13-45-28-65037,29-23-24-25-256-257,0";
const JA3_133: &str = "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-34-51-43-13-45-28-27-65037,4588-29-23-24-25-256-257,0";
const JA3_135: &str = "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-34-18-51-43-13-45-28-27-65037,4588-29-23-24-25-256-257,0";
const JA3_158: &str = "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-34-18-51-43-13-45-28-27-65037,4588-29-23-24-25,0";

/// The ClientHello that impit sends for `fingerprint`: legacy version, cipher suites, and each extension's type
/// and body in wire order.
Expand Down Expand Up @@ -574,6 +719,7 @@ mod tests {
("firefox_133", firefox_133::fingerprint(), JA3_133),
("firefox_135", firefox_135::fingerprint(), JA3_135),
("firefox_144", firefox_144::fingerprint(), JA3_135),
("firefox_158", firefox_158::fingerprint(), JA3_158),
];

let mut mismatches = Vec::new();
Expand All @@ -600,6 +746,7 @@ mod tests {
("firefox_133", firefox_133::fingerprint()),
("firefox_135", firefox_135::fingerprint()),
("firefox_144", firefox_144::fingerprint()),
("firefox_158", firefox_158::fingerprint()),
];

let mut wrong = Vec::new();
Expand Down
Loading