1mod cache;
26mod composer;
27mod diff;
28
29pub use cache::{clear_tls_cache, get_or_build_tls, tls_cache_len};
30pub use composer::HeaderComposer;
31pub use diff::{FingerprintDiff, diff_fingerprints};
32
33#[cfg(feature = "emulation")]
39pub fn tls_fingerprint_from_preset(preset: TlsPreset) -> TlsFingerprint {
40 crate::emulation::device::tls_fingerprint_from_preset(preset)
42}
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
50pub enum TlsPreset {
51 ChromeBase,
53 ChromeEchGrease,
55 ChromePermute,
57 ChromePermuteEch,
59 ChromePermuteEchPsk,
61 ChromeKyber,
63 ChromeMlkem768,
65 FirefoxBase,
67 FirefoxEchGrease,
69 SafariBase,
71 OkHttpBase,
73}
74
75#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
77pub enum Curve {
78 X25519,
79 X25519Kyber768Draft00,
80 X25519MLKEM768,
81 Secp256r1,
82 Secp384r1,
83 Secp521r1,
84}
85
86impl Curve {
87 pub const fn openssl_name(&self) -> &'static str {
89 match self {
90 Curve::X25519 => "X25519",
91 Curve::X25519Kyber768Draft00 => "X25519Kyber768Draft00",
92 Curve::X25519MLKEM768 => "X25519MLKEM768",
93 Curve::Secp256r1 => "P-256",
94 Curve::Secp384r1 => "P-384",
95 Curve::Secp521r1 => "P-521",
96 }
97 }
98}
99
100impl std::fmt::Display for Curve {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 f.write_str(self.openssl_name())
103 }
104}
105
106#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
108pub enum CipherSuite {
109 Tls13Aes128GcmSha256,
111 Tls13Aes256GcmSha384,
112 Tls13ChaCha20Poly1305Sha256,
113 EcdheEcdsaWithAes128GcmSha256,
115 EcdheEcdsaWithAes256GcmSha384,
116 EcdheEcdsaWithChaCha20Poly1305Sha256,
117 EcdheEcdsaWithAes128CbcSha,
118 EcdheEcdsaWithAes256CbcSha,
119 EcdheRsaWithAes128GcmSha256,
121 EcdheRsaWithAes256GcmSha384,
122 EcdheRsaWithChaCha20Poly1305Sha256,
123 EcdheRsaWithAes128CbcSha,
124 EcdheRsaWithAes256CbcSha,
125 RsaWithAes128GcmSha256,
127 RsaWithAes256GcmSha384,
128 RsaWithAes128CbcSha,
129 RsaWithAes256CbcSha,
130}
131
132impl CipherSuite {
133 pub const fn openssl_name(&self) -> &'static str {
135 match self {
136 CipherSuite::Tls13Aes128GcmSha256 => "TLS_AES_128_GCM_SHA256",
137 CipherSuite::Tls13Aes256GcmSha384 => "TLS_AES_256_GCM_SHA384",
138 CipherSuite::Tls13ChaCha20Poly1305Sha256 => "TLS_CHACHA20_POLY1305_SHA256",
139 CipherSuite::EcdheEcdsaWithAes128GcmSha256 => "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
140 CipherSuite::EcdheEcdsaWithAes256GcmSha384 => "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
141 CipherSuite::EcdheEcdsaWithChaCha20Poly1305Sha256 => {
142 "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"
143 }
144 CipherSuite::EcdheEcdsaWithAes128CbcSha => "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
145 CipherSuite::EcdheEcdsaWithAes256CbcSha => "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
146 CipherSuite::EcdheRsaWithAes128GcmSha256 => "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
147 CipherSuite::EcdheRsaWithAes256GcmSha384 => "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
148 CipherSuite::EcdheRsaWithChaCha20Poly1305Sha256 => {
149 "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
150 }
151 CipherSuite::EcdheRsaWithAes128CbcSha => "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
152 CipherSuite::EcdheRsaWithAes256CbcSha => "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
153 CipherSuite::RsaWithAes128GcmSha256 => "TLS_RSA_WITH_AES_128_GCM_SHA256",
154 CipherSuite::RsaWithAes256GcmSha384 => "TLS_RSA_WITH_AES_256_GCM_SHA384",
155 CipherSuite::RsaWithAes128CbcSha => "TLS_RSA_WITH_AES_128_CBC_SHA",
156 CipherSuite::RsaWithAes256CbcSha => "TLS_RSA_WITH_AES_256_CBC_SHA",
157 }
158 }
159}
160
161#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
163pub enum SignatureAlgorithm {
164 EcdsaSecp256r1Sha256,
165 RsaPssRsaeSha256,
166 RsaPkcs1Sha256,
167 EcdsaSecp384r1Sha384,
168 RsaPssRsaeSha384,
169 RsaPkcs1Sha384,
170 RsaPssRsaeSha512,
171 RsaPkcs1Sha512,
172}
173
174impl SignatureAlgorithm {
175 pub const fn openssl_name(&self) -> &'static str {
177 match self {
178 SignatureAlgorithm::EcdsaSecp256r1Sha256 => "ecdsa_secp256r1_sha256",
179 SignatureAlgorithm::RsaPssRsaeSha256 => "rsa_pss_rsae_sha256",
180 SignatureAlgorithm::RsaPkcs1Sha256 => "rsa_pkcs1_sha256",
181 SignatureAlgorithm::EcdsaSecp384r1Sha384 => "ecdsa_secp384r1_sha384",
182 SignatureAlgorithm::RsaPssRsaeSha384 => "rsa_pss_rsae_sha384",
183 SignatureAlgorithm::RsaPkcs1Sha384 => "rsa_pkcs1_sha384",
184 SignatureAlgorithm::RsaPssRsaeSha512 => "rsa_pss_rsae_sha512",
185 SignatureAlgorithm::RsaPkcs1Sha512 => "rsa_pkcs1_sha512",
186 }
187 }
188}
189
190#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
192pub enum CertCompression {
193 Brotli,
194 Zlib,
195 Zstd,
196}
197
198#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
200pub enum EchMode {
201 Disabled,
202 Grease,
203}
204
205#[derive(Clone, Debug, PartialEq, Eq, Hash)]
207pub struct TlsFingerprint {
208 pub curves: Vec<Curve>,
210 pub cipher_suites: Vec<CipherSuite>,
212 pub signature_algorithms: Vec<SignatureAlgorithm>,
214 pub permute_extensions: bool,
216 pub ech_mode: EchMode,
218 pub pre_shared_key: bool,
220 pub cert_compression: Vec<CertCompression>,
222 pub alps_use_new_codepoint: bool,
224}
225
226impl Default for TlsFingerprint {
227 fn default() -> Self {
228 Self {
229 curves: vec![Curve::X25519, Curve::Secp256r1, Curve::Secp384r1],
230 cipher_suites: vec![
231 CipherSuite::Tls13Aes128GcmSha256,
232 CipherSuite::Tls13Aes256GcmSha384,
233 CipherSuite::Tls13ChaCha20Poly1305Sha256,
234 CipherSuite::EcdheEcdsaWithAes128GcmSha256,
235 CipherSuite::EcdheRsaWithAes128GcmSha256,
236 CipherSuite::EcdheEcdsaWithAes256GcmSha384,
237 CipherSuite::EcdheRsaWithAes256GcmSha384,
238 CipherSuite::EcdheEcdsaWithChaCha20Poly1305Sha256,
239 CipherSuite::EcdheRsaWithChaCha20Poly1305Sha256,
240 CipherSuite::EcdheRsaWithAes128CbcSha,
241 CipherSuite::EcdheRsaWithAes256CbcSha,
242 CipherSuite::RsaWithAes128GcmSha256,
243 CipherSuite::RsaWithAes256GcmSha384,
244 CipherSuite::RsaWithAes128CbcSha,
245 CipherSuite::RsaWithAes256CbcSha,
246 ],
247 signature_algorithms: vec![
248 SignatureAlgorithm::EcdsaSecp256r1Sha256,
249 SignatureAlgorithm::RsaPssRsaeSha256,
250 SignatureAlgorithm::RsaPkcs1Sha256,
251 SignatureAlgorithm::EcdsaSecp384r1Sha384,
252 SignatureAlgorithm::RsaPssRsaeSha384,
253 SignatureAlgorithm::RsaPkcs1Sha384,
254 SignatureAlgorithm::RsaPssRsaeSha512,
255 SignatureAlgorithm::RsaPkcs1Sha512,
256 ],
257 permute_extensions: false,
258 ech_mode: EchMode::Disabled,
259 pre_shared_key: false,
260 cert_compression: vec![CertCompression::Brotli],
261 alps_use_new_codepoint: false,
262 }
263 }
264}
265
266impl TlsFingerprint {
267 pub fn curves_string(&self) -> String {
269 self.curves
270 .iter()
271 .map(|c| c.openssl_name())
272 .collect::<Vec<_>>()
273 .join(":")
274 }
275
276 pub fn cipher_suites_string(&self) -> String {
278 self.cipher_suites
279 .iter()
280 .map(|c| c.openssl_name())
281 .collect::<Vec<_>>()
282 .join(":")
283 }
284
285 pub fn signature_algorithms_string(&self) -> String {
287 self.signature_algorithms
288 .iter()
289 .map(|a| a.openssl_name())
290 .collect::<Vec<_>>()
291 .join(":")
292 }
293}
294
295#[derive(Clone, Debug, PartialEq, Eq, Hash)]
297pub struct Http2Fingerprint {
298 pub initial_window_size: u32,
300 pub initial_connection_window_size: u32,
302 pub max_concurrent_streams: Option<u32>,
304 pub max_header_list_size: u32,
306 pub header_table_size: u32,
308 pub enable_push: Option<bool>,
310 pub pseudo_header_order: PseudoHeaderOrder,
312}
313
314#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
316pub enum PseudoHeaderOrder {
317 MethodAuthoritySchemePath,
319}
320
321impl Default for Http2Fingerprint {
322 fn default() -> Self {
323 Self {
324 initial_window_size: 6291456,
325 initial_connection_window_size: 15728640,
326 max_concurrent_streams: Some(1000),
327 max_header_list_size: 262144,
328 header_table_size: 65536,
329 enable_push: None,
330 pseudo_header_order: PseudoHeaderOrder::MethodAuthoritySchemePath,
331 }
332 }
333}
334
335#[derive(Clone, Debug, PartialEq, Eq)]
340pub struct BrowserFingerprint {
341 pub name: &'static str,
343 pub version: &'static str,
345 pub tls: TlsFingerprint,
347 pub http2: Http2Fingerprint,
349 pub headers: Vec<(&'static str, &'static str)>,
351}
352
353#[cfg(feature = "emulation-serde")]
354impl serde::Serialize for BrowserFingerprint {
355 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
356 where
357 S: serde::Serializer,
358 {
359 use serde::ser::SerializeStruct;
360 let mut state = serializer.serialize_struct("BrowserFingerprint", 5)?;
361 state.serialize_field("name", &self.name)?;
362 state.serialize_field("version", &self.version)?;
363 state.serialize_field("tls_curves", &self.tls.curves_string())?;
364 state.serialize_field("tls_cipher_suites", &self.tls.cipher_suites_string())?;
365 state.serialize_field(
366 "tls_signature_algorithms",
367 &self.tls.signature_algorithms_string(),
368 )?;
369 state.serialize_field("tls_permute_extensions", &self.tls.permute_extensions)?;
370 state.serialize_field("tls_ech_mode", &format!("{:?}", self.tls.ech_mode))?;
371 state.serialize_field("tls_pre_shared_key", &self.tls.pre_shared_key)?;
372 state.serialize_field(
373 "tls_alps_use_new_codepoint",
374 &self.tls.alps_use_new_codepoint,
375 )?;
376 state.serialize_field("h2_initial_window_size", &self.http2.initial_window_size)?;
377 state.serialize_field(
378 "h2_max_concurrent_streams",
379 &self.http2.max_concurrent_streams,
380 )?;
381 state.serialize_field("h2_max_header_list_size", &self.http2.max_header_list_size)?;
382 state.serialize_field("h2_header_table_size", &self.http2.header_table_size)?;
383 state.end()
384 }
385}
386
387impl BrowserFingerprint {
388 pub fn new(
390 name: &'static str,
391 version: &'static str,
392 tls: TlsFingerprint,
393 http2: Http2Fingerprint,
394 headers: Vec<(&'static str, &'static str)>,
395 ) -> Self {
396 Self {
397 name,
398 version,
399 tls,
400 http2,
401 headers,
402 }
403 }
404}