steel_protocol/packet_writer.rs
1//! # Steel Protocol Packet Writer
2//!
3//! This module contains the implementation of the packet writer.
4/*
5Credit to https://github.com/Pumpkin-MC/Pumpkin/ for this implementation.
6*/
7
8use std::{
9 io,
10 pin::Pin,
11 task::{Context, Poll},
12};
13
14use aes::cipher::KeyIvInit;
15use thiserror::Error;
16use tokio::io::{AsyncWrite, AsyncWriteExt};
17
18use crate::{
19 packet_traits::EncodedPacket,
20 utils::{Aes128Cfb8Enc, PacketError, StreamEncryptor},
21};
22
23// raw -> compress -> encrypt
24/// A writer that can encrypt data.
25pub enum EncryptionWriter<W: AsyncWrite + Unpin> {
26 /// A writer that encrypts data.
27 Encrypt(Box<StreamEncryptor<W>>),
28 /// A writer that does not encrypt data.
29 None(W),
30}
31
32impl<W: AsyncWrite + Unpin> EncryptionWriter<W> {
33 /// Upgrades the writer to encrypt data.
34 ///
35 /// # Panics
36 /// - If the writer is already encrypting data.
37 #[must_use]
38 pub fn upgrade(self, cipher: Aes128Cfb8Enc) -> Self {
39 match self {
40 Self::None(stream) => Self::Encrypt(Box::new(StreamEncryptor::new(cipher, stream))),
41 Self::Encrypt(_) => panic!("Cannot upgrade a stream that already has a cipher!"),
42 }
43 }
44}
45
46impl<W: AsyncWrite + Unpin> AsyncWrite for EncryptionWriter<W> {
47 fn poll_write(
48 self: Pin<&mut Self>,
49 cx: &mut Context<'_>,
50 buf: &[u8],
51 ) -> Poll<io::Result<usize>> {
52 match self.get_mut() {
53 Self::Encrypt(writer) => {
54 let writer = Pin::new(writer);
55 writer.poll_write(cx, buf)
56 }
57 Self::None(writer) => {
58 let writer = Pin::new(writer);
59 writer.poll_write(cx, buf)
60 }
61 }
62 }
63
64 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
65 match self.get_mut() {
66 Self::Encrypt(writer) => {
67 let writer = Pin::new(writer);
68 writer.poll_flush(cx)
69 }
70 Self::None(writer) => {
71 let writer = Pin::new(writer);
72 writer.poll_flush(cx)
73 }
74 }
75 }
76
77 fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
78 match self.get_mut() {
79 Self::Encrypt(writer) => {
80 let writer = Pin::new(writer);
81 writer.poll_shutdown(cx)
82 }
83 Self::None(writer) => {
84 let writer = Pin::new(writer);
85 writer.poll_shutdown(cx)
86 }
87 }
88 }
89}
90
91/// Encoder: Server -> Client
92/// Supports `ZLib` endecoding/compression
93/// Supports Aes128 Encryption
94pub struct TCPNetworkEncoder<W: AsyncWrite + Unpin> {
95 writer: EncryptionWriter<W>,
96}
97
98impl<W: AsyncWrite + Unpin> TCPNetworkEncoder<W> {
99 /// Creates a new `TCPNetworkEncoder`.
100 pub const fn new(writer: W) -> Self {
101 Self {
102 writer: EncryptionWriter::None(writer),
103 }
104 }
105
106 /// NOTE: Encryption can only be set; a minecraft stream cannot go back to being unencrypted
107 ///
108 /// # Panics
109 /// - If the stream is already encrypted.
110 /// - If the key is invalid.
111 pub fn set_encryption(&mut self, key: &[u8; 16]) {
112 if matches!(self.writer, EncryptionWriter::Encrypt(_)) {
113 panic!("Cannot upgrade a stream that already has a cipher!");
114 }
115 let cipher = Aes128Cfb8Enc::new_from_slices(key, key).expect("invalid key");
116 replace_with::replace_with_or_abort(&mut self.writer, |encoder| encoder.upgrade(cipher));
117 }
118
119 /// Writes a packet to the stream.
120 ///
121 /// # Errors
122 /// - If the packet fails to write.
123 /// - If the stream fails to flush.
124 pub async fn write_packet(&mut self, packet: &EncodedPacket) -> Result<(), PacketError> {
125 self.writer
126 .write_all(&packet.encoded_data)
127 .await
128 .map_err(|e| PacketError::EncryptionFailed(e.to_string()))?;
129
130 self.writer
131 .flush()
132 .await
133 .map_err(|e| PacketError::EncryptionFailed(e.to_string()))
134 }
135}
136
137/// An error that occurs when the compression level is invalid.
138#[derive(Error, Debug)]
139#[error("Invalid compression Level")]
140pub struct CompressionLevelError;
141
142/* TODO: Tests.
143#[cfg(test)]
144mod tests {
145 use std::io::Read;
146
147 use super::*;
148 use crate::java::client::status::CStatusResponse;
149 use crate::packet::Packet;
150 use crate::{ClientPacket, ReadingError};
151 use aes::Aes128;
152 use cfb8::Decryptor as Cfb8Decryptor;
153 use cfb8::cipher::AsyncStreamCipher;
154 use flate2::read::ZlibDecoder;
155 use pumpkin_data::packet::client:STATUS_STATUS_RESPONSE;
156 use pumpkin_macros::packet;
157 use serde::Serialize;
158
159 /// Define a custom packet for testing maximum packet size
160 #[derive(Serialize)]
161 #[packet(STATUS_STATUS_RESPONSE)]
162 pub struct MaxSizePacket {
163 data: Vec<u8>,
164 }
165
166 impl MaxSizePacket {
167 pub fn new(size: usize) -> Self {
168 Self {
169 data: vec![0xAB; size], // Fill with arbitrary data
170 }
171 }
172 }
173
174 /// Helper function to decode a `VarInt` from bytes
175 fn decode_varint(buffer: &mut &[u8]) -> Result<i32, ReadingError> {
176 Ok(buffer.get_var_int()?.0)
177 }
178
179 /// Helper function to decompress data using libdeflater's Zlib decompressor
180 fn decompress_zlib(data: &[u8], expected_size: usize) -> Result<Vec<u8>, std::io::Error> {
181 assert!(!data.is_empty());
182 let mut decompressed = vec![0u8; expected_size];
183 ZlibDecoder::new(data).read_exact(&mut decompressed)?;
184 Ok(decompressed)
185 }
186
187 /// Helper function to decrypt data using AES-128 CFB-8 mode
188 fn decrypt_aes128(encrypted_data: &mut [u8], key: &[u8; 16], iv: &[u8; 16]) {
189 let decryptor = Cfb8Decryptor::<Aes128>::new_from_slices(key, iv).expect("Invalid key/iv");
190 decryptor.decrypt(encrypted_data);
191 }
192
193 /// Helper function to build a packet with optional compression and encryption
194 async fn build_packet_with_encoder<T: ClientPacket>(
195 packet: &T,
196 compression_info: Option<(CompressionThreshold, CompressionLevel)>,
197 key: Option<&[u8; 16]>,
198 ) -> Box<[u8]> {
199 let mut buf = Vec::new();
200 let mut encoder = TCPNetworkEncoder::new(&mut buf);
201 if let Some(compression_info) = compression_info {
202 encoder.set_compression(compression_info);
203 }
204
205 if let Some(key) = key {
206 encoder.set_encryption(key);
207 }
208
209 let mut packet_buf = Vec::new();
210 let writer = &mut packet_buf;
211 writer.write_var_int(&VarInt(T::PACKET_ID)).unwrap();
212 packet.write_packet_data(writer).unwrap();
213
214 encoder.write_packet(packet_buf.into()).await.unwrap();
215
216 buf.into_boxed_slice()
217 }
218
219 /// Test encoding without compression and encryption
220 #[tokio::test]
221 async fn test_encode_without_compression_and_encryption() {
222 // Create a CStatusResponse packet
223 let packet =
224 CStatusResponse::new(String::from("{\"description\": \"A Minecraft Server\"}"));
225
226 // Build the packet without compression and encryption
227 let packet_bytes = build_packet_with_encoder(&packet, None, None).await;
228
229 // Decode the packet manually to verify correctness
230 let mut buffer = &packet_bytes[..];
231
232 // Read packet length VarInt
233 let packet_length = decode_varint(&mut buffer).expect("Failed to decode packet length");
234 assert_eq!(
235 packet_length as usize,
236 buffer.len(),
237 "Packet length mismatch"
238 );
239
240 // Read packet ID VarInt
241 let decoded_packet_id = decode_varint(&mut buffer).expect("Failed to decode packet ID");
242 assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID);
243
244 // Remaining buffer is the payload
245 // We need to obtain the expected payload
246 let mut expected_payload = Vec::new();
247 packet.write_packet_data(&mut expected_payload).unwrap();
248
249 assert_eq!(buffer, expected_payload);
250 }
251
252 /// Test encoding with compression
253 #[tokio::test]
254 async fn test_encode_with_compression() {
255 // Create a CStatusResponse packet
256 let packet =
257 CStatusResponse::new("{\"description\": \"A Minecraft Server\"}".parse().unwrap());
258
259 // Build the packet with compression enabled
260 let packet_bytes = build_packet_with_encoder(&packet, Some((0, 6)), None).await;
261
262 // Decode the packet manually to verify correctness
263 let mut buffer = &packet_bytes[..];
264
265 // Read packet length VarInt
266 let packet_length = decode_varint(&mut buffer).expect("Failed to decode packet length");
267 assert_eq!(
268 packet_length as usize,
269 buffer.len(),
270 "Packet length mismatch"
271 );
272
273 // Read data length VarInt (uncompressed data length)
274 let data_length = decode_varint(&mut buffer).expect("Failed to decode data length");
275 let mut expected_payload = Vec::new();
276 packet.write_packet_data(&mut expected_payload).unwrap();
277 let uncompressed_data_length =
278 VarInt(CStatusResponse::PACKET_ID).written_size() + expected_payload.len();
279 assert_eq!(data_length as usize, uncompressed_data_length);
280
281 // Remaining buffer is the compressed data
282 let compressed_data = buffer;
283
284 // Decompress the data
285 let decompressed_data = decompress_zlib(compressed_data, data_length as usize)
286 .expect("Failed to decompress data");
287
288 // Verify packet ID and payload
289 let mut decompressed_buffer = &decompressed_data[..];
290
291 // Read packet ID VarInt
292 let decoded_packet_id =
293 decode_varint(&mut decompressed_buffer).expect("Failed to decode packet ID");
294 assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID);
295
296 // Remaining buffer is the payload
297 assert_eq!(decompressed_buffer, expected_payload);
298 }
299
300 /// Test encoding with encryption
301 #[tokio::test]
302 async fn test_encode_with_encryption() {
303 // Create a CStatusResponse packet
304 let packet =
305 CStatusResponse::new("{\"description\": \"A Minecraft Server\"}".parse().unwrap());
306
307 // Encryption key and IV (IV is the same as key in this case)
308 let key = [0x00u8; 16]; // Example key
309
310 // Build the packet with encryption enabled (no compression)
311 let mut packet_bytes = build_packet_with_encoder(&packet, None, Some(&key)).await;
312
313 // Decrypt the packet
314 decrypt_aes128(&mut packet_bytes, &key, &key);
315
316 // Decode the packet manually to verify correctness
317 let mut buffer = &packet_bytes[..];
318
319 // Read packet length VarInt
320 let packet_length = decode_varint(&mut buffer).expect("Failed to decode packet length");
321 assert_eq!(
322 packet_length as usize,
323 buffer.len(),
324 "Packet length mismatch"
325 );
326
327 // Read packet ID VarInt
328 let decoded_packet_id = decode_varint(&mut buffer).expect("Failed to decode packet ID");
329 assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID);
330
331 // Remaining buffer is the payload
332 let mut expected_payload = Vec::new();
333 packet.write_packet_data(&mut expected_payload).unwrap();
334 assert_eq!(buffer, expected_payload);
335 }
336
337 /// Test encoding with both compression and encryption
338 #[tokio::test]
339 async fn test_encode_with_compression_and_encryption() {
340 // Create a CStatusResponse packet
341 let packet =
342 CStatusResponse::new("{\"description\": \"A Minecraft Server\"}".parse().unwrap());
343
344 // Encryption key and IV (IV is the same as key in this case)
345 let key = [0x01u8; 16]; // Example key
346
347 // Build the packet with both compression and encryption enabled
348 // Compression threshold is set to 0 to force compression
349 let mut packet_bytes = build_packet_with_encoder(&packet, Some((0, 6)), Some(&key)).await;
350
351 // Decrypt the packet
352 decrypt_aes128(&mut packet_bytes, &key, &key);
353
354 // Decode the packet manually to verify correctness
355 let mut buffer = &packet_bytes[..];
356
357 // Read packet length VarInt
358 let packet_length = decode_varint(&mut buffer).expect("Failed to decode packet length");
359 assert_eq!(
360 packet_length as usize,
361 buffer.len(),
362 "Packet length mismatch"
363 );
364
365 // Read data length VarInt (uncompressed data length)
366 let data_length = decode_varint(&mut buffer).expect("Failed to decode data length");
367 let mut expected_payload = Vec::new();
368 packet.write_packet_data(&mut expected_payload).unwrap();
369 let uncompressed_data_length =
370 VarInt(CStatusResponse::PACKET_ID).written_size() + expected_payload.len();
371 assert_eq!(data_length as usize, uncompressed_data_length);
372
373 // Remaining buffer is the compressed data
374 let compressed_data = buffer;
375
376 // Decompress the data
377 let decompressed_data = decompress_zlib(compressed_data, data_length as usize)
378 .expect("Failed to decompress data");
379
380 // Verify packet ID and payload
381 let mut decompressed_buffer = &decompressed_data[..];
382
383 // Read packet ID VarInt
384 let decoded_packet_id =
385 decode_varint(&mut decompressed_buffer).expect("Failed to decode packet ID");
386 assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID);
387
388 // Remaining buffer is the payload
389 assert_eq!(decompressed_buffer, expected_payload);
390 }
391
392 /// Test encoding with zero-length payload
393 #[tokio::test]
394 async fn test_encode_with_zero_length_payload() {
395 // Create a CStatusResponse packet with empty payload
396 let packet = CStatusResponse::new(String::from(""));
397
398 // Build the packet without compression and encryption
399 let packet_bytes = build_packet_with_encoder(&packet, None, None).await;
400
401 // Decode the packet manually to verify correctness
402 let mut buffer = &packet_bytes[..];
403
404 // Read packet length VarInt
405 let packet_length = decode_varint(&mut buffer).expect("Failed to decode packet length");
406 assert_eq!(
407 packet_length as usize,
408 buffer.len(),
409 "Packet length mismatch"
410 );
411
412 // Read packet ID VarInt
413 let decoded_packet_id = decode_varint(&mut buffer).expect("Failed to decode packet ID");
414 assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID);
415
416 // Remaining buffer is the payload (empty)
417 let mut expected_payload = Vec::new();
418 packet.write_packet_data(&mut expected_payload).unwrap();
419
420 assert_eq!(
421 buffer.len(),
422 expected_payload.len(),
423 "Payload length mismatch"
424 );
425 assert_eq!(buffer, expected_payload);
426 }
427
428 /// Test encoding with maximum length payload
429 #[tokio::test]
430 async fn test_encode_with_maximum_string_length() {
431 // Maximum allowed string length is 32767 bytes
432 let max_string_length = 32767;
433 let payload_str = "A".repeat(max_string_length);
434 let packet = CStatusResponse::new(payload_str);
435
436 // Build the packet without compression and encryption
437 let packet_bytes = build_packet_with_encoder(&packet, None, None).await;
438
439 // Verify that the packet size does not exceed MAX_PACKET_SIZE as usize
440 assert!(
441 packet_bytes.len() <= MAX_PACKET_SIZE as usize,
442 "Packet size exceeds maximum allowed size"
443 );
444
445 // Decode the packet manually to verify correctness
446 let mut buffer = &packet_bytes[..];
447
448 // Read packet length VarInt
449 let packet_length = decode_varint(&mut buffer).expect("Failed to decode packet length");
450 assert_eq!(
451 packet_length as usize,
452 buffer.len(),
453 "Packet length mismatch"
454 );
455
456 // Read packet ID VarInt
457 let decoded_packet_id = decode_varint(&mut buffer).expect("Failed to decode packet ID");
458 // Assume packet ID is 0 for CStatusResponse
459 assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID);
460
461 // Remaining buffer is the payload
462 let mut expected_payload = Vec::new();
463 packet.write_packet_data(&mut expected_payload).unwrap();
464
465 assert_eq!(buffer, expected_payload);
466 }
467
468 /// Test encoding a packet that exceeds MAX_PACKET_SIZE as usize
469 #[tokio::test]
470 #[should_panic(expected = "TooLong")]
471 async fn test_encode_packet_exceeding_maximum_size() {
472 // Create a custom packet with data exceeding MAX_PACKET_SIZE as usize
473 let data_size = MAX_PACKET_SIZE as usize + 1; // Exceed by 1 byte
474 let packet = MaxSizePacket::new(data_size);
475
476 // Build the packet without compression and encryption
477 // This should panic with PacketEncodeError::TooLong
478 build_packet_with_encoder(&packet, None, None).await;
479 }
480
481 /// Test encoding with a small payload that should not be compressed
482 #[tokio::test]
483 async fn test_encode_small_payload_no_compression() {
484 // Create a CStatusResponse packet with small payload
485 let packet = CStatusResponse::new(String::from("Hi"));
486
487 // Build the packet with compression enabled
488 // Compression threshold is set to a value higher than payload length
489 let packet_bytes = build_packet_with_encoder(&packet, Some((10, 6)), None).await;
490
491 // Decode the packet manually to verify that it was not compressed
492 let mut buffer = &packet_bytes[..];
493
494 // Read packet length VarInt
495 let packet_length = decode_varint(&mut buffer).expect("Failed to decode packet length");
496 assert_eq!(
497 packet_length as usize,
498 buffer.len(),
499 "Packet length mismatch"
500 );
501
502 // Read data length VarInt (should be 0 indicating no compression)
503 let data_length = decode_varint(&mut buffer).expect("Failed to decode data length");
504 assert_eq!(
505 data_length, 0,
506 "Data length should be 0 indicating no compression"
507 );
508
509 // Read packet ID VarInt
510 let decoded_packet_id = decode_varint(&mut buffer).expect("Failed to decode packet ID");
511 assert_eq!(decoded_packet_id, CStatusResponse::PACKET_ID);
512
513 // Remaining buffer is the payload
514 let mut expected_payload = Vec::new();
515 packet.write_packet_data(&mut expected_payload).unwrap();
516
517 assert_eq!(buffer, expected_payload);
518 }
519}
520*/