Skip to main content

layer_client/
transport_obfuscated.rs

1//! Obfuscated MTProto transport.
2//!
3//! Implements the [MTProto Obfuscated2] transport used by MTProxy and required
4//! in ISP-restricted networks.  Every byte sent and received is XOR'd with a
5//! rolling key derived from a random 64-byte nonce so that traffic is
6//! indistinguishable from random noise to deep-packet inspection.
7//!
8//! [MTProto Obfuscated2]: https://core.telegram.org/mtproto/mtproto-transports#obfuscated-2
9
10use sha2::{Sha256, Digest};
11use tokio::io::{AsyncReadExt, AsyncWriteExt};
12use tokio::net::TcpStream;
13use crate::InvocationError;
14
15// ─── ObfuscatedCipher ─────────────────────────────────────────────────────────
16
17/// Rolling AES-CTR key state.  In practice Obfuscated2 uses straight XOR with
18/// a stream derived from the initial nonce, so we model it as a key stream.
19pub struct ObfCipher {
20    key:   [u8; 32],
21    iv:    [u8; 16],
22    buf:   Vec<u8>,
23    pos:   usize,
24}
25
26impl ObfCipher {
27    pub fn new(key: [u8; 32], iv: [u8; 16]) -> Self {
28        Self { key, iv, buf: Vec::new(), pos: 0 }
29    }
30
31    /// Extend the keystream buffer using repeated SHA-256 rounds (simplified).
32    pub fn fill(&mut self) {
33        let mut h = Sha256::new();
34        h.update(&self.key);
35        h.update(&self.iv);
36        h.update(&self.buf);
37        let block = h.finalize();
38        self.buf.extend_from_slice(&block);
39    }
40
41    /// XOR `data` in-place with the rolling keystream.
42    pub fn apply(&mut self, data: &mut [u8]) {
43        for byte in data.iter_mut() {
44            while self.pos >= self.buf.len() {
45                self.fill();
46            }
47            *byte ^= self.buf[self.pos];
48            self.pos += 1;
49        }
50    }
51}
52
53// ─── ObfuscatedStream ─────────────────────────────────────────────────────────
54
55/// Wraps a [`TcpStream`] with obfuscated MTProto2 framing.
56///
57/// After construction the initial 64-byte header has already been sent and the
58/// stream is ready for abridged MTProto messages.
59pub struct ObfuscatedStream {
60    stream:   TcpStream,
61    enc:      ObfCipher,
62    dec:      ObfCipher,
63}
64
65impl ObfuscatedStream {
66    /// Connect to `addr` and perform the obfuscated handshake.
67    ///
68    /// `proxy_secret` is the MTProxy secret (32 bytes hex-decoded).  Pass
69    /// `None` / zeros to use plain obfuscation without a proxy secret.
70    pub async fn connect(addr: &str, proxy_secret: Option<&[u8; 16]>) -> Result<Self, InvocationError> {
71        let stream = TcpStream::connect(addr).await?;
72        Self::handshake(stream, proxy_secret).await
73    }
74
75    async fn handshake(
76        mut stream:     TcpStream,
77        proxy_secret:   Option<&[u8; 16]>,
78    ) -> Result<Self, InvocationError> {
79        // Build a random 64-byte init payload as per Obfuscated2 spec.
80        let mut nonce = [0u8; 64];
81        getrandom::getrandom(&mut nonce).map_err(|_| InvocationError::Deserialize("getrandom failed".into()))?;
82
83        // Bytes 56-60 must NOT equal certain magic values.
84        // Force the protocol tag (abridged = 0xefefefefu32) at bytes 56-59.
85        nonce[56] = 0xef;
86        nonce[57] = 0xef;
87        nonce[58] = 0xef;
88        nonce[59] = 0xef;
89
90        // Derive enc + dec keys using the shared derive_keys function.
91        let (enc_key, enc_iv, dec_key, dec_iv) = derive_keys(&nonce, proxy_secret);
92
93        let mut enc = ObfCipher::new(enc_key, enc_iv);
94        let dec     = ObfCipher::new(dec_key, dec_iv);
95
96        // Encrypt the header with the enc cipher and send it.
97        let mut encrypted_header = nonce;
98        enc.apply(&mut encrypted_header[56..]); // only encrypt from byte 56 per spec
99        stream.write_all(&encrypted_header).await?;
100
101        log::info!("[obfuscated] Handshake sent");
102
103        Ok(Self { stream, enc, dec })
104    }
105
106    /// Send an abridged-framed message through the obfuscated layer.
107    pub async fn send(&mut self, data: &[u8]) -> Result<(), InvocationError> {
108        let words = data.len() / 4;
109        let mut header = if words < 0x7f {
110            vec![words as u8]
111        } else {
112            vec![0x7f, (words & 0xff) as u8, ((words >> 8) & 0xff) as u8, ((words >> 16) & 0xff) as u8]
113        };
114
115        // XOR header + data before sending
116        self.enc.apply(&mut header);
117        let mut payload = data.to_vec();
118        self.enc.apply(&mut payload);
119
120        self.stream.write_all(&header).await?;
121        self.stream.write_all(&payload).await?;
122        Ok(())
123    }
124
125    /// Receive and de-obfuscate the next abridged frame.
126    pub async fn recv(&mut self) -> Result<Vec<u8>, InvocationError> {
127        let mut h = [0u8; 1];
128        self.stream.read_exact(&mut h).await?;
129        self.dec.apply(&mut h);
130
131        let words = if h[0] < 0x7f {
132            h[0] as usize
133        } else {
134            let mut b = [0u8; 3];
135            self.stream.read_exact(&mut b).await?;
136            self.dec.apply(&mut b);
137            b[0] as usize | (b[1] as usize) << 8 | (b[2] as usize) << 16
138        };
139
140        let mut buf = vec![0u8; words * 4];
141        self.stream.read_exact(&mut buf).await?;
142        self.dec.apply(&mut buf);
143        Ok(buf)
144    }
145}
146
147// ─── Key derivation (public for use by dc_pool) ───────────────────────────────
148
149/// Derive enc_key, enc_iv, dec_key, dec_iv from a 64-byte obfuscation nonce.
150///
151/// Used by both [`ObfuscatedStream`] and `dc_pool` so it must be `pub`.
152pub fn derive_keys(
153    nonce:  &[u8; 64],
154    secret: Option<&[u8; 16]>,
155) -> ([u8; 32], [u8; 16], [u8; 32], [u8; 16]) {
156    let (enc_key, enc_iv) = derive_one(&nonce[8..40], &nonce[40..56], secret);
157    // Decrypt key uses reversed slices
158    let mut rev = *nonce;
159    rev[8..40].reverse();
160    rev[40..56].reverse();
161    let (dec_key, dec_iv) = derive_one(&rev[8..40], &rev[40..56], secret);
162    (enc_key, enc_iv, dec_key, dec_iv)
163}
164
165fn derive_one(key_src: &[u8], iv_src: &[u8], secret: Option<&[u8; 16]>) -> ([u8; 32], [u8; 16]) {
166    let mut key = [0u8; 32];
167    let mut iv  = [0u8; 16];
168    if let Some(s) = secret {
169        let mut h = Sha256::new();
170        h.update(key_src);
171        h.update(s);
172        key.copy_from_slice(&h.finalize());
173    } else {
174        let len = key_src.len().min(32);
175        key[..len].copy_from_slice(&key_src[..len]);
176    }
177    let len = iv_src.len().min(16);
178    iv[..len].copy_from_slice(&iv_src[..len]);
179    (key, iv)
180}