1use bytes::{Buf, BufMut, Bytes};
4use uuid::Uuid;
5
6use crate::types::{self, GameProfile, ProfileProperty, ProtocolError};
7use crate::varint;
8
9use super::Packet;
10
11const MAX_ENCRYPTED_FIELD: usize = 256;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct LoginStartPacket {
21 pub username: String,
23 pub uuid: Uuid,
25}
26
27impl Packet for LoginStartPacket {
28 const PACKET_ID: i32 = 0x00;
29
30 fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
31 let username = types::read_string_max(buf, 16)?;
32 let uuid = types::read_uuid(buf)?;
33 Ok(Self { username, uuid })
34 }
35
36 fn encode(&self, buf: &mut impl BufMut) {
37 types::write_string(buf, &self.username);
38 types::write_uuid(buf, self.uuid);
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct EncryptionRequestPacket {
45 pub server_id: String,
47 pub public_key: Vec<u8>,
49 pub verify_token: Vec<u8>,
51 pub should_authenticate: bool,
53}
54
55impl Packet for EncryptionRequestPacket {
56 const PACKET_ID: i32 = 0x01;
57
58 #[allow(clippy::cast_sign_loss)]
59 fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
60 let server_id = types::read_string_max(buf, 20)?;
61 let pk_len = varint::read_var_int(buf)? as usize;
62 if pk_len > MAX_ENCRYPTED_FIELD {
63 return Err(ProtocolError::ByteArrayTooLong {
64 length: pk_len,
65 max: MAX_ENCRYPTED_FIELD,
66 });
67 }
68 if buf.remaining() < pk_len {
69 return Err(ProtocolError::UnexpectedEof);
70 }
71 let public_key = buf.copy_to_bytes(pk_len).to_vec();
72 let vt_len = varint::read_var_int(buf)? as usize;
73 if vt_len > MAX_ENCRYPTED_FIELD {
74 return Err(ProtocolError::ByteArrayTooLong {
75 length: vt_len,
76 max: MAX_ENCRYPTED_FIELD,
77 });
78 }
79 if buf.remaining() < vt_len {
80 return Err(ProtocolError::UnexpectedEof);
81 }
82 let verify_token = buf.copy_to_bytes(vt_len).to_vec();
83 let should_authenticate = if buf.has_remaining() {
84 buf.get_u8() != 0
85 } else {
86 true
87 };
88 Ok(Self {
89 server_id,
90 public_key,
91 verify_token,
92 should_authenticate,
93 })
94 }
95
96 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
97 fn encode(&self, buf: &mut impl BufMut) {
98 types::write_string(buf, &self.server_id);
99 varint::write_var_int(buf, self.public_key.len() as i32);
100 buf.put_slice(&self.public_key);
101 varint::write_var_int(buf, self.verify_token.len() as i32);
102 buf.put_slice(&self.verify_token);
103 buf.put_u8(u8::from(self.should_authenticate));
104 }
105}
106
107#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct EncryptionResponsePacket {
110 pub shared_secret: Vec<u8>,
112 pub verify_token: Vec<u8>,
114}
115
116impl Packet for EncryptionResponsePacket {
117 const PACKET_ID: i32 = 0x01;
118
119 #[allow(clippy::cast_sign_loss)]
120 fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
121 let ss_len = varint::read_var_int(buf)? as usize;
122 if ss_len > MAX_ENCRYPTED_FIELD {
123 return Err(ProtocolError::ByteArrayTooLong {
124 length: ss_len,
125 max: MAX_ENCRYPTED_FIELD,
126 });
127 }
128 if buf.remaining() < ss_len {
129 return Err(ProtocolError::UnexpectedEof);
130 }
131 let shared_secret = buf.copy_to_bytes(ss_len).to_vec();
132 let vt_len = varint::read_var_int(buf)? as usize;
133 if vt_len > MAX_ENCRYPTED_FIELD {
134 return Err(ProtocolError::ByteArrayTooLong {
135 length: vt_len,
136 max: MAX_ENCRYPTED_FIELD,
137 });
138 }
139 if buf.remaining() < vt_len {
140 return Err(ProtocolError::UnexpectedEof);
141 }
142 let verify_token = buf.copy_to_bytes(vt_len).to_vec();
143 Ok(Self {
144 shared_secret,
145 verify_token,
146 })
147 }
148
149 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
150 fn encode(&self, buf: &mut impl BufMut) {
151 varint::write_var_int(buf, self.shared_secret.len() as i32);
152 buf.put_slice(&self.shared_secret);
153 varint::write_var_int(buf, self.verify_token.len() as i32);
154 buf.put_slice(&self.verify_token);
155 }
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
160pub struct LoginSuccessPacket {
161 pub uuid: Uuid,
163 pub username: String,
165 pub properties: Vec<ProfileProperty>,
167}
168
169impl LoginSuccessPacket {
170 #[must_use]
172 pub fn from_profile(profile: &GameProfile) -> Self {
173 Self {
174 uuid: profile.id,
175 username: profile.name.clone(),
176 properties: profile.properties.clone(),
177 }
178 }
179}
180
181impl Packet for LoginSuccessPacket {
182 const PACKET_ID: i32 = 0x02;
183
184 fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
185 let uuid = types::read_uuid(buf)?;
186 let username = types::read_string_max(buf, 16)?;
187 let properties = types::read_properties(buf)?;
188 Ok(Self {
189 uuid,
190 username,
191 properties,
192 })
193 }
194
195 fn encode(&self, buf: &mut impl BufMut) {
196 types::write_uuid(buf, self.uuid);
197 types::write_string(buf, &self.username);
198 types::write_properties(buf, &self.properties);
199 }
200}
201
202#[derive(Debug, Clone, PartialEq, Eq)]
204pub struct SetCompressionPacket {
205 pub threshold: i32,
208}
209
210impl Packet for SetCompressionPacket {
211 const PACKET_ID: i32 = 0x03;
212
213 fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
214 let threshold = varint::read_var_int(buf)?;
215 Ok(Self { threshold })
216 }
217
218 fn encode(&self, buf: &mut impl BufMut) {
219 varint::write_var_int(buf, self.threshold);
220 }
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
226pub struct LoginPluginRequestPacket {
227 pub message_id: i32,
229 pub channel: String,
231 pub data: Bytes,
233}
234
235impl Packet for LoginPluginRequestPacket {
236 const PACKET_ID: i32 = 0x04;
237
238 fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
239 let message_id = varint::read_var_int(buf)?;
240 let channel = types::read_string(buf)?;
241 let data = buf.copy_to_bytes(buf.remaining());
242 Ok(Self {
243 message_id,
244 channel,
245 data,
246 })
247 }
248
249 fn encode(&self, buf: &mut impl BufMut) {
250 varint::write_var_int(buf, self.message_id);
251 types::write_string(buf, &self.channel);
252 buf.put_slice(&self.data);
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
258pub struct LoginPluginResponsePacket {
259 pub message_id: i32,
261 pub successful: bool,
263 pub data: Bytes,
265}
266
267impl Packet for LoginPluginResponsePacket {
268 const PACKET_ID: i32 = 0x02;
269
270 fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
271 let message_id = varint::read_var_int(buf)?;
272 if !buf.has_remaining() {
273 return Err(ProtocolError::UnexpectedEof);
274 }
275 let successful = buf.get_u8() != 0;
276 let data = if successful {
277 buf.copy_to_bytes(buf.remaining())
278 } else {
279 Bytes::new()
280 };
281 Ok(Self {
282 message_id,
283 successful,
284 data,
285 })
286 }
287
288 fn encode(&self, buf: &mut impl BufMut) {
289 varint::write_var_int(buf, self.message_id);
290 buf.put_u8(u8::from(self.successful));
291 if self.successful {
292 buf.put_slice(&self.data);
293 }
294 }
295}
296
297#[derive(Debug, Clone, PartialEq, Eq)]
300pub struct LoginAcknowledgedPacket;
301
302impl Packet for LoginAcknowledgedPacket {
303 const PACKET_ID: i32 = 0x03;
304
305 fn decode(_buf: &mut impl Buf) -> Result<Self, ProtocolError> {
306 Ok(Self)
307 }
308
309 fn encode(&self, _buf: &mut impl BufMut) {}
310}
311
312#[derive(Debug, Clone, PartialEq, Eq)]
314pub struct LoginDisconnectPacket {
315 pub reason: String,
317}
318
319impl Packet for LoginDisconnectPacket {
320 const PACKET_ID: i32 = 0x00;
321
322 fn decode(buf: &mut impl Buf) -> Result<Self, ProtocolError> {
323 let reason = types::read_string(buf)?;
324 Ok(Self { reason })
325 }
326
327 fn encode(&self, buf: &mut impl BufMut) {
328 types::write_string(buf, &self.reason);
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335 use proptest::prelude::*;
336
337 fn profile_property_strategy() -> impl Strategy<Value = ProfileProperty> {
338 (
339 ".{0,32}", ".{0,1024}", prop::option::weighted(0.5, ".{0,1024}"), )
343 .prop_map(|(name, value, signature)| ProfileProperty {
344 name,
345 value,
346 signature,
347 })
348 }
349
350 proptest! {
351 #[test]
352 fn login_start_roundtrip(
353 username in ".{0,16}",
354 u in any::<u128>()
355 ) {
356 let packet = LoginStartPacket {
357 username,
358 uuid: Uuid::from_u128(u),
359 };
360 let mut buf = Vec::new();
361 packet.encode(&mut buf);
362 let decoded = LoginStartPacket::decode(&mut &buf[..]).unwrap();
363 prop_assert_eq!(decoded, packet);
364 }
365
366 #[test]
367 fn encryption_request_roundtrip(
368 server_id in ".{0,20}",
369 public_key in prop::collection::vec(any::<u8>(), 0..=MAX_ENCRYPTED_FIELD),
370 verify_token in prop::collection::vec(any::<u8>(), 0..=MAX_ENCRYPTED_FIELD),
371 should_authenticate in any::<bool>()
372 ) {
373 let packet = EncryptionRequestPacket {
374 server_id,
375 public_key,
376 verify_token,
377 should_authenticate,
378 };
379 let mut buf = Vec::new();
380 packet.encode(&mut buf);
381 let decoded = EncryptionRequestPacket::decode(&mut &buf[..]).unwrap();
382 prop_assert_eq!(decoded, packet);
383 }
384
385 #[test]
386 fn encryption_response_roundtrip(
387 shared_secret in prop::collection::vec(any::<u8>(), 0..=MAX_ENCRYPTED_FIELD),
388 verify_token in prop::collection::vec(any::<u8>(), 0..=MAX_ENCRYPTED_FIELD)
389 ) {
390 let packet = EncryptionResponsePacket {
391 shared_secret,
392 verify_token,
393 };
394 let mut buf = Vec::new();
395 packet.encode(&mut buf);
396 let decoded = EncryptionResponsePacket::decode(&mut &buf[..]).unwrap();
397 prop_assert_eq!(decoded, packet);
398 }
399
400 #[test]
401 fn login_success_roundtrip(
402 u in any::<u128>(),
403 username in ".{0,16}",
404 properties in prop::collection::vec(profile_property_strategy(), 0..4)
405 ) {
406 let packet = LoginSuccessPacket {
407 uuid: Uuid::from_u128(u),
408 username,
409 properties,
410 };
411 let mut buf = Vec::new();
412 packet.encode(&mut buf);
413 let decoded = LoginSuccessPacket::decode(&mut &buf[..]).unwrap();
414 prop_assert_eq!(decoded, packet);
415 }
416
417 #[test]
418 fn set_compression_roundtrip(threshold in any::<i32>()) {
419 let packet = SetCompressionPacket { threshold };
420 let mut buf = Vec::new();
421 packet.encode(&mut buf);
422 let decoded = SetCompressionPacket::decode(&mut &buf[..]).unwrap();
423 prop_assert_eq!(decoded, packet);
424 }
425
426 #[test]
427 fn login_plugin_request_roundtrip(
428 message_id in any::<i32>(),
429 channel in ".{0,128}",
430 data in prop::collection::vec(any::<u8>(), 0..1024)
431 ) {
432 let packet = LoginPluginRequestPacket {
433 message_id,
434 channel,
435 data: Bytes::from(data),
436 };
437 let mut buf = Vec::new();
438 packet.encode(&mut buf);
439 let decoded = LoginPluginRequestPacket::decode(&mut &buf[..]).unwrap();
440 prop_assert_eq!(decoded, packet);
441 }
442
443 #[test]
444 fn login_plugin_response_roundtrip(
445 message_id in any::<i32>(),
446 successful in any::<bool>(),
447 data in prop::collection::vec(any::<u8>(), 0..1024)
448 ) {
449 let packet = LoginPluginResponsePacket {
450 message_id,
451 successful,
452 data: if successful { Bytes::from(data) } else { Bytes::new() },
453 };
454 let mut buf = Vec::new();
455 packet.encode(&mut buf);
456 let decoded = LoginPluginResponsePacket::decode(&mut &buf[..]).unwrap();
457 prop_assert_eq!(decoded, packet);
458 }
459
460 #[test]
461 fn login_acknowledged_roundtrip(packet in Just(LoginAcknowledgedPacket)) {
462 let mut buf = Vec::new();
463 packet.encode(&mut buf);
464 let decoded = LoginAcknowledgedPacket::decode(&mut &buf[..]).unwrap();
465 prop_assert_eq!(decoded, packet);
466 }
467
468 #[test]
469 fn login_disconnect_roundtrip(reason in ".{0,1024}") {
470 let packet = LoginDisconnectPacket { reason };
471 let mut buf = Vec::new();
472 packet.encode(&mut buf);
473 let decoded = LoginDisconnectPacket::decode(&mut &buf[..]).unwrap();
474 prop_assert_eq!(decoded, packet);
475 }
476 }
477
478 #[test]
479 fn encryption_request_rejects_oversized_public_key() {
480 let packet = EncryptionRequestPacket {
481 server_id: String::new(),
482 public_key: vec![0xAA; MAX_ENCRYPTED_FIELD + 1],
483 verify_token: vec![0xBB; 4],
484 should_authenticate: true,
485 };
486 let mut buf = Vec::new();
487 packet.encode(&mut buf);
488 let result = EncryptionRequestPacket::decode(&mut &buf[..]);
489 assert!(matches!(
490 result,
491 Err(ProtocolError::ByteArrayTooLong {
492 length,
493 max: MAX_ENCRYPTED_FIELD,
494 }) if length == MAX_ENCRYPTED_FIELD + 1
495 ));
496 }
497
498 #[test]
499 fn encryption_request_rejects_oversized_verify_token() {
500 let packet = EncryptionRequestPacket {
501 server_id: String::new(),
502 public_key: vec![0xAA; 128],
503 verify_token: vec![0xBB; MAX_ENCRYPTED_FIELD + 1],
504 should_authenticate: true,
505 };
506 let mut buf = Vec::new();
507 packet.encode(&mut buf);
508 let result = EncryptionRequestPacket::decode(&mut &buf[..]);
509 assert!(matches!(
510 result,
511 Err(ProtocolError::ByteArrayTooLong {
512 length,
513 max: MAX_ENCRYPTED_FIELD,
514 }) if length == MAX_ENCRYPTED_FIELD + 1
515 ));
516 }
517
518 #[test]
519 fn encryption_response_rejects_oversized_shared_secret() {
520 let packet = EncryptionResponsePacket {
521 shared_secret: vec![0xAA; MAX_ENCRYPTED_FIELD + 1],
522 verify_token: vec![0xBB; 4],
523 };
524 let mut buf = Vec::new();
525 packet.encode(&mut buf);
526 let result = EncryptionResponsePacket::decode(&mut &buf[..]);
527 assert!(matches!(
528 result,
529 Err(ProtocolError::ByteArrayTooLong {
530 length,
531 max: MAX_ENCRYPTED_FIELD,
532 }) if length == MAX_ENCRYPTED_FIELD + 1
533 ));
534 }
535
536 #[test]
537 fn encryption_response_rejects_oversized_verify_token() {
538 let packet = EncryptionResponsePacket {
539 shared_secret: vec![0xAA; 128],
540 verify_token: vec![0xBB; MAX_ENCRYPTED_FIELD + 1],
541 };
542 let mut buf = Vec::new();
543 packet.encode(&mut buf);
544 let result = EncryptionResponsePacket::decode(&mut &buf[..]);
545 assert!(matches!(
546 result,
547 Err(ProtocolError::ByteArrayTooLong {
548 length,
549 max: MAX_ENCRYPTED_FIELD,
550 }) if length == MAX_ENCRYPTED_FIELD + 1
551 ));
552 }
553}