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