steel_protocol/packet_reader.rs
1//! # Steel Protocol Packet Reader
2//!
3//! This module contains the implementation of the packet reader.
4/*
5Credit to https://github.com/Pumpkin-MC/Pumpkin/ for this implementation.
6*/
7
8use std::{
9 io::{self, Read},
10 num::NonZeroU32,
11 pin::Pin,
12 task::{Context, Poll},
13};
14
15use aes::cipher::KeyIvInit;
16use flate2::read::ZlibDecoder;
17use steel_utils::codec::VarInt;
18use steel_utils::serial::ReadFrom;
19use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf};
20
21use crate::utils::{
22 Aes128Cfb8Dec, MAX_PACKET_DATA_SIZE, MAX_PACKET_SIZE, PacketError, RawPacket, StreamDecryptor,
23};
24
25/// A reader that can decrypt data.
26pub enum DecryptionReader<R: AsyncRead + Unpin> {
27 /// A reader that decrypts data.
28 Decrypt(Box<StreamDecryptor<R>>),
29 /// A reader that does not decrypt data.
30 None(R),
31}
32
33impl<R: AsyncRead + Unpin> DecryptionReader<R> {
34 /// Upgrades the reader to decrypt data.
35 ///
36 /// # Panics
37 /// - If the reader is already decrypting data.
38 #[must_use]
39 pub fn upgrade(self, cipher: Aes128Cfb8Dec) -> Self {
40 match self {
41 Self::None(stream) => Self::Decrypt(Box::new(StreamDecryptor::new(cipher, stream))),
42 Self::Decrypt(_) => panic!("Cannot upgrade a stream that already has a cipher!"),
43 }
44 }
45}
46
47impl<R: AsyncRead + Unpin> AsyncRead for DecryptionReader<R> {
48 #[inline]
49 fn poll_read(
50 self: Pin<&mut Self>,
51 cx: &mut Context<'_>,
52 buf: &mut ReadBuf<'_>,
53 ) -> Poll<io::Result<()>> {
54 match self.get_mut() {
55 Self::Decrypt(reader) => {
56 let reader = Pin::new(reader);
57 reader.poll_read(cx, buf)
58 }
59 Self::None(reader) => {
60 let reader = Pin::new(reader);
61 reader.poll_read(cx, buf)
62 }
63 }
64 }
65}
66
67/// Decoder: Client -> Server
68/// Supports `ZLib` decoding/decompression
69/// Supports Aes128 Encryption
70pub struct TCPNetworkDecoder<R: AsyncRead + Unpin> {
71 reader: DecryptionReader<R>,
72 compression: Option<NonZeroU32>,
73}
74
75impl<R: AsyncRead + Unpin> TCPNetworkDecoder<R> {
76 /// Creates a new `TCPNetworkDecoder`.
77 pub const fn new(reader: R) -> Self {
78 Self {
79 reader: DecryptionReader::None(reader),
80 compression: None,
81 }
82 }
83
84 /// Sets the compression threshold for the decoder.
85 pub const fn set_compression(&mut self, threshold: NonZeroU32) {
86 self.compression = Some(threshold);
87 }
88
89 /// NOTE: Encryption can only be set; a minecraft stream cannot go back to being unencrypted
90 ///
91 /// # Panics
92 /// - If the reader is already decrypting data.
93 /// - If the key is invalid.
94 pub fn set_encryption(&mut self, key: &[u8; 16]) {
95 if matches!(self.reader, DecryptionReader::Decrypt(_)) {
96 panic!("Cannot upgrade a stream that already has a cipher!");
97 }
98 let cipher = Aes128Cfb8Dec::new_from_slices(key, key).expect("invalid key");
99 replace_with::replace_with_or_abort(&mut self.reader, |decoder| decoder.upgrade(cipher));
100 }
101
102 /// Gets a raw packet from the stream.
103 ///
104 /// # Errors
105 /// - If the packet length is invalid.
106 /// - If the packet is too long.
107 /// - If the packet is not compressed when it should be.
108 /// - If the packet fails to decompress.
109 #[expect(clippy::cast_sign_loss)]
110 pub async fn get_raw_packet(&mut self) -> Result<RawPacket, PacketError> {
111 let packet_len = VarInt::read_async(&mut self.reader).await? as usize;
112
113 if packet_len > MAX_PACKET_SIZE {
114 Err(PacketError::OutOfBounds)?;
115 }
116
117 // Read the entire packet data into a buffer
118 let mut packet_data = vec![0u8; packet_len];
119 self.reader
120 .read_exact(&mut packet_data)
121 .await
122 .map_err(|e| PacketError::Other(e.to_string()))?;
123
124 let mut cursor = io::Cursor::new(packet_data.as_slice());
125
126 let decompressed_data = if let Some(threshold) = self.compression {
127 let decompressed_len = VarInt::read(&mut cursor)?.0 as usize;
128 let raw_packet_len = packet_len - VarInt::written_size(decompressed_len as i32);
129
130 if decompressed_len > MAX_PACKET_DATA_SIZE {
131 Err(PacketError::TooLong(decompressed_len))?;
132 }
133
134 if decompressed_len > 0 {
135 // Decompress the remaining data
136 let mut decompressed = Vec::with_capacity(decompressed_len);
137 ZlibDecoder::new(&mut cursor)
138 .read_to_end(&mut decompressed)
139 .map_err(|e| PacketError::DecompressionFailed(e.to_string()))?;
140 decompressed
141 } else {
142 // Validate that we are not less than the compression threshold
143 if raw_packet_len > threshold.get() as _ {
144 Err(PacketError::NotCompressed)?;
145 }
146
147 // Rest of the data is uncompressed
148 let pos = cursor.position() as usize;
149 packet_data[pos..].to_vec()
150 }
151 } else {
152 packet_data
153 };
154
155 // Parse packet ID and payload from decompressed data
156 let mut cursor = io::Cursor::new(decompressed_data.as_slice());
157 let packet_id = VarInt::read(&mut cursor)?.0;
158 let pos = cursor.position() as usize;
159 let payload = decompressed_data[pos..].to_vec();
160
161 Ok(RawPacket {
162 id: packet_id,
163 payload,
164 })
165 }
166}
167
168/* TODO: Tests.
169#[cfg(test)]
170mod tests {
171
172 use std::io::Write;
173
174 use super::*;
175 use aes::Aes128;
176 use cfb8::Encryptor as Cfb8Encryptor;
177 use cfb8::cipher::AsyncStreamCipher;
178 use flate2::Compression;
179 use flate2::write::ZlibEncoder;
180
181 /// Helper function to compress data using libdeflater's Zlib compressor
182 fn compress_zlib(data: &[u8]) -> Vec<u8> {
183 let mut compressed = Vec::new();
184 ZlibEncoder::new(&mut compressed, Compression::default())
185 .write_all(data)
186 .unwrap();
187 compressed
188 }
189
190 /// Helper function to encrypt data using AES-128 CFB-8 mode
191 fn encrypt_aes128(data: &mut [u8], key: &[u8; 16], iv: &[u8; 16]) {
192 let encryptor = Cfb8Encryptor::<Aes128>::new_from_slices(key, iv).expect("Invalid key/iv");
193 encryptor.encrypt(data);
194 }
195
196 /// Helper function to build a packet with optional compression and encryption
197 fn build_packet(
198 packet_id: i32,
199 payload: &[u8],
200 compress: bool,
201 key: Option<&[u8; 16]>,
202 iv: Option<&[u8; 16]>,
203 ) -> Vec<u8> {
204 let mut buffer = Vec::new();
205
206 if compress {
207 // Create a buffer that includes `packet_id_varint` and payload
208 let mut data_to_compress = Vec::new();
209 let packet_id_varint = VarInt(packet_id);
210 data_to_compress.write_var_int(&packet_id_varint).unwrap();
211 data_to_compress.write_slice(payload).unwrap();
212
213 // Compress the combined data
214 let compressed_payload = compress_zlib(&data_to_compress);
215 let data_len = data_to_compress.len() as i32; // 1 + payload.len()
216 let data_len_varint = VarInt(data_len);
217 buffer.write_var_int(&data_len_varint).unwrap();
218 buffer.write_slice(&compressed_payload).unwrap();
219 } else {
220 // No compression; `data_len` is payload length
221 let packet_id_varint = VarInt(packet_id);
222 buffer.write_var_int(&packet_id_varint).unwrap();
223 buffer.write_slice(payload).unwrap();
224 }
225
226 // Calculate packet length: length of buffer
227 let packet_len = buffer.len() as i32;
228 let packet_len_varint = VarInt(packet_len);
229 let mut packet_length_encoded = Vec::new();
230 {
231 packet_len_varint
232 .encode(&mut packet_length_encoded)
233 .unwrap();
234 }
235
236 // Create a new buffer for the entire packet
237 let mut packet = Vec::new();
238 packet.extend_from_slice(&packet_length_encoded);
239 packet.extend_from_slice(&buffer);
240
241 // Encrypt if key and IV are provided.
242 if let (Some(k), Some(v)) = (key, iv) {
243 encrypt_aes128(&mut packet, k, v);
244 packet
245 } else {
246 packet
247 }
248 }
249
250 /// Test decoding without compression and encryption
251 #[tokio::test]
252 async fn test_decode_without_compression_and_encryption() {
253 // Sample packet data: packet_id = 1, payload = "Hello"
254 let packet_id = 1;
255 let payload = b"Hello";
256
257 // Build the packet without compression and encryption
258 let packet = build_packet(packet_id, payload, false, None, None);
259
260 // Initialize the decoder without compression and encryption
261 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
262
263 // Attempt to decode
264 let raw_packet = decoder.get_raw_packet().await.expect("Decoding failed");
265
266 assert_eq!(raw_packet.id, packet_id);
267 assert_eq!(raw_packet.payload.as_ref(), payload);
268 }
269
270 /// Test decoding with compression
271 #[tokio::test]
272 async fn test_decode_with_compression() {
273 // Sample packet data: packet_id = 2, payload = "Hello, compressed world!"
274 let packet_id = 2;
275 let payload = b"Hello, compressed world!";
276
277 // Build the packet with compression enabled
278 let packet = build_packet(packet_id, payload, true, None, None);
279
280 // Initialize the decoder with compression enabled
281 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
282 // Larger than payload
283 decoder.set_compression(1000);
284
285 // Attempt to decode
286 let raw_packet = decoder.get_raw_packet().await.expect("Decoding failed");
287
288 assert_eq!(raw_packet.id, packet_id);
289 assert_eq!(raw_packet.payload.as_ref(), payload);
290 }
291
292 /// Test decoding with encryption
293 #[tokio::test]
294 async fn test_decode_with_encryption() {
295 // Sample packet data: packet_id = 3, payload = "Hello, encrypted world!"
296 let packet_id = 3;
297 let payload = b"Hello, encrypted world!";
298
299 // Define encryption key and IV
300 let key = [0x00u8; 16]; // Example key
301
302 // Build the packet with encryption enabled (no compression)
303 let packet = build_packet(packet_id, payload, false, Some(&key), Some(&key));
304
305 // Initialize the decoder with encryption enabled
306 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
307 decoder.set_encryption(&key);
308
309 // Attempt to decode
310 let raw_packet = decoder.get_raw_packet().await.expect("Decoding failed");
311
312 assert_eq!(raw_packet.id, packet_id);
313 assert_eq!(raw_packet.payload.as_ref(), payload);
314 }
315
316 /// Test decoding with both compression and encryption
317 #[tokio::test]
318 async fn test_decode_with_compression_and_encryption() {
319 // Sample packet data: packet_id = 4, payload = "Hello, compressed and encrypted world!"
320 let packet_id = 4;
321 let payload = b"Hello, compressed and encrypted world!";
322
323 // Define encryption key and IV
324 let key = [0x01u8; 16]; // Example key
325 let iv = [0x01u8; 16]; // Example IV
326
327 // Build the packet with both compression and encryption enabled
328 let packet = build_packet(packet_id, payload, true, Some(&key), Some(&iv));
329
330 // Initialize the decoder with both compression and encryption enabled
331 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
332 decoder.set_compression(1000);
333 decoder.set_encryption(&key);
334
335 // Attempt to decode
336 let raw_packet = decoder.get_raw_packet().await.expect("Decoding failed");
337
338 assert_eq!(raw_packet.id, packet_id);
339 assert_eq!(raw_packet.payload.as_ref(), payload);
340 }
341
342 /// Test decoding with invalid compressed data
343 #[tokio::test]
344 async fn test_decode_with_invalid_compressed_data() {
345 // Sample packet data: packet_id = 5, payload_len = 10, but compressed data is invalid
346 let data_len = 10; // Expected decompressed size
347 let invalid_compressed_data = vec![0xFF, 0xFF, 0xFF]; // Invalid Zlib data
348
349 // Build the packet with compression enabled but invalid compressed data
350 let mut buffer = Vec::new();
351 let data_len_varint = VarInt(data_len);
352 buffer.write_var_int(&data_len_varint).unwrap();
353 buffer.write_slice(&invalid_compressed_data).unwrap();
354
355 // Calculate packet length: VarInt(data_len) + invalid compressed data
356 let packet_len = buffer.len() as i32;
357 let packet_len_varint = VarInt(packet_len);
358
359 // Create a new buffer for the entire packet
360 let mut packet_buffer = Vec::new();
361 packet_buffer.write_var_int(&packet_len_varint).unwrap();
362 packet_buffer.write_slice(&buffer).unwrap();
363
364 let packet_bytes = packet_buffer;
365
366 // Initialize the decoder with compression enabled
367 let mut decoder = TCPNetworkDecoder::new(&packet_bytes[..]);
368 decoder.set_compression(1000);
369
370 // Attempt to decode and expect a decompression error
371 let result = decoder.get_raw_packet().await;
372
373 if result.is_ok() {
374 panic!("This should have errored!");
375 }
376 }
377
378 /// Test decoding with a zero-length packet
379 #[tokio::test]
380 async fn test_decode_with_zero_length_packet() {
381 // Sample packet data: packet_id = 7, payload = "" (empty)
382 let packet_id = 7;
383 let payload = b"";
384
385 // Build the packet without compression and encryption
386 let packet = build_packet(packet_id, payload, false, None, None);
387
388 // Initialize the decoder without compression and encryption
389 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
390
391 // Attempt to decode and expect a read error
392 let raw_packet = decoder.get_raw_packet().await.unwrap();
393 assert_eq!(raw_packet.id, packet_id);
394 assert_eq!(raw_packet.payload.as_ref(), payload);
395 }
396
397 /// Test decoding with maximum length packet
398 #[tokio::test]
399 async fn test_decode_with_maximum_length_packet() {
400 // Sample packet data: packet_id = 8, payload = "A" repeated MAX_PACKET_SIZE times
401 // Sample packet data: packet_id = 8, payload = "A" repeated (MAX_PACKET_SIZE - 1) times
402 let packet_id = 8;
403 let payload = vec![0x41u8; MAX_PACKET_SIZE as usize - 1]; // "A" repeated
404
405 // Build the packet with compression enabled
406 let packet = build_packet(packet_id, &payload, true, None, None);
407 println!("Built packet (with compression, maximum length): {packet:?}");
408
409 // Initialize the decoder with compression enabled
410 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
411 decoder.set_compression(MAX_PACKET_SIZE as usize + 1);
412
413 // Attempt to decode
414 let result = decoder.get_raw_packet().await;
415
416 let raw_packet = result.unwrap();
417 assert_eq!(raw_packet.id, packet_id);
418 assert_eq!(raw_packet.payload.as_ref(), payload);
419 }
420}
421 */