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> {
85 match self.framing {
86 ObfuscatedFraming::Abridged => {
87 debug_assert_eq!(
88 data.len() % 4,
89 0,
90 "obfuscated send: payload must be 4-byte aligned"
91 );
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 }
111 ObfuscatedFraming::PaddedIntermediate => {
112 let mut pad_len_buf = [0u8; 1];
115 ferogram_crypto::fill_random(&mut pad_len_buf);
116 let pad_len = (pad_len_buf[0] & 0x0f) as usize;
117 let total_payload = data.len() + pad_len;
118 let mut frame = Vec::with_capacity(4 + total_payload);
119 frame.extend_from_slice(&(total_payload as u32).to_le_bytes());
120 frame.extend_from_slice(data);
121 let mut pad = vec![0u8; pad_len];
122 ferogram_crypto::fill_random(&mut pad);
123 frame.extend_from_slice(&pad);
124 self.cipher.encrypt(&mut frame);
125 self.stream.write_all(&frame).await?;
126 }
127 }
128 Ok(())
129 }
130
131 pub async fn recv(&mut self) -> Result<Vec<u8>, ConnectError> {
132 match self.framing {
133 ObfuscatedFraming::Abridged => {
134 let mut h = [0u8; 1];
135 self.stream.read_exact(&mut h).await?;
136 self.cipher.decrypt(&mut h);
137
138 let words = if h[0] < 0x7f {
139 h[0] as usize
140 } else if h[0] == 0x7f {
141 let mut b = [0u8; 3];
142 self.stream.read_exact(&mut b).await?;
143 self.cipher.decrypt(&mut b);
144 b[0] as usize | (b[1] as usize) << 8 | (b[2] as usize) << 16
145 } else {
146 let mut rest = [0u8; 3];
147 self.stream.read_exact(&mut rest).await?;
148 self.cipher.decrypt(&mut rest);
149 let code = i32::from_le_bytes([h[0], rest[0], rest[1], rest[2]]);
150 return Err(ConnectError::Io(std::io::Error::new(
151 std::io::ErrorKind::ConnectionRefused,
152 format!("transport error from server: {code}"),
153 )));
154 };
155
156 let mut buf = vec![0u8; words * 4];
157 self.stream.read_exact(&mut buf).await?;
158 self.cipher.decrypt(&mut buf);
159
160 if buf.len() == 4 {
161 let code = i32::from_le_bytes(buf[..4].try_into().unwrap());
162 if code < 0 {
163 return Err(ConnectError::Io(std::io::Error::new(
164 std::io::ErrorKind::ConnectionRefused,
165 format!("transport error from server: {code}"),
166 )));
167 }
168 }
169
170 Ok(buf)
171 }
172 ObfuscatedFraming::PaddedIntermediate => {
173 let mut len_buf = [0u8; 4];
174 self.stream.read_exact(&mut len_buf).await?;
175 self.cipher.decrypt(&mut len_buf);
176 let total_len = i32::from_le_bytes(len_buf);
177 if total_len < 0 {
178 return Err(ConnectError::Io(std::io::Error::new(
179 std::io::ErrorKind::ConnectionRefused,
180 format!("transport error from server: {total_len}"),
181 )));
182 }
183 let mut buf = vec![0u8; total_len as usize];
184 self.stream.read_exact(&mut buf).await?;
185 self.cipher.decrypt(&mut buf);
186 if buf.len() >= 24 {
187 let pad = (buf.len() - 24) % 16;
188 buf.truncate(buf.len() - pad);
189 }
190 Ok(buf)
191 }
192 }
193 }
194}