175 lines
5.3 KiB
Rust
175 lines
5.3 KiB
Rust
//! Dynamic SNI & TLS Mimicry Engine
|
|
|
|
use std::collections::HashMap;
|
|
|
|
/// Geo-based SNI target selection
|
|
pub struct MimicryEngine {
|
|
geo_sni_map: HashMap<String, Vec<String>>,
|
|
}
|
|
|
|
impl MimicryEngine {
|
|
pub fn new() -> Self {
|
|
let mut geo_sni_map = HashMap::new();
|
|
|
|
// Default geo-SNI mappings for contextual mimicry
|
|
geo_sni_map.insert(
|
|
"RU".into(),
|
|
vec![
|
|
"gosuslugi.ru".into(),
|
|
"sberbank.ru".into(),
|
|
"yandex.ru".into(),
|
|
],
|
|
);
|
|
geo_sni_map.insert(
|
|
"NO".into(),
|
|
vec!["bankid.no".into(), "vipps.no".into(), "altinn.no".into()],
|
|
);
|
|
geo_sni_map.insert(
|
|
"DE".into(),
|
|
vec![
|
|
"sparkasse.de".into(),
|
|
"deutsche-bank.de".into(),
|
|
"bund.de".into(),
|
|
],
|
|
);
|
|
geo_sni_map.insert(
|
|
"US".into(),
|
|
vec![
|
|
"apple.com".into(),
|
|
"microsoft.com".into(),
|
|
"amazon.com".into(),
|
|
],
|
|
);
|
|
geo_sni_map.insert(
|
|
"CN".into(),
|
|
vec!["qq.com".into(), "baidu.com".into(), "taobao.com".into()],
|
|
);
|
|
|
|
Self { geo_sni_map }
|
|
}
|
|
|
|
/// Select SNI based on geo-location
|
|
pub fn select_sni(&self, country_code: &str) -> Option<&str> {
|
|
self.geo_sni_map
|
|
.get(country_code)
|
|
.and_then(|list| list.first().map(|s| s.as_str()))
|
|
}
|
|
|
|
/// Get random SNI from geo list for anti-fingerprinting
|
|
pub fn random_sni(&self, country_code: &str) -> Option<&str> {
|
|
self.geo_sni_map.get(country_code).and_then(|list| {
|
|
if list.is_empty() {
|
|
None
|
|
} else {
|
|
let idx = rand::random::<usize>() % list.len();
|
|
Some(list[idx].as_str())
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Add custom geo-SNI mapping
|
|
pub fn add_mapping(&mut self, country: String, domains: Vec<String>) {
|
|
self.geo_sni_map.insert(country, domains);
|
|
}
|
|
}
|
|
|
|
impl Default for MimicryEngine {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/// TLS ClientHello builder for REALITY-like mimicry
|
|
pub struct TlsHelloBuilder {
|
|
sni: String,
|
|
random_session_id: bool,
|
|
}
|
|
|
|
impl TlsHelloBuilder {
|
|
pub fn new(sni: impl Into<String>) -> Self {
|
|
Self {
|
|
sni: sni.into(),
|
|
random_session_id: true,
|
|
}
|
|
}
|
|
|
|
/// Build minimal TLS 1.3 ClientHello-like header
|
|
pub fn build(&self) -> Vec<u8> {
|
|
let mut hello = Vec::with_capacity(256);
|
|
|
|
// TLS record header
|
|
hello.push(0x16); // Handshake
|
|
hello.extend_from_slice(&[0x03, 0x01]); // TLS 1.0 for compat
|
|
|
|
// Placeholder for length (will update)
|
|
let len_pos = hello.len();
|
|
hello.extend_from_slice(&[0x00, 0x00]);
|
|
|
|
// Handshake header
|
|
hello.push(0x01); // ClientHello
|
|
let hs_len_pos = hello.len();
|
|
hello.extend_from_slice(&[0x00, 0x00, 0x00]); // length placeholder
|
|
|
|
// Client version (TLS 1.2 presented, 1.3 in extensions)
|
|
hello.extend_from_slice(&[0x03, 0x03]);
|
|
|
|
// Random (32 bytes)
|
|
let random: [u8; 32] = rand::random();
|
|
hello.extend_from_slice(&random);
|
|
|
|
// Session ID (32 bytes if random)
|
|
if self.random_session_id {
|
|
hello.push(32);
|
|
let session_id: [u8; 32] = rand::random();
|
|
hello.extend_from_slice(&session_id);
|
|
} else {
|
|
hello.push(0);
|
|
}
|
|
|
|
// Cipher suites (TLS 1.3 suites)
|
|
hello.extend_from_slice(&[0x00, 0x06]); // 3 suites
|
|
hello.extend_from_slice(&[0x13, 0x01]); // TLS_AES_128_GCM_SHA256
|
|
hello.extend_from_slice(&[0x13, 0x02]); // TLS_AES_256_GCM_SHA384
|
|
hello.extend_from_slice(&[0x13, 0x03]); // TLS_CHACHA20_POLY1305_SHA256
|
|
|
|
// Compression (null)
|
|
hello.extend_from_slice(&[0x01, 0x00]);
|
|
|
|
// Extensions with SNI
|
|
let ext_start = hello.len();
|
|
hello.extend_from_slice(&[0x00, 0x00]); // ext length placeholder
|
|
|
|
// SNI extension
|
|
hello.extend_from_slice(&[0x00, 0x00]); // type: server_name
|
|
let sni_bytes = self.sni.as_bytes();
|
|
let sni_ext_len = sni_bytes.len() + 5;
|
|
hello.extend_from_slice(&(sni_ext_len as u16).to_be_bytes());
|
|
hello.extend_from_slice(&((sni_bytes.len() + 3) as u16).to_be_bytes());
|
|
hello.push(0x00); // host_name type
|
|
hello.extend_from_slice(&(sni_bytes.len() as u16).to_be_bytes());
|
|
hello.extend_from_slice(sni_bytes);
|
|
|
|
// supported_versions extension
|
|
hello.extend_from_slice(&[0x00, 0x2b]); // type
|
|
hello.extend_from_slice(&[0x00, 0x03]); // length
|
|
hello.push(0x02); // versions length
|
|
hello.extend_from_slice(&[0x03, 0x04]); // TLS 1.3
|
|
|
|
// Update lengths
|
|
let ext_len = hello.len() - ext_start - 2;
|
|
hello[ext_start] = (ext_len >> 8) as u8;
|
|
hello[ext_start + 1] = ext_len as u8;
|
|
|
|
let total_len = hello.len() - 5;
|
|
hello[len_pos] = (total_len >> 8) as u8;
|
|
hello[len_pos + 1] = total_len as u8;
|
|
|
|
let hs_len = hello.len() - hs_len_pos - 3;
|
|
hello[hs_len_pos] = 0;
|
|
hello[hs_len_pos + 1] = (hs_len >> 8) as u8;
|
|
hello[hs_len_pos + 2] = hs_len as u8;
|
|
|
|
hello
|
|
}
|
|
}
|