ferogram_connect/
transport_obfuscated.rs1pub use ferogram_crypto::ObfuscatedCipher;
14
15use crate::ConnectError;
16use tokio::io::{AsyncReadExt, AsyncWriteExt};
17use tokio::net::TcpStream;
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum ObfuscatedFraming {
27 Abridged,
28 PaddedIntermediate,
29}
30
31pub struct ObfuscatedStream {
32 stream: TcpStream,
33 cipher: ObfuscatedCipher,
34 framing: ObfuscatedFraming,
35}
36
37impl ObfuscatedStream {
38 pub async fn connect(
40 addr: &str,
41 proxy_secret: Option<&[u8; 16]>,
42 dc_id: i16,
43 ) -> Result<Self, ConnectError> {
44 let stream = TcpStream::connect(addr).await?;
45 Self::handshake(stream, proxy_secret, dc_id, ObfuscatedFraming::Abridged).await
46 }
47
48 pub async fn connect_padded(
50 addr: &str,
51 proxy_secret: Option<&[u8; 16]>,
52 dc_id: i16,
53 ) -> Result<Self, ConnectError> {
54 let stream = TcpStream::connect(addr).await?;
55 Self::handshake(
56 stream,
57 proxy_secret,
58 dc_id,
59 ObfuscatedFraming::PaddedIntermediate,
60 )
61 .await
62 }
63
64 async fn handshake(
65 mut stream: TcpStream,
66 proxy_secret: Option<&[u8; 16]>,
67 dc_id: i16,
68 framing: ObfuscatedFraming,
69 ) -> Result<Self, ConnectError> {
70 let framing_byte = match framing {
71 ObfuscatedFraming::Abridged => 0xef,
72 ObfuscatedFraming::PaddedIntermediate => 0xdd,
73 };
74 let secret = proxy_secret.map(|s| s.as_ref());
75 let (nonce, cipher) = ferogram_crypto::build_obfuscated_init(framing_byte, dc_id, secret);
76 stream.write_all(&nonce).await?;
77 Ok(Self {
78 stream,
79 cipher,
80 framing,
81 })
82 }
83
84 pub async fn send(&mut self, data: &[u8]) -> Result<(), ConnectError> {
87 match self.framing {
88 ObfuscatedFraming::Abridged => {
89 debug_assert_eq!(
90 data.len() % 4,
91 0,
92 "obfuscated send: payload must be 4-byte aligned"
93 );
94 let words = data.len() / 4;
95 let mut frame = if words < 0x7f {
96 let mut v = Vec::with_capacity(1 + data.len());
97 v.push(words as u8);
98 v
99 } else {
100 let mut v = Vec::with_capacity(4 + data.len());
101 v.extend_from_slice(&[
102 0x7f,
103 (words & 0xff) as u8,
104 ((words >> 8) & 0xff) as u8,
105 ((words >> 16) & 0xff) as u8,
106 ]);
107 v
108 };
109 frame.extend_from_slice(data);
110 self.cipher.encrypt(&mut frame);
111 self.stream.write_all(&frame).await?;
112 }
113 ObfuscatedFraming::PaddedIntermediate => {
114 let mut pad_len_buf = [0u8; 1];
117 ferogram_crypto::fill_random(&mut pad_len_buf);
118 let pad_len = (pad_len_buf[0] & 0x0f) as usize;
119 let total_payload = data.len() + pad_len;
120 let mut frame = Vec::with_capacity(4 + total_payload);
121 frame.extend_from_slice(&(total_payload as u32).to_le_bytes());
122 frame.extend_from_slice(data);
123 let mut pad = vec![0u8; pad_len];
124 ferogram_crypto::fill_random(&mut pad);
125 frame.extend_from_slice(&pad);
126 self.cipher.encrypt(&mut frame);
127 self.stream.write_all(&frame).await?;
128 }
129 }
130 Ok(())
131 }
132
133 pub async fn recv(&mut self) -> Result<Vec<u8>, ConnectError> {
138 match self.framing {
139 ObfuscatedFraming::Abridged => {
140 let mut h = [0u8; 1];
141 self.stream.read_exact(&mut h).await?;
142 self.cipher.decrypt(&mut h);
143
144 let words = if h[0] < 0x7f {
145 h[0] as usize
146 } else if h[0] == 0x7f {
147 let mut b = [0u8; 3];
148 self.stream.read_exact(&mut b).await?;
149 self.cipher.decrypt(&mut b);
150 b[0] as usize | (b[1] as usize) << 8 | (b[2] as usize) << 16
151 } else {
152 let mut rest = [0u8; 3];
153 self.stream.read_exact(&mut rest).await?;
154 self.cipher.decrypt(&mut rest);
155 let code = i32::from_le_bytes([h[0], rest[0], rest[1], rest[2]]);
156 return Err(ConnectError::Io(std::io::Error::new(
157 std::io::ErrorKind::ConnectionRefused,
158 format!("transport error from server: {code}"),
159 )));
160 };
161
162 let mut buf = vec![0u8; words * 4];
163 self.stream.read_exact(&mut buf).await?;
164 self.cipher.decrypt(&mut buf);
165
166 if buf.len() == 4 {
167 let code = i32::from_le_bytes(buf[..4].try_into().unwrap());
168 if code < 0 {
169 return Err(ConnectError::Io(std::io::Error::new(
170 std::io::ErrorKind::ConnectionRefused,
171 format!("transport error from server: {code}"),
172 )));
173 }
174 }
175
176 Ok(buf)
177 }
178 ObfuscatedFraming::PaddedIntermediate => {
179 let mut len_buf = [0u8; 4];
180 self.stream.read_exact(&mut len_buf).await?;
181 self.cipher.decrypt(&mut len_buf);
182 let total_len = i32::from_le_bytes(len_buf);
183 if total_len < 0 {
184 return Err(ConnectError::Io(std::io::Error::new(
185 std::io::ErrorKind::ConnectionRefused,
186 format!("transport error from server: {total_len}"),
187 )));
188 }
189 let mut buf = vec![0u8; total_len as usize];
190 self.stream.read_exact(&mut buf).await?;
191 self.cipher.decrypt(&mut buf);
192 if buf.len() >= 24 {
193 let pad = (buf.len() - 24) % 16;
194 buf.truncate(buf.len() - pad);
195 }
196 Ok(buf)
197 }
198 }
199 }
200}