Skip to main content

layer_client/
transport_obfuscated.rs

1//! Obfuscated MTProto transport (Obfuscated2).
2
3pub use layer_crypto::ObfuscatedCipher;
4
5use crate::InvocationError;
6use tokio::io::{AsyncReadExt, AsyncWriteExt};
7use tokio::net::TcpStream;
8
9pub struct ObfuscatedStream {
10    stream: TcpStream,
11    cipher: ObfuscatedCipher,
12}
13
14impl ObfuscatedStream {
15    pub async fn connect(
16        addr: &str,
17        proxy_secret: Option<&[u8; 16]>,
18        dc_id: i16,
19    ) -> Result<Self, InvocationError> {
20        let stream = TcpStream::connect(addr).await?;
21        Self::handshake(stream, proxy_secret, dc_id).await
22    }
23
24    async fn handshake(
25        mut stream: TcpStream,
26        proxy_secret: Option<&[u8; 16]>,
27        dc_id: i16,
28    ) -> Result<Self, InvocationError> {
29        use sha2::Digest;
30
31        let mut nonce = [0u8; 64];
32        loop {
33            getrandom::getrandom(&mut nonce)
34                .map_err(|_| InvocationError::Deserialize("getrandom failed".into()))?;
35            let first = u32::from_le_bytes(nonce[0..4].try_into().unwrap());
36            let second = u32::from_le_bytes(nonce[4..8].try_into().unwrap());
37            let bad = nonce[0] == 0xEF
38                || first == 0x44414548
39                || first == 0x54534F50
40                || first == 0x20544547
41                || first == 0xEEEEEEEE
42                || first == 0xDDDDDDDD
43                || first == 0x02010316
44                || second == 0x00000000;
45            if !bad {
46                break;
47            }
48        }
49
50        let tx_raw: [u8; 32] = nonce[8..40].try_into().unwrap();
51        let tx_iv: [u8; 16] = nonce[40..56].try_into().unwrap();
52        let mut rev48 = nonce[8..56].to_vec();
53        rev48.reverse();
54        let rx_raw: [u8; 32] = rev48[0..32].try_into().unwrap();
55        let rx_iv: [u8; 16] = rev48[32..48].try_into().unwrap();
56
57        let (tx_key, rx_key): ([u8; 32], [u8; 32]) = if let Some(s) = proxy_secret {
58            let mut h = sha2::Sha256::new();
59            h.update(tx_raw);
60            h.update(s.as_ref());
61            let tx: [u8; 32] = h.finalize().into();
62            let mut h = sha2::Sha256::new();
63            h.update(rx_raw);
64            h.update(s.as_ref());
65            let rx: [u8; 32] = h.finalize().into();
66            (tx, rx)
67        } else {
68            (tx_raw, rx_raw)
69        };
70
71        nonce[56] = 0xef;
72        nonce[57] = 0xef;
73        nonce[58] = 0xef;
74        nonce[59] = 0xef;
75        let dc_bytes = dc_id.to_le_bytes();
76        nonce[60] = dc_bytes[0];
77        nonce[61] = dc_bytes[1];
78
79        // Single continuous cipher: advance TX past plaintext nonce[0..56], then
80        // encrypt nonce[56..64].  The same instance is stored for all later TX so
81        // the AES-CTR stream continues from position 64 matching tDesktop.
82        let mut cipher = ObfuscatedCipher::from_keys(&tx_key, &tx_iv, &rx_key, &rx_iv);
83        let mut skip = [0u8; 56];
84        cipher.encrypt(&mut skip);
85        cipher.encrypt(&mut nonce[56..64]);
86
87        stream.write_all(&nonce).await?;
88        Ok(Self { stream, cipher })
89    }
90
91    pub async fn send(&mut self, data: &[u8]) -> Result<(), InvocationError> {
92        let words = data.len() / 4;
93        let mut frame = if words < 0x7f {
94            let mut v = Vec::with_capacity(1 + data.len());
95            v.push(words as u8);
96            v
97        } else {
98            let mut v = Vec::with_capacity(4 + data.len());
99            v.extend_from_slice(&[
100                0x7f,
101                (words & 0xff) as u8,
102                ((words >> 8) & 0xff) as u8,
103                ((words >> 16) & 0xff) as u8,
104            ]);
105            v
106        };
107        frame.extend_from_slice(data);
108        self.cipher.encrypt(&mut frame);
109        self.stream.write_all(&frame).await?;
110        Ok(())
111    }
112
113    pub async fn recv(&mut self) -> Result<Vec<u8>, InvocationError> {
114        let mut h = [0u8; 1];
115        self.stream.read_exact(&mut h).await?;
116        self.cipher.decrypt(&mut h);
117
118        let words = if h[0] < 0x7f {
119            h[0] as usize
120        } else {
121            let mut b = [0u8; 3];
122            self.stream.read_exact(&mut b).await?;
123            self.cipher.decrypt(&mut b);
124            b[0] as usize | (b[1] as usize) << 8 | (b[2] as usize) << 16
125        };
126
127        let mut buf = vec![0u8; words * 4];
128        self.stream.read_exact(&mut buf).await?;
129        self.cipher.decrypt(&mut buf);
130        Ok(buf)
131    }
132}