Files
telemt/src/proxy/handshake.rs
T

470 lines
15 KiB
Rust
Raw Normal View History

2026-02-07 19:49:41 +03:00
//! MTProto Handshake
2025-12-30 05:08:05 +03:00
2026-02-24 03:40:59 +03:00
#![allow(dead_code)]
2025-12-30 05:08:05 +03:00
use std::net::SocketAddr;
2026-02-20 12:55:26 +03:00
use std::sync::Arc;
2026-02-23 05:47:44 +03:00
use std::time::Duration;
2025-12-30 05:08:05 +03:00
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
use tracing::{debug, warn, trace};
2026-02-07 19:49:41 +03:00
use zeroize::Zeroize;
2025-12-30 05:08:05 +03:00
2026-02-07 18:26:44 +03:00
use crate::crypto::{sha256, AesCtr, SecureRandom};
2026-02-21 03:36:13 +03:00
use rand::Rng;
2025-12-30 05:08:05 +03:00
use crate::protocol::constants::*;
use crate::protocol::tls;
use crate::stream::{FakeTlsReader, FakeTlsWriter, CryptoReader, CryptoWriter};
use crate::error::{ProxyError, HandshakeResult};
use crate::stats::ReplayChecker;
use crate::config::ProxyConfig;
2026-02-20 12:55:26 +03:00
use crate::tls_front::{TlsFrontCache, emulator};
2025-12-30 05:08:05 +03:00
const ACCESS_SECRET_BYTES: usize = 16;
fn decode_user_secrets(
config: &ProxyConfig,
preferred_user: Option<&str>,
) -> Vec<(String, Vec<u8>)> {
let mut secrets = Vec::with_capacity(config.access.users.len());
if let Some(preferred) = preferred_user
&& let Some(secret_hex) = config.access.users.get(preferred)
&& let Ok(bytes) = hex::decode(secret_hex)
&& bytes.len() == ACCESS_SECRET_BYTES
{
secrets.push((preferred.to_string(), bytes));
}
for (name, secret_hex) in &config.access.users {
if preferred_user.is_some_and(|preferred| preferred == name.as_str()) {
continue;
}
if let Ok(bytes) = hex::decode(secret_hex)
&& bytes.len() == ACCESS_SECRET_BYTES
{
secrets.push((name.clone(), bytes));
}
}
secrets
}
2025-12-30 05:08:05 +03:00
/// Result of successful handshake
2026-02-07 19:49:41 +03:00
///
/// Key material (`dec_key`, `dec_iv`, `enc_key`, `enc_iv`) is
/// zeroized on drop.
#[derive(Debug)]
2025-12-30 05:08:05 +03:00
pub struct HandshakeSuccess {
/// Authenticated user name
pub user: String,
/// Target datacenter index
pub dc_idx: i16,
/// Protocol variant (abridged/intermediate/secure)
pub proto_tag: ProtoTag,
/// Decryption key and IV (for reading from client)
pub dec_key: [u8; 32],
pub dec_iv: u128,
/// Encryption key and IV (for writing to client)
pub enc_key: [u8; 32],
pub enc_iv: u128,
/// Client address
pub peer: SocketAddr,
/// Whether TLS was used
pub is_tls: bool,
}
2026-02-07 19:49:41 +03:00
impl Drop for HandshakeSuccess {
fn drop(&mut self) {
self.dec_key.zeroize();
self.dec_iv.zeroize();
self.enc_key.zeroize();
self.enc_iv.zeroize();
}
}
2025-12-30 05:08:05 +03:00
/// Handle fake TLS handshake
pub async fn handle_tls_handshake<R, W>(
handshake: &[u8],
reader: R,
mut writer: W,
peer: SocketAddr,
config: &ProxyConfig,
replay_checker: &ReplayChecker,
2026-02-07 18:26:44 +03:00
rng: &SecureRandom,
2026-02-20 12:58:04 +03:00
tls_cache: Option<Arc<TlsFrontCache>>,
2026-01-20 01:20:02 +03:00
) -> HandshakeResult<(FakeTlsReader<R>, FakeTlsWriter<W>, String), R, W>
2025-12-30 05:08:05 +03:00
where
R: AsyncRead + Unpin,
W: AsyncWrite + Unpin,
{
debug!(peer = %peer, handshake_len = handshake.len(), "Processing TLS handshake");
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
if handshake.len() < tls::TLS_DIGEST_POS + tls::TLS_DIGEST_LEN + 1 {
debug!(peer = %peer, "TLS handshake too short");
2026-01-20 01:20:02 +03:00
return HandshakeResult::BadClient { reader, writer };
2025-12-30 05:08:05 +03:00
}
2026-02-14 00:26:07 +03:00
let secrets = decode_user_secrets(config, None);
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let validation = match tls::validate_tls_handshake(
handshake,
&secrets,
2026-01-20 01:20:02 +03:00
config.access.ignore_time_skew,
2025-12-30 05:08:05 +03:00
) {
Some(v) => v,
None => {
2026-01-20 02:08:23 +03:00
debug!(
peer = %peer,
ignore_time_skew = config.access.ignore_time_skew,
"TLS handshake validation failed - no matching user or time skew"
);
2026-01-20 01:20:02 +03:00
return HandshakeResult::BadClient { reader, writer };
2025-12-30 05:08:05 +03:00
}
};
2026-02-14 00:26:07 +03:00
// Replay tracking is applied only after successful authentication to avoid
// letting unauthenticated probes evict valid entries from the replay cache.
let digest_half = &validation.digest[..tls::TLS_DIGEST_HALF_LEN];
if replay_checker.check_and_add_tls_digest(digest_half) {
warn!(peer = %peer, "TLS replay attack detected (duplicate digest)");
return HandshakeResult::BadClient { reader, writer };
}
2025-12-30 05:08:05 +03:00
let secret = match secrets.iter().find(|(name, _)| *name == validation.user) {
Some((_, s)) => s,
2026-01-20 01:20:02 +03:00
None => return HandshakeResult::BadClient { reader, writer },
2025-12-30 05:08:05 +03:00
};
2026-02-14 00:26:07 +03:00
2026-02-20 12:58:04 +03:00
let cached = if config.censorship.tls_emulation {
if let Some(cache) = tls_cache.as_ref() {
2026-02-23 05:42:07 +03:00
let selected_domain = if let Some(sni) = tls::extract_sni_from_client_hello(handshake) {
if cache.contains_domain(&sni).await {
sni
} else {
config.censorship.tls_domain.clone()
}
2026-02-20 12:58:04 +03:00
} else {
2026-02-23 05:42:07 +03:00
config.censorship.tls_domain.clone()
};
let cached_entry = cache.get(&selected_domain).await;
2026-02-23 05:47:44 +03:00
let use_full_cert_payload = cache
.take_full_cert_budget_for_ip(
peer.ip(),
Duration::from_secs(config.censorship.tls_full_cert_ttl_secs),
)
.await;
2026-02-23 05:42:07 +03:00
Some((cached_entry, use_full_cert_payload))
2026-02-20 12:58:04 +03:00
} else {
None
}
} else {
None
};
2026-02-21 03:36:13 +03:00
let alpn_list = if config.censorship.alpn_enforce {
tls::extract_alpn_from_client_hello(handshake)
} else {
Vec::new()
};
let selected_alpn = if config.censorship.alpn_enforce {
if alpn_list.iter().any(|p| p == b"h2") {
Some(b"h2".to_vec())
} else if alpn_list.iter().any(|p| p == b"http/1.1") {
Some(b"http/1.1".to_vec())
} else {
None
}
} else {
None
};
2026-02-23 05:42:07 +03:00
let response = if let Some((cached_entry, use_full_cert_payload)) = cached {
2026-02-20 12:58:04 +03:00
emulator::build_emulated_server_hello(
secret,
&validation.digest,
&validation.session_id,
&cached_entry,
2026-02-23 05:42:07 +03:00
use_full_cert_payload,
2026-02-20 12:58:04 +03:00
rng,
2026-02-21 03:36:13 +03:00
selected_alpn.clone(),
config.censorship.tls_new_session_tickets,
2026-02-20 12:58:04 +03:00
)
} else {
tls::build_server_hello(
secret,
&validation.digest,
&validation.session_id,
config.censorship.fake_cert_len,
rng,
2026-02-21 03:36:13 +03:00
selected_alpn.clone(),
config.censorship.tls_new_session_tickets,
2026-02-20 12:58:04 +03:00
)
};
2026-02-14 00:26:07 +03:00
2026-02-21 03:36:13 +03:00
// Optional anti-fingerprint delay before sending ServerHello.
if config.censorship.server_hello_delay_max_ms > 0 {
let min = config.censorship.server_hello_delay_min_ms;
let max = config.censorship.server_hello_delay_max_ms.max(min);
let delay_ms = if max == min {
max
} else {
rand::rng().random_range(min..=max)
};
if delay_ms > 0 {
tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await;
}
}
2025-12-30 05:08:05 +03:00
debug!(peer = %peer, response_len = response.len(), "Sending TLS ServerHello");
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
if let Err(e) = writer.write_all(&response).await {
2026-01-20 02:08:23 +03:00
warn!(peer = %peer, error = %e, "Failed to write TLS ServerHello");
2025-12-30 05:08:05 +03:00
return HandshakeResult::Error(ProxyError::Io(e));
}
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
if let Err(e) = writer.flush().await {
2026-01-20 02:08:23 +03:00
warn!(peer = %peer, error = %e, "Failed to flush TLS ServerHello");
2025-12-30 05:08:05 +03:00
return HandshakeResult::Error(ProxyError::Io(e));
}
2026-02-14 00:26:07 +03:00
debug!(
2025-12-30 05:08:05 +03:00
peer = %peer,
user = %validation.user,
"TLS handshake successful"
);
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
HandshakeResult::Success((
FakeTlsReader::new(reader),
FakeTlsWriter::new(writer),
validation.user,
))
}
/// Handle MTProto obfuscation handshake
pub async fn handle_mtproto_handshake<R, W>(
handshake: &[u8; HANDSHAKE_LEN],
reader: R,
writer: W,
peer: SocketAddr,
config: &ProxyConfig,
replay_checker: &ReplayChecker,
is_tls: bool,
preferred_user: Option<&str>,
2026-01-20 01:20:02 +03:00
) -> HandshakeResult<(CryptoReader<R>, CryptoWriter<W>, HandshakeSuccess), R, W>
2025-12-30 05:08:05 +03:00
where
R: AsyncRead + Unpin + Send,
W: AsyncWrite + Unpin + Send,
{
trace!(peer = %peer, handshake = ?hex::encode(handshake), "MTProto handshake bytes");
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let dec_prekey_iv = &handshake[SKIP_LEN..SKIP_LEN + PREKEY_LEN + IV_LEN];
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let enc_prekey_iv: Vec<u8> = dec_prekey_iv.iter().rev().copied().collect();
2026-02-14 00:26:07 +03:00
let decoded_users = decode_user_secrets(config, preferred_user);
for (user, secret) in decoded_users {
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let dec_prekey = &dec_prekey_iv[..PREKEY_LEN];
let dec_iv_bytes = &dec_prekey_iv[PREKEY_LEN..];
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let mut dec_key_input = Vec::with_capacity(PREKEY_LEN + secret.len());
dec_key_input.extend_from_slice(dec_prekey);
dec_key_input.extend_from_slice(&secret);
let dec_key = sha256(&dec_key_input);
2026-02-14 00:26:07 +03:00
let mut dec_iv_arr = [0u8; IV_LEN];
dec_iv_arr.copy_from_slice(dec_iv_bytes);
let dec_iv = u128::from_be_bytes(dec_iv_arr);
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let mut decryptor = AesCtr::new(&dec_key, dec_iv);
let decrypted = decryptor.decrypt(handshake);
2026-02-14 00:26:07 +03:00
let tag_bytes: [u8; 4] = [
decrypted[PROTO_TAG_POS],
decrypted[PROTO_TAG_POS + 1],
decrypted[PROTO_TAG_POS + 2],
decrypted[PROTO_TAG_POS + 3],
];
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let proto_tag = match ProtoTag::from_bytes(tag_bytes) {
Some(tag) => tag,
2026-01-20 02:08:23 +03:00
None => continue,
2025-12-30 05:08:05 +03:00
};
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let mode_ok = match proto_tag {
ProtoTag::Secure => {
2026-02-23 02:28:00 +03:00
if is_tls {
config.general.modes.tls || config.general.modes.secure
} else {
config.general.modes.secure || config.general.modes.tls
}
2025-12-30 05:08:05 +03:00
}
2026-01-20 01:20:02 +03:00
ProtoTag::Intermediate | ProtoTag::Abridged => config.general.modes.classic,
2025-12-30 05:08:05 +03:00
};
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
if !mode_ok {
debug!(peer = %peer, user = %user, proto = ?proto_tag, "Mode not enabled");
continue;
}
2026-02-14 00:26:07 +03:00
let dc_idx = i16::from_le_bytes([decrypted[DC_IDX_POS], decrypted[DC_IDX_POS + 1]]);
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let enc_prekey = &enc_prekey_iv[..PREKEY_LEN];
let enc_iv_bytes = &enc_prekey_iv[PREKEY_LEN..];
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let mut enc_key_input = Vec::with_capacity(PREKEY_LEN + secret.len());
enc_key_input.extend_from_slice(enc_prekey);
enc_key_input.extend_from_slice(&secret);
let enc_key = sha256(&enc_key_input);
2026-02-14 00:26:07 +03:00
let mut enc_iv_arr = [0u8; IV_LEN];
enc_iv_arr.copy_from_slice(enc_iv_bytes);
let enc_iv = u128::from_be_bytes(enc_iv_arr);
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let encryptor = AesCtr::new(&enc_key, enc_iv);
2026-02-14 00:26:07 +03:00
// Apply replay tracking only after successful authentication to prevent
// unauthenticated probes from evicting legitimate replay-cache entries.
if replay_checker.check_and_add_handshake(dec_prekey_iv) {
warn!(peer = %peer, user = %user, "MTProto replay attack detected");
return HandshakeResult::BadClient { reader, writer };
}
2025-12-30 05:08:05 +03:00
let success = HandshakeSuccess {
user: user.clone(),
dc_idx,
proto_tag,
dec_key,
dec_iv,
enc_key,
enc_iv,
peer,
is_tls,
};
2026-02-14 00:26:07 +03:00
debug!(
2025-12-30 05:08:05 +03:00
peer = %peer,
user = %user,
dc = dc_idx,
proto = ?proto_tag,
tls = is_tls,
"MTProto handshake successful"
);
2026-02-14 00:26:07 +03:00
2026-02-21 02:15:10 +03:00
let max_pending = config.general.crypto_pending_buffer;
2025-12-30 05:08:05 +03:00
return HandshakeResult::Success((
CryptoReader::new(reader, decryptor),
2026-02-21 02:15:10 +03:00
CryptoWriter::new(writer, encryptor, max_pending),
2025-12-30 05:08:05 +03:00
success,
));
}
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
debug!(peer = %peer, "MTProto handshake: no matching user found");
2026-01-20 01:20:02 +03:00
HandshakeResult::BadClient { reader, writer }
2025-12-30 05:08:05 +03:00
}
/// Generate nonce for Telegram connection
pub fn generate_tg_nonce(
proto_tag: ProtoTag,
2026-02-13 03:51:36 +03:00
dc_idx: i16,
2026-02-15 12:30:40 +03:00
_client_dec_key: &[u8; 32],
_client_dec_iv: u128,
client_enc_key: &[u8; 32],
client_enc_iv: u128,
2026-02-07 18:26:44 +03:00
rng: &SecureRandom,
2025-12-30 05:08:05 +03:00
fast_mode: bool,
) -> ([u8; HANDSHAKE_LEN], [u8; 32], u128, [u8; 32], u128) {
loop {
2026-02-07 18:26:44 +03:00
let bytes = rng.bytes(HANDSHAKE_LEN);
let Ok(mut nonce): Result<[u8; HANDSHAKE_LEN], _> = bytes.try_into() else {
continue;
};
2026-02-14 00:26:07 +03:00
2026-01-20 02:08:23 +03:00
if RESERVED_NONCE_FIRST_BYTES.contains(&nonce[0]) { continue; }
2026-02-14 00:26:07 +03:00
let first_four: [u8; 4] = [nonce[0], nonce[1], nonce[2], nonce[3]];
2026-01-20 02:08:23 +03:00
if RESERVED_NONCE_BEGINNINGS.contains(&first_four) { continue; }
2026-02-14 00:26:07 +03:00
let continue_four: [u8; 4] = [nonce[4], nonce[5], nonce[6], nonce[7]];
2026-01-20 02:08:23 +03:00
if RESERVED_NONCE_CONTINUES.contains(&continue_four) { continue; }
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
nonce[PROTO_TAG_POS..PROTO_TAG_POS + 4].copy_from_slice(&proto_tag.to_bytes());
2026-02-13 03:51:36 +03:00
// CRITICAL: write dc_idx so upstream DC knows where to route
nonce[DC_IDX_POS..DC_IDX_POS + 2].copy_from_slice(&dc_idx.to_le_bytes());
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
if fast_mode {
2026-02-15 12:30:40 +03:00
let mut key_iv = Vec::with_capacity(KEY_LEN + IV_LEN);
key_iv.extend_from_slice(client_enc_key);
key_iv.extend_from_slice(&client_enc_iv.to_be_bytes());
key_iv.reverse(); // Python/C behavior: reversed enc_key+enc_iv in nonce
nonce[SKIP_LEN..SKIP_LEN + KEY_LEN + IV_LEN].copy_from_slice(&key_iv);
2025-12-30 05:08:05 +03:00
}
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
let enc_key_iv = &nonce[SKIP_LEN..SKIP_LEN + KEY_LEN + IV_LEN];
let dec_key_iv: Vec<u8> = enc_key_iv.iter().rev().copied().collect();
2026-02-14 00:26:07 +03:00
let mut tg_enc_key = [0u8; 32];
tg_enc_key.copy_from_slice(&enc_key_iv[..KEY_LEN]);
let mut tg_enc_iv_arr = [0u8; IV_LEN];
tg_enc_iv_arr.copy_from_slice(&enc_key_iv[KEY_LEN..]);
let tg_enc_iv = u128::from_be_bytes(tg_enc_iv_arr);
2026-02-14 00:26:07 +03:00
let mut tg_dec_key = [0u8; 32];
tg_dec_key.copy_from_slice(&dec_key_iv[..KEY_LEN]);
let mut tg_dec_iv_arr = [0u8; IV_LEN];
tg_dec_iv_arr.copy_from_slice(&dec_key_iv[KEY_LEN..]);
let tg_dec_iv = u128::from_be_bytes(tg_dec_iv_arr);
2026-02-14 00:26:07 +03:00
2025-12-30 05:08:05 +03:00
return (nonce, tg_enc_key, tg_enc_iv, tg_dec_key, tg_dec_iv);
}
}
2026-02-14 00:26:07 +03:00
/// Encrypt nonce for sending to Telegram and return cipher objects with correct counter state
pub fn encrypt_tg_nonce_with_ciphers(nonce: &[u8; HANDSHAKE_LEN]) -> (Vec<u8>, AesCtr, AesCtr) {
2025-12-30 05:08:05 +03:00
let enc_key_iv = &nonce[SKIP_LEN..SKIP_LEN + KEY_LEN + IV_LEN];
2026-02-14 00:26:07 +03:00
let dec_key_iv: Vec<u8> = enc_key_iv.iter().rev().copied().collect();
let mut enc_key = [0u8; 32];
enc_key.copy_from_slice(&enc_key_iv[..KEY_LEN]);
let mut enc_iv_arr = [0u8; IV_LEN];
enc_iv_arr.copy_from_slice(&enc_key_iv[KEY_LEN..]);
let enc_iv = u128::from_be_bytes(enc_iv_arr);
2026-02-14 00:26:07 +03:00
let mut dec_key = [0u8; 32];
dec_key.copy_from_slice(&dec_key_iv[..KEY_LEN]);
let mut dec_iv_arr = [0u8; IV_LEN];
dec_iv_arr.copy_from_slice(&dec_key_iv[KEY_LEN..]);
let dec_iv = u128::from_be_bytes(dec_iv_arr);
2026-02-14 00:26:07 +03:00
let mut encryptor = AesCtr::new(&enc_key, enc_iv);
let encrypted_full = encryptor.encrypt(nonce); // counter: 0 → 4
2025-12-30 05:08:05 +03:00
let mut result = nonce[..PROTO_TAG_POS].to_vec();
result.extend_from_slice(&encrypted_full[PROTO_TAG_POS..]);
2026-02-14 00:26:07 +03:00
let decryptor = AesCtr::new(&dec_key, dec_iv);
(result, encryptor, decryptor)
}
/// Encrypt nonce for sending to Telegram (legacy function for compatibility)
pub fn encrypt_tg_nonce(nonce: &[u8; HANDSHAKE_LEN]) -> Vec<u8> {
let (encrypted, _, _) = encrypt_tg_nonce_with_ciphers(nonce);
encrypted
2025-12-30 05:08:05 +03:00
}
#[cfg(test)]
#[path = "handshake_security_tests.rs"]
mod security_tests;
2026-02-14 00:26:07 +03:00
/// Compile-time guard: HandshakeSuccess holds cryptographic key material and
/// must never be Copy. A Copy impl would allow silent key duplication,
/// undermining the zeroize-on-drop guarantee.
mod compile_time_security_checks {
use super::HandshakeSuccess;
use static_assertions::assert_not_impl_all;
2026-02-14 00:26:07 +03:00
assert_not_impl_all!(HandshakeSuccess: Copy, Clone);
2026-02-15 12:30:40 +03:00
}