From 9a907a247050c6945d42735a55efb961d93ed65f Mon Sep 17 00:00:00 2001 From: Alexey <247128645+axkurcom@users.noreply.github.com> Date: Fri, 20 Feb 2026 12:55:26 +0300 Subject: [PATCH] TLS-F: added Emu + Cache --- src/main.rs | 1 + src/proxy/client.rs | 8 +++- src/proxy/handshake.rs | 3 ++ src/tls_front/emulator.rs | 99 +++++++++++++++++++++++++++++++++++++++ src/tls_front/mod.rs | 7 +++ 5 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 src/tls_front/emulator.rs create mode 100644 src/tls_front/mod.rs diff --git a/src/main.rs b/src/main.rs index 3f1393a..4581a0c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ mod proxy; mod stats; mod stream; mod transport; +mod tls_front; mod util; use crate::config::{LogLevel, ProxyConfig}; diff --git a/src/proxy/client.rs b/src/proxy/client.rs index 37f1ff7..8a8ae81 100644 --- a/src/proxy/client.rs +++ b/src/proxy/client.rs @@ -31,6 +31,7 @@ use crate::stats::{ReplayChecker, Stats}; use crate::stream::{BufferPool, CryptoReader, CryptoWriter}; use crate::transport::middle_proxy::MePool; use crate::transport::{UpstreamManager, configure_client_socket, parse_proxy_protocol}; +use crate::tls_front::TlsFrontCache; use crate::proxy::direct_relay::handle_via_direct; use crate::proxy::handshake::{HandshakeSuccess, handle_mtproto_handshake, handle_tls_handshake}; @@ -47,6 +48,7 @@ pub async fn handle_client_stream( buffer_pool: Arc, rng: Arc, me_pool: Option>, + tls_cache: Option>, ip_tracker: Arc, ) -> Result<()> where @@ -111,7 +113,7 @@ where let (mut tls_reader, tls_writer, _tls_user) = match handle_tls_handshake( &handshake, read_half, write_half, real_peer, - &config, &replay_checker, &rng, + &config, &replay_checker, &rng, tls_cache.clone(), ).await { HandshakeResult::Success(result) => result, HandshakeResult::BadClient { reader, writer } => { @@ -224,6 +226,7 @@ pub struct RunningClientHandler { buffer_pool: Arc, rng: Arc, me_pool: Option>, + tls_cache: Option>, ip_tracker: Arc, } @@ -238,6 +241,7 @@ impl ClientHandler { buffer_pool: Arc, rng: Arc, me_pool: Option>, + tls_cache: Option>, ip_tracker: Arc, ) -> RunningClientHandler { RunningClientHandler { @@ -250,6 +254,7 @@ impl ClientHandler { buffer_pool, rng, me_pool, + tls_cache, ip_tracker, } } @@ -367,6 +372,7 @@ impl RunningClientHandler { &config, &replay_checker, &self.rng, + self.tls_cache.clone(), ) .await { diff --git a/src/proxy/handshake.rs b/src/proxy/handshake.rs index 0023b7a..157b5b0 100644 --- a/src/proxy/handshake.rs +++ b/src/proxy/handshake.rs @@ -1,6 +1,7 @@ //! MTProto Handshake use std::net::SocketAddr; +use std::sync::Arc; use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; use tracing::{debug, warn, trace, info}; use zeroize::Zeroize; @@ -12,6 +13,8 @@ use crate::stream::{FakeTlsReader, FakeTlsWriter, CryptoReader, CryptoWriter}; use crate::error::{ProxyError, HandshakeResult}; use crate::stats::ReplayChecker; use crate::config::ProxyConfig; +use crate::tls_front::{TlsFrontCache, emulator}; +use crate::tls_front::types::CachedTlsData; /// Result of successful handshake /// diff --git a/src/tls_front/emulator.rs b/src/tls_front/emulator.rs new file mode 100644 index 0000000..0c2dda0 --- /dev/null +++ b/src/tls_front/emulator.rs @@ -0,0 +1,99 @@ +use crate::crypto::{sha256_hmac, SecureRandom}; +use crate::protocol::constants::{ + TLS_RECORD_APPLICATION, TLS_RECORD_CHANGE_CIPHER, TLS_RECORD_HANDSHAKE, TLS_VERSION, +}; +use crate::protocol::tls::{TLS_DIGEST_LEN, TLS_DIGEST_POS, gen_fake_x25519_key}; +use crate::tls_front::types::CachedTlsData; + +/// Build a ServerHello + CCS + ApplicationData sequence using cached TLS metadata. +pub fn build_emulated_server_hello( + secret: &[u8], + client_digest: &[u8; TLS_DIGEST_LEN], + session_id: &[u8], + cached: &CachedTlsData, + rng: &SecureRandom, +) -> Vec { + // --- ServerHello --- + let mut extensions = Vec::new(); + // KeyShare (x25519) + let key = gen_fake_x25519_key(rng); + extensions.extend_from_slice(&0x0033u16.to_be_bytes()); // key_share + extensions.extend_from_slice(&(2 + 2 + 32u16).to_be_bytes()); // len + extensions.extend_from_slice(&0x001du16.to_be_bytes()); // X25519 + extensions.extend_from_slice(&(32u16).to_be_bytes()); + extensions.extend_from_slice(&key); + // supported_versions (TLS1.3) + extensions.extend_from_slice(&0x002bu16.to_be_bytes()); + extensions.extend_from_slice(&(2u16).to_be_bytes()); + extensions.extend_from_slice(&0x0304u16.to_be_bytes()); + + let extensions_len = extensions.len() as u16; + + let body_len = 2 + // version + 32 + // random + 1 + session_id.len() + // session id + 2 + // cipher + 1 + // compression + 2 + extensions.len(); // extensions + + let mut message = Vec::with_capacity(4 + body_len); + message.push(0x02); // ServerHello + let len_bytes = (body_len as u32).to_be_bytes(); + message.extend_from_slice(&len_bytes[1..4]); + message.extend_from_slice(&cached.server_hello_template.version); // 0x0303 + message.extend_from_slice(&[0u8; 32]); // random placeholder + message.push(session_id.len() as u8); + message.extend_from_slice(session_id); + message.extend_from_slice(&cached.server_hello_template.cipher_suite); + message.push(cached.server_hello_template.compression); + message.extend_from_slice(&extensions_len.to_be_bytes()); + message.extend_from_slice(&extensions); + + let mut server_hello = Vec::with_capacity(5 + message.len()); + server_hello.push(TLS_RECORD_HANDSHAKE); + server_hello.extend_from_slice(&TLS_VERSION); + server_hello.extend_from_slice(&(message.len() as u16).to_be_bytes()); + server_hello.extend_from_slice(&message); + + // --- ChangeCipherSpec --- + let change_cipher_spec = [ + TLS_RECORD_CHANGE_CIPHER, + TLS_VERSION[0], + TLS_VERSION[1], + 0x00, + 0x01, + 0x01, + ]; + + // --- ApplicationData (fake encrypted records) --- + let sizes = if cached.app_data_records_sizes.is_empty() { + vec![cached.total_app_data_len.max(1024)] + } else { + cached.app_data_records_sizes.clone() + }; + + let mut app_data = Vec::new(); + for size in sizes { + let mut rec = Vec::with_capacity(5 + size); + rec.push(TLS_RECORD_APPLICATION); + rec.extend_from_slice(&TLS_VERSION); + rec.extend_from_slice(&(size as u16).to_be_bytes()); + rec.extend_from_slice(&rng.bytes(size)); + app_data.extend_from_slice(&rec); + } + + // --- Combine --- + let mut response = Vec::with_capacity(server_hello.len() + change_cipher_spec.len() + app_data.len()); + response.extend_from_slice(&server_hello); + response.extend_from_slice(&change_cipher_spec); + response.extend_from_slice(&app_data); + + // --- HMAC --- + let mut hmac_input = Vec::with_capacity(TLS_DIGEST_LEN + response.len()); + hmac_input.extend_from_slice(client_digest); + hmac_input.extend_from_slice(&response); + let digest = sha256_hmac(secret, &hmac_input); + response[TLS_DIGEST_POS..TLS_DIGEST_POS + TLS_DIGEST_LEN].copy_from_slice(&digest); + + response +} diff --git a/src/tls_front/mod.rs b/src/tls_front/mod.rs new file mode 100644 index 0000000..89f3988 --- /dev/null +++ b/src/tls_front/mod.rs @@ -0,0 +1,7 @@ +pub mod types; +pub mod cache; +pub mod fetcher; +pub mod emulator; + +pub use cache::TlsFrontCache; +pub use types::{CachedTlsData, TlsFetchResult};