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 (packet_buffer, packet_data_start) = 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 let mut decompressed = vec![0; decompressed_len];
136 let mut decoder = ZlibDecoder::new(&mut cursor);
137 decoder
138 .read_exact(&mut decompressed)
139 .map_err(|e| PacketError::DecompressionFailed(e.to_string()))?;
140
141 let mut overflow = [0];
142 if decoder
143 .read(&mut overflow)
144 .map_err(|e| PacketError::DecompressionFailed(e.to_string()))?
145 != 0
146 {
147 Err(PacketError::DecompressionFailed(format!(
148 "decompressed packet exceeds declared length of {decompressed_len}"
149 )))?;
150 }
151 (decompressed, 0)
152 } else {
153 // Validate that we are not less than the compression threshold
154 if raw_packet_len > threshold.get() as _ {
155 Err(PacketError::NotCompressed)?;
156 }
157
158 // The rest of the packet data is uncompressed.
159 let packet_data_start = cursor.position() as usize;
160 (packet_data, packet_data_start)
161 }
162 } else {
163 (packet_data, 0)
164 };
165
166 // Parse the packet ID while retaining the buffer that already owns the payload.
167 let mut cursor = io::Cursor::new(&packet_buffer[packet_data_start..]);
168 let packet_id = VarInt::read(&mut cursor)?.0;
169 let payload_start = packet_data_start + cursor.position() as usize;
170
171 Ok(RawPacket::from_buffer(
172 packet_id,
173 packet_buffer,
174 payload_start,
175 ))
176 }
177}
178
179#[cfg(test)]
180mod payload_tests {
181 use std::{io::Write as _, num::NonZeroU32};
182
183 use flate2::{Compression, write::ZlibEncoder};
184
185 use super::TCPNetworkDecoder;
186
187 const VARINT_DATA_MASK: u8 = 0x7f;
188 const VARINT_CONTINUE_BIT: u8 = 0x80;
189
190 fn write_varint(mut value: u32, output: &mut Vec<u8>) {
191 loop {
192 if value <= u32::from(VARINT_DATA_MASK) {
193 output.push(value as u8);
194 return;
195 }
196 output.push((value as u8 & VARINT_DATA_MASK) | VARINT_CONTINUE_BIT);
197 value >>= 7;
198 }
199 }
200
201 fn packet_data(packet_id: u32, payload: &[u8]) -> Vec<u8> {
202 let mut data = Vec::with_capacity(payload.len() + 5);
203 write_varint(packet_id, &mut data);
204 data.extend_from_slice(payload);
205 data
206 }
207
208 fn frame(data: &[u8]) -> Vec<u8> {
209 let mut framed = Vec::with_capacity(data.len() + 5);
210 write_varint(data.len() as u32, &mut framed);
211 framed.extend_from_slice(data);
212 framed
213 }
214
215 #[tokio::test]
216 async fn payload_slice_starts_after_framing_and_packet_id() {
217 let packet_id = 300;
218 let payload = b"payload bytes";
219 let frame = frame(&packet_data(packet_id, payload));
220 let mut decoder = TCPNetworkDecoder::new(frame.as_slice());
221
222 let packet = decoder.get_raw_packet().await.expect("decode plain packet");
223
224 assert_eq!(packet.id, packet_id as i32);
225 assert_eq!(packet.payload(), payload);
226 }
227
228 #[tokio::test]
229 async fn uncompressed_payload_slice_skips_compression_length() {
230 let packet_id = 300;
231 let payload = b"payload bytes";
232 let packet_data = packet_data(packet_id, payload);
233 let mut compression_data = Vec::with_capacity(packet_data.len() + 1);
234 write_varint(0, &mut compression_data);
235 compression_data.extend_from_slice(&packet_data);
236 let frame = frame(&compression_data);
237 let mut decoder = TCPNetworkDecoder::new(frame.as_slice());
238 decoder.set_compression(NonZeroU32::new(256).expect("nonzero threshold"));
239
240 let packet = decoder
241 .get_raw_packet()
242 .await
243 .expect("decode uncompressed packet");
244
245 assert_eq!(packet.id, packet_id as i32);
246 assert_eq!(packet.payload(), payload);
247 }
248
249 #[tokio::test]
250 async fn decompressed_payload_slice_starts_after_packet_id() {
251 let packet_id = 300;
252 let payload = vec![0x5a; 1_024];
253 let packet_data = packet_data(packet_id, &payload);
254 let mut encoder = ZlibEncoder::new(Vec::new(), Compression::fast());
255 encoder
256 .write_all(&packet_data)
257 .expect("compress packet data");
258 let compressed = encoder.finish().expect("finish packet compression");
259 let mut compression_data = Vec::with_capacity(compressed.len() + 5);
260 write_varint(packet_data.len() as u32, &mut compression_data);
261 compression_data.extend_from_slice(&compressed);
262 let frame = frame(&compression_data);
263 let mut decoder = TCPNetworkDecoder::new(frame.as_slice());
264 decoder.set_compression(NonZeroU32::new(256).expect("nonzero threshold"));
265
266 let packet = decoder
267 .get_raw_packet()
268 .await
269 .expect("decode compressed packet");
270
271 assert_eq!(packet.id, packet_id as i32);
272 assert_eq!(packet.payload(), payload);
273 }
274}
275
276/* TODO: Tests.
277#[cfg(test)]
278mod tests {
279
280 use std::io::Write;
281
282 use super::*;
283 use aes::Aes128;
284 use cfb8::Encryptor as Cfb8Encryptor;
285 use cfb8::cipher::AsyncStreamCipher;
286 use flate2::Compression;
287 use flate2::write::ZlibEncoder;
288
289 /// Helper function to compress data using libdeflater's Zlib compressor
290 fn compress_zlib(data: &[u8]) -> Vec<u8> {
291 let mut compressed = Vec::new();
292 ZlibEncoder::new(&mut compressed, Compression::default())
293 .write_all(data)
294 .unwrap();
295 compressed
296 }
297
298 /// Helper function to encrypt data using AES-128 CFB-8 mode
299 fn encrypt_aes128(data: &mut [u8], key: &[u8; 16], iv: &[u8; 16]) {
300 let encryptor = Cfb8Encryptor::<Aes128>::new_from_slices(key, iv).expect("Invalid key/iv");
301 encryptor.encrypt(data);
302 }
303
304 /// Helper function to build a packet with optional compression and encryption
305 fn build_packet(
306 packet_id: i32,
307 payload: &[u8],
308 compress: bool,
309 key: Option<&[u8; 16]>,
310 iv: Option<&[u8; 16]>,
311 ) -> Vec<u8> {
312 let mut buffer = Vec::new();
313
314 if compress {
315 // Create a buffer that includes `packet_id_varint` and payload
316 let mut data_to_compress = Vec::new();
317 let packet_id_varint = VarInt(packet_id);
318 data_to_compress.write_var_int(&packet_id_varint).unwrap();
319 data_to_compress.write_slice(payload).unwrap();
320
321 // Compress the combined data
322 let compressed_payload = compress_zlib(&data_to_compress);
323 let data_len = data_to_compress.len() as i32; // 1 + payload.len()
324 let data_len_varint = VarInt(data_len);
325 buffer.write_var_int(&data_len_varint).unwrap();
326 buffer.write_slice(&compressed_payload).unwrap();
327 } else {
328 // No compression; `data_len` is payload length
329 let packet_id_varint = VarInt(packet_id);
330 buffer.write_var_int(&packet_id_varint).unwrap();
331 buffer.write_slice(payload).unwrap();
332 }
333
334 // Calculate packet length: length of buffer
335 let packet_len = buffer.len() as i32;
336 let packet_len_varint = VarInt(packet_len);
337 let mut packet_length_encoded = Vec::new();
338 {
339 packet_len_varint
340 .encode(&mut packet_length_encoded)
341 .unwrap();
342 }
343
344 // Create a new buffer for the entire packet
345 let mut packet = Vec::new();
346 packet.extend_from_slice(&packet_length_encoded);
347 packet.extend_from_slice(&buffer);
348
349 // Encrypt if key and IV are provided.
350 if let (Some(k), Some(v)) = (key, iv) {
351 encrypt_aes128(&mut packet, k, v);
352 packet
353 } else {
354 packet
355 }
356 }
357
358 /// Test decoding without compression and encryption
359 #[tokio::test]
360 async fn test_decode_without_compression_and_encryption() {
361 // Sample packet data: packet_id = 1, payload = "Hello"
362 let packet_id = 1;
363 let payload = b"Hello";
364
365 // Build the packet without compression and encryption
366 let packet = build_packet(packet_id, payload, false, None, None);
367
368 // Initialize the decoder without compression and encryption
369 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
370
371 // Attempt to decode
372 let raw_packet = decoder.get_raw_packet().await.expect("Decoding failed");
373
374 assert_eq!(raw_packet.id, packet_id);
375 assert_eq!(raw_packet.payload.as_ref(), payload);
376 }
377
378 /// Test decoding with compression
379 #[tokio::test]
380 async fn test_decode_with_compression() {
381 // Sample packet data: packet_id = 2, payload = "Hello, compressed world!"
382 let packet_id = 2;
383 let payload = b"Hello, compressed world!";
384
385 // Build the packet with compression enabled
386 let packet = build_packet(packet_id, payload, true, None, None);
387
388 // Initialize the decoder with compression enabled
389 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
390 // Larger than payload
391 decoder.set_compression(1000);
392
393 // Attempt to decode
394 let raw_packet = decoder.get_raw_packet().await.expect("Decoding failed");
395
396 assert_eq!(raw_packet.id, packet_id);
397 assert_eq!(raw_packet.payload.as_ref(), payload);
398 }
399
400 /// Test decoding with encryption
401 #[tokio::test]
402 async fn test_decode_with_encryption() {
403 // Sample packet data: packet_id = 3, payload = "Hello, encrypted world!"
404 let packet_id = 3;
405 let payload = b"Hello, encrypted world!";
406
407 // Define encryption key and IV
408 let key = [0x00u8; 16]; // Example key
409
410 // Build the packet with encryption enabled (no compression)
411 let packet = build_packet(packet_id, payload, false, Some(&key), Some(&key));
412
413 // Initialize the decoder with encryption enabled
414 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
415 decoder.set_encryption(&key);
416
417 // Attempt to decode
418 let raw_packet = decoder.get_raw_packet().await.expect("Decoding failed");
419
420 assert_eq!(raw_packet.id, packet_id);
421 assert_eq!(raw_packet.payload.as_ref(), payload);
422 }
423
424 /// Test decoding with both compression and encryption
425 #[tokio::test]
426 async fn test_decode_with_compression_and_encryption() {
427 // Sample packet data: packet_id = 4, payload = "Hello, compressed and encrypted world!"
428 let packet_id = 4;
429 let payload = b"Hello, compressed and encrypted world!";
430
431 // Define encryption key and IV
432 let key = [0x01u8; 16]; // Example key
433 let iv = [0x01u8; 16]; // Example IV
434
435 // Build the packet with both compression and encryption enabled
436 let packet = build_packet(packet_id, payload, true, Some(&key), Some(&iv));
437
438 // Initialize the decoder with both compression and encryption enabled
439 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
440 decoder.set_compression(1000);
441 decoder.set_encryption(&key);
442
443 // Attempt to decode
444 let raw_packet = decoder.get_raw_packet().await.expect("Decoding failed");
445
446 assert_eq!(raw_packet.id, packet_id);
447 assert_eq!(raw_packet.payload.as_ref(), payload);
448 }
449
450 /// Test decoding with invalid compressed data
451 #[tokio::test]
452 async fn test_decode_with_invalid_compressed_data() {
453 // Sample packet data: packet_id = 5, payload_len = 10, but compressed data is invalid
454 let data_len = 10; // Expected decompressed size
455 let invalid_compressed_data = vec![0xFF, 0xFF, 0xFF]; // Invalid Zlib data
456
457 // Build the packet with compression enabled but invalid compressed data
458 let mut buffer = Vec::new();
459 let data_len_varint = VarInt(data_len);
460 buffer.write_var_int(&data_len_varint).unwrap();
461 buffer.write_slice(&invalid_compressed_data).unwrap();
462
463 // Calculate packet length: VarInt(data_len) + invalid compressed data
464 let packet_len = buffer.len() as i32;
465 let packet_len_varint = VarInt(packet_len);
466
467 // Create a new buffer for the entire packet
468 let mut packet_buffer = Vec::new();
469 packet_buffer.write_var_int(&packet_len_varint).unwrap();
470 packet_buffer.write_slice(&buffer).unwrap();
471
472 let packet_bytes = packet_buffer;
473
474 // Initialize the decoder with compression enabled
475 let mut decoder = TCPNetworkDecoder::new(&packet_bytes[..]);
476 decoder.set_compression(1000);
477
478 // Attempt to decode and expect a decompression error
479 let result = decoder.get_raw_packet().await;
480
481 if result.is_ok() {
482 panic!("This should have errored!");
483 }
484 }
485
486 /// Test decoding with a zero-length packet
487 #[tokio::test]
488 async fn test_decode_with_zero_length_packet() {
489 // Sample packet data: packet_id = 7, payload = "" (empty)
490 let packet_id = 7;
491 let payload = b"";
492
493 // Build the packet without compression and encryption
494 let packet = build_packet(packet_id, payload, false, None, None);
495
496 // Initialize the decoder without compression and encryption
497 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
498
499 // Attempt to decode and expect a read error
500 let raw_packet = decoder.get_raw_packet().await.unwrap();
501 assert_eq!(raw_packet.id, packet_id);
502 assert_eq!(raw_packet.payload.as_ref(), payload);
503 }
504
505 /// Test decoding with maximum length packet
506 #[tokio::test]
507 async fn test_decode_with_maximum_length_packet() {
508 // Sample packet data: packet_id = 8, payload = "A" repeated MAX_PACKET_SIZE times
509 // Sample packet data: packet_id = 8, payload = "A" repeated (MAX_PACKET_SIZE - 1) times
510 let packet_id = 8;
511 let payload = vec![0x41u8; MAX_PACKET_SIZE as usize - 1]; // "A" repeated
512
513 // Build the packet with compression enabled
514 let packet = build_packet(packet_id, &payload, true, None, None);
515 println!("Built packet (with compression, maximum length): {packet:?}");
516
517 // Initialize the decoder with compression enabled
518 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
519 decoder.set_compression(MAX_PACKET_SIZE as usize + 1);
520
521 // Attempt to decode
522 let result = decoder.get_raw_packet().await;
523
524 let raw_packet = result.unwrap();
525 assert_eq!(raw_packet.id, packet_id);
526 assert_eq!(raw_packet.payload.as_ref(), payload);
527 }
528}
529 */
530
531#[cfg(test)]
532mod compression_security_tests {
533 use std::io::Write;
534
535 use flate2::{Compression, write::ZlibEncoder};
536 use steel_utils::serial::WriteTo;
537
538 use super::*;
539
540 fn compressed_packet(claimed_len: usize, decompressed: &[u8]) -> Vec<u8> {
541 let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
542 encoder
543 .write_all(decompressed)
544 .expect("test payload compression should succeed");
545 let compressed = encoder
546 .finish()
547 .expect("test payload compression should finish");
548
549 let mut packet_data = Vec::new();
550 VarInt::from(claimed_len)
551 .write(&mut packet_data)
552 .expect("test claimed length should encode");
553 packet_data.extend_from_slice(&compressed);
554
555 let mut packet = Vec::new();
556 VarInt::from(packet_data.len())
557 .write(&mut packet)
558 .expect("test packet length should encode");
559 packet.extend_from_slice(&packet_data);
560 packet
561 }
562
563 #[tokio::test]
564 async fn rejects_zlib_stream_expanding_past_declared_length() {
565 // This models a tiny wire payload claiming one byte while expanding to twice the protocol
566 // maximum.
567 let expanded = vec![0; MAX_PACKET_DATA_SIZE * 2];
568 let packet = compressed_packet(1, &expanded);
569 assert!(packet.len() < 32 * 1024);
570
571 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
572 decoder.set_compression(NonZeroU32::MIN);
573
574 assert!(matches!(
575 decoder.get_raw_packet().await,
576 Err(PacketError::DecompressionFailed(_))
577 ));
578 }
579
580 #[tokio::test]
581 async fn rejects_zlib_stream_shorter_than_declared_length() {
582 let packet = compressed_packet(2, &[0]);
583 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
584 decoder.set_compression(NonZeroU32::MIN);
585
586 assert!(matches!(
587 decoder.get_raw_packet().await,
588 Err(PacketError::DecompressionFailed(_))
589 ));
590 }
591
592 #[tokio::test]
593 async fn accepts_zlib_stream_matching_declared_length() {
594 let decompressed = [2, b'h', b'e', b'l', b'l', b'o'];
595 let packet = compressed_packet(decompressed.len(), &decompressed);
596 let mut decoder = TCPNetworkDecoder::new(packet.as_slice());
597 decoder.set_compression(NonZeroU32::MIN);
598
599 let raw_packet = decoder
600 .get_raw_packet()
601 .await
602 .expect("valid compressed packet should decode");
603
604 assert_eq!(raw_packet.id, 2);
605 assert_eq!(raw_packet.payload(), b"hello");
606 }
607}