Allow disable encryption

This commit is contained in:
Snowiiii 2024-09-28 13:03:31 +02:00
parent 92fef82bda
commit 1ddaca1406
14 changed files with 117 additions and 101 deletions

1
Cargo.lock generated
View File

@ -2031,6 +2031,7 @@ dependencies = [
"log",
"num-derive",
"num-traits",
"pumpkin-config",
"pumpkin-core",
"pumpkin-macros",
"pumpkin-world",

View File

@ -1,24 +1,39 @@
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
// Packet compression
/// Packet compression
pub struct CompressionConfig {
/// Is compression enabled ?
pub enabled: bool,
#[serde(flatten)]
pub compression_info: CompressionInfo,
}
#[derive(Deserialize, Serialize, Clone)]
/// We have this in a Seperate struct so we can use it outside of the Config
pub struct CompressionInfo {
/// The compression threshold used when compression is enabled
pub compression_threshold: u32,
pub threshold: u32,
/// A value between 0..9
/// 1 = Optimize for the best speed of encoding.
/// 9 = Optimize for the size of data being encoded.
pub compression_level: u32,
pub level: u32,
}
impl Default for CompressionInfo {
fn default() -> Self {
Self {
threshold: 256,
level: 4,
}
}
}
impl Default for CompressionConfig {
fn default() -> Self {
Self {
enabled: true,
compression_threshold: 256,
compression_level: 4,
compression_info: Default::default(),
}
}
}

View File

@ -20,7 +20,7 @@ pub use pvp::PVPConfig;
pub use rcon::RCONConfig;
mod commands;
mod compression;
pub mod compression;
mod pvp;
mod rcon;

View File

@ -4,6 +4,7 @@ version.workspace = true
edition.workspace = true
[dependencies]
pumpkin-config = { path = "../pumpkin-config" }
pumpkin-macros = { path = "../pumpkin-macros" }
pumpkin-world = { path = "../pumpkin-world" }
pumpkin-core = { path = "../pumpkin-core" }

View File

@ -19,7 +19,7 @@ type Cipher = cfb8::Decryptor<aes::Aes128>;
pub struct PacketDecoder {
buf: BytesMut,
decompress_buf: BytesMut,
compression: Option<u32>,
compression: bool,
cipher: Option<Cipher>,
}
@ -45,7 +45,7 @@ impl PacketDecoder {
let packet_len_len = VarInt(packet_len).written_size();
let mut data;
if self.compression.is_some() {
if self.compression {
r = &r[..packet_len as usize];
let data_len = VarInt::decode(&mut r).map_err(|_| PacketError::TooLong)?.0;
@ -96,19 +96,26 @@ impl PacketDecoder {
}))
}
pub fn enable_encryption(&mut self, key: &[u8; 16]) {
assert!(self.cipher.is_none(), "encryption is already enabled");
pub fn set_encryption(&mut self, key: Option<&[u8; 16]>) {
if let Some(key) = key {
assert!(self.cipher.is_none(), "encryption is already enabled");
let mut cipher = Cipher::new_from_slices(key, key).expect("invalid key");
let mut cipher = Cipher::new_from_slices(key, key).expect("invalid key");
// Don't forget to decrypt the data we already have.
Self::decrypt_bytes(&mut cipher, &mut self.buf);
// Don't forget to decrypt the data we already have.
self.cipher = Some(cipher);
Self::decrypt_bytes(&mut cipher, &mut self.buf);
self.cipher = Some(cipher);
} else {
assert!(self.cipher.is_some(), "encryption is already disabled");
self.cipher = None;
}
}
/// Enables ZLib Deompression
pub fn set_compression(&mut self, compression: Option<u32>) {
/// Sets ZLib Deompression
pub fn set_compression(&mut self, compression: bool) {
self.compression = compression;
}

View File

@ -2,6 +2,7 @@ use std::io::Write;
use aes::cipher::{generic_array::GenericArray, BlockEncryptMut, BlockSizeUser, KeyIvInit};
use bytes::{BufMut, BytesMut};
use pumpkin_config::compression::CompressionInfo;
use std::io::Read;
@ -19,7 +20,7 @@ type Cipher = cfb8::Encryptor<aes::Aes128>;
pub struct PacketEncoder {
buf: BytesMut,
compress_buf: Vec<u8>,
compression: Option<(u32, u32)>,
compression: Option<CompressionInfo>,
cipher: Option<Cipher>,
}
@ -40,10 +41,10 @@ impl PacketEncoder {
let data_len = self.buf.len() - start_len;
if let Some((threshold, compression_level)) = self.compression {
if data_len > threshold as usize {
if let Some(compression) = &self.compression {
if data_len > compression.threshold as usize {
let mut z =
ZlibEncoder::new(&self.buf[start_len..], Compression::new(compression_level));
ZlibEncoder::new(&self.buf[start_len..], Compression::new(compression.level));
self.compress_buf.clear();
@ -117,13 +118,20 @@ impl PacketEncoder {
Ok(())
}
pub fn enable_encryption(&mut self, key: &[u8; 16]) {
assert!(self.cipher.is_none(), "encryption is already enabled");
self.cipher = Some(Cipher::new_from_slices(key, key).expect("invalid key"));
pub fn set_encryption(&mut self, key: Option<&[u8; 16]>) {
if let Some(key) = key {
assert!(self.cipher.is_none(), "encryption is already enabled");
self.cipher = Some(Cipher::new_from_slices(key, key).expect("invalid key"));
} else {
assert!(self.cipher.is_some(), "encryption is disabled");
self.cipher = None;
}
}
/// Enables ZLib Compression
pub fn set_compression(&mut self, compression: Option<(u32, u32)>) {
pub fn set_compression(&mut self, compression: Option<CompressionInfo>) {
self.compression = compression;
}

View File

@ -26,10 +26,9 @@ impl BlockId {
.iter();
let block_state = match properties {
Some(properties) => match block_states.find(|state| &state.properties == properties) {
Some(state) => state,
None => return Err(WorldError::BlockStateIdNotFound),
},
Some(properties) => block_states
.find(|state| &state.properties == properties)
.ok_or_else(|| WorldError::BlockStateIdNotFound)?,
None => block_states
.find(|state| state.is_default)
.expect("Every Block should have at least 1 default state"),

View File

@ -135,6 +135,8 @@ impl Level {
}
}
pub fn get_block() {}
/// Reads/Generates many chunks in a world
/// MUST be called from a tokio runtime thread
///
@ -147,7 +149,6 @@ impl Level {
) {
chunks.into_par_iter().for_each(|at| {
if is_alive {
dbg!("a");
return;
}
let mut loaded_chunks = self.loaded_chunks.lock();
@ -235,29 +236,21 @@ impl Level {
// Read the file using the offset and size
let mut file_buf = {
let seek_result = region_file.seek(std::io::SeekFrom::Start(offset));
if seek_result.is_err() {
return Err(WorldError::RegionIsInvalid);
}
region_file
.seek(std::io::SeekFrom::Start(offset))
.map_err(|_| WorldError::RegionIsInvalid)?;
let mut out = vec![0; size];
let read_result = region_file.read_exact(&mut out);
if read_result.is_err() {
return Err(WorldError::RegionIsInvalid);
}
region_file
.read_exact(&mut out)
.map_err(|_| WorldError::RegionIsInvalid)?;
out
};
// TODO: check checksum to make sure chunk is not corrupted
let header = file_buf.drain(0..5).collect_vec();
let compression = match Compression::from_byte(header[4]) {
Some(c) => c,
None => {
return Err(WorldError::Compression(
CompressionError::UnknownCompression,
))
}
};
let compression = Compression::from_byte(header[4])
.ok_or_else(|| WorldError::Compression(CompressionError::UnknownCompression))?;
let size = u32::from_be_bytes(header[..4].try_into().unwrap());
@ -277,23 +270,15 @@ impl Level {
Compression::Gzip => {
let mut z = GzDecoder::new(&compressed_data[..]);
let mut chunk_data = Vec::with_capacity(compressed_data.len());
match z.read_to_end(&mut chunk_data) {
Ok(_) => {}
Err(e) => {
return Err(CompressionError::GZipError(e));
}
}
z.read_to_end(&mut chunk_data)
.map_err(CompressionError::GZipError)?;
Ok(chunk_data)
}
Compression::Zlib => {
let mut z = ZlibDecoder::new(&compressed_data[..]);
let mut chunk_data = Vec::with_capacity(compressed_data.len());
match z.read_to_end(&mut chunk_data) {
Ok(_) => {}
Err(e) => {
return Err(CompressionError::ZlibError(e));
}
}
z.read_to_end(&mut chunk_data)
.map_err(CompressionError::ZlibError)?;
Ok(chunk_data)
}
Compression::None => Ok(compressed_data),

View File

@ -107,7 +107,7 @@ impl Client {
) {
let shared_secret = server.decrypt(&encryption_response.shared_secret).unwrap();
self.enable_encryption(&shared_secret)
self.set_encryption(Some(&shared_secret))
.unwrap_or_else(|e| self.kick(&e.to_string()));
let mut gameprofile = self.gameprofile.lock();
@ -115,6 +115,7 @@ impl Client {
if BASIC_CONFIG.online_mode {
let hash = server.digest_secret(&shared_secret);
let ip = self.address.lock().ip();
match authentication::authenticate(
&gameprofile.as_ref().unwrap().name,
&hash,
@ -123,15 +124,15 @@ impl Client {
)
.await
{
Ok(p) => {
Ok(profile) => {
// Check if player should join
if let Some(p) = &p.profile_actions {
if let Some(actions) = &profile.profile_actions {
if !ADVANCED_CONFIG
.authentication
.player_profile
.allow_banned_players
{
if !p.is_empty() {
if !actions.is_empty() {
self.kick("Your account can't join");
}
} else {
@ -140,28 +141,27 @@ impl Client {
.player_profile
.allowed_actions
{
if !p.contains(allowed) {
if !actions.contains(allowed) {
self.kick("Your account can't join");
}
}
}
}
*gameprofile = Some(p);
*gameprofile = Some(profile);
}
Err(e) => self.kick(&e.to_string()),
}
}
for property in &gameprofile.as_ref().unwrap().properties {
// TODO: use this (this was the todo here before, ill add it again cuz its prob here for a reason)
let _ = unpack_textures(property, &ADVANCED_CONFIG.authentication.textures);
unpack_textures(property, &ADVANCED_CONFIG.authentication.textures)
.unwrap_or_else(|e| self.kick(&e.to_string()));
}
// enable compression
if ADVANCED_CONFIG.packet_compression.enabled {
let threshold = ADVANCED_CONFIG.packet_compression.compression_threshold;
let level = ADVANCED_CONFIG.packet_compression.compression_level;
self.send_packet(&CSetCompression::new(threshold.into()));
self.set_compression(Some((threshold, level)));
let compression = ADVANCED_CONFIG.packet_compression.compression_info.clone();
self.send_packet(&CSetCompression::new(compression.threshold.into()));
self.set_compression(Some(compression));
}
if let Some(profile) = gameprofile.as_ref() {

View File

@ -16,6 +16,7 @@ use authentication::GameProfile;
use crossbeam::atomic::AtomicCell;
use mio::{event::Event, net::TcpStream, Token};
use parking_lot::Mutex;
use pumpkin_config::compression::CompressionInfo;
use pumpkin_protocol::{
bytebuf::{packet_id::Packet, DeserializerError},
client::{config::CConfigDisconnect, login::CLoginDisconnect},
@ -151,24 +152,29 @@ impl Client {
client_packets_queue.push(packet);
}
/// Enables encryption
pub fn enable_encryption(
/// Sets the Packet encryption
pub fn set_encryption(
&self,
shared_secret: &[u8], // decrypted
shared_secret: Option<&[u8]>, // decrypted
) -> Result<(), EncryptionError> {
self.encryption
.store(true, std::sync::atomic::Ordering::Relaxed);
let crypt_key: [u8; 16] = shared_secret
.try_into()
.map_err(|_| EncryptionError::SharedWrongLength)?;
self.dec.lock().enable_encryption(&crypt_key);
self.enc.lock().enable_encryption(&crypt_key);
if let Some(shared_secret) = shared_secret {
self.encryption
.store(true, std::sync::atomic::Ordering::Relaxed);
let crypt_key: [u8; 16] = shared_secret
.try_into()
.map_err(|_| EncryptionError::SharedWrongLength)?;
self.dec.lock().set_encryption(Some(&crypt_key));
self.enc.lock().set_encryption(Some(&crypt_key));
} else {
self.dec.lock().set_encryption(None);
self.enc.lock().set_encryption(None);
}
Ok(())
}
/// Compression threshold, Compression level
pub fn set_compression(&self, compression: Option<(u32, u32)>) {
self.dec.lock().set_compression(compression.map(|v| v.0));
/// Sets the Packet compression
pub fn set_compression(&self, compression: Option<CompressionInfo>) {
self.dec.lock().set_compression(compression.is_some());
self.enc.lock().set_compression(compression);
}
@ -200,14 +206,11 @@ impl Client {
/// Processes all packets send by the client
pub async fn process_packets(&self, server: &Arc<Server>) {
while let Some(mut packet) = self.client_packets_queue.lock().pop() {
match self.handle_packet(server, &mut packet).await {
Ok(_) => {}
Err(e) => {
let text = format!("Error while reading incoming packet {}", e);
log::error!("{}", text);
self.kick(&text)
}
};
let _ = self.handle_packet(server, &mut packet).await.map_err(|e| {
let text = format!("Error while reading incoming packet {}", e);
log::error!("{}", text);
self.kick(&text)
});
}
}

View File

@ -131,7 +131,7 @@ impl Player {
self.kick(TextComponent::text("Invalid movement"));
return;
}
if !position_rotation.yaw.is_finite() || !position_rotation.pitch.is_finite() {
if position_rotation.yaw.is_infinite() || position_rotation.pitch.is_infinite() {
self.kick(TextComponent::text("Invalid rotation"));
return;
}

View File

@ -140,13 +140,10 @@ impl RCONClient {
}
}
// If we get a close here, we might have a reply, which we still want to write.
match self.poll(server, password).await {
Ok(()) => {}
Err(e) => {
log::error!("rcon error: {e}");
self.closed = true;
}
}
let _ = self.poll(server, password).await.map_err(|e| {
log::error!("rcon error: {e}");
self.closed = true;
});
}
self.closed
}

View File

@ -58,7 +58,7 @@ impl CachedStatus {
}
pub fn build_response(config: &BasicConfiguration) -> StatusResponse {
let icon_path = concat!(env!("CARGO_MANIFEST_DIR"), "/icon.png");
let icon_path = "/icon.png";
let icon = if Path::new(icon_path).exists() {
Some(Self::load_icon(icon_path))
} else {

View File

@ -103,11 +103,11 @@ impl Server {
};
// Basically the default world
// TODO: select default from config
let world = self.worlds[0].clone();
let world = &self.worlds[0];
let player = Arc::new(Player::new(client, world.clone(), entity_id, gamemode));
world.add_player(token, player.clone());
(player, world)
(player, world.clone())
}
pub fn try_get_container(