diff --git a/pumpkin/src/client/authentication.rs b/pumpkin/src/client/authentication.rs index 9af1020ce..0d3a537c4 100644 --- a/pumpkin/src/client/authentication.rs +++ b/pumpkin/src/client/authentication.rs @@ -1,7 +1,6 @@ use std::{collections::HashMap, net::IpAddr, sync::Arc}; use base64::{engine::general_purpose, Engine}; -use num_bigint::BigInt; use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG}; use pumpkin_core::ProfileAction; use pumpkin_protocol::Property; @@ -93,15 +92,10 @@ pub fn unpack_textures(property: Property, config: &TextureConfig) -> Result<(), for texture in textures.textures { let url = Url::parse(&texture.1.url).map_err(|e| TextureError::InvalidURL(e.to_string()))?; - is_texture_url_valid(url, config)? - } + is_texture_url_valid(url, config)? } Ok(()) } -pub fn auth_digest(bytes: &[u8]) -> String { - BigInt::from_signed_bytes_be(bytes).to_str_radix(16) -} - pub fn is_texture_url_valid(url: Url, config: &TextureConfig) -> Result<(), TextureError> { let scheme = url.scheme(); if !config.allowed_url_schemes.contains(&scheme.to_string()) { diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index c8aa6a8bd..817adb245 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -6,7 +6,7 @@ use pumpkin_core::text::TextComponent; use pumpkin_protocol::{ client::{ config::{CConfigAddResourcePack, CFinishConfig, CKnownPacks, CRegistryData}, - login::{CEncryptionRequest, CLoginSuccess, CSetCompression}, + login::{CLoginSuccess, CSetCompression}, status::{CPingResponse, CStatusResponse}, }, server::{ @@ -17,8 +17,6 @@ use pumpkin_protocol::{ }, ConnectionState, KnownPack, CURRENT_MC_PROTOCOL, }; -use rsa::Pkcs1v15Encrypt; -use sha1::{Digest, Sha1}; use crate::{ client::authentication::{self, GameProfile}, @@ -27,10 +25,7 @@ use crate::{ server::{Server, CURRENT_MC_VERSION}, }; -use super::{ - authentication::{auth_digest, unpack_textures}, - Client, EncryptionError, PlayerConfig, -}; +use super::{authentication::unpack_textures, Client, PlayerConfig}; /// Processes incoming Packets from the Client to the Server /// Implements the `Client` Packets @@ -101,14 +96,7 @@ impl Client { // TODO: check config for encryption let verify_token: [u8; 4] = rand::random(); - let public_key_der = &server.public_key_der; - let packet = CEncryptionRequest::new( - "", - public_key_der, - &verify_token, - BASIC_CONFIG.online_mode, // TODO - ); - self.send_packet(&packet); + self.send_packet(&server.encryption_request(&verify_token, BASIC_CONFIG.online_mode)); } pub async fn handle_encryption_response( @@ -116,22 +104,15 @@ impl Client { server: &Arc, encryption_response: SEncryptionResponse, ) { - let shared_secret = server - .private_key - .decrypt(Pkcs1v15Encrypt, &encryption_response.shared_secret) - .map_err(|_| EncryptionError::FailedDecrypt) - .unwrap(); + let shared_secret = server.decrypt(&encryption_response.shared_secret).unwrap(); + self.enable_encryption(&shared_secret) .unwrap_or_else(|e| self.kick(&e.to_string())); let mut gameprofile = self.gameprofile.lock(); if BASIC_CONFIG.online_mode { - let hash = Sha1::new() - .chain_update(&shared_secret) - .chain_update(&server.public_key_der) - .finalize(); - let hash = auth_digest(&hash); + let hash = server.digest_secret(&shared_secret); let ip = self.address.lock().ip(); match authentication::authenticate( &gameprofile.as_ref().unwrap().name, @@ -231,7 +212,7 @@ impl Client { id: "core", version: "1.21", }])); - dbg!("login achnowlaged"); + dbg!("login acknowledged"); } pub fn handle_client_information_config( &self, diff --git a/pumpkin/src/server/bikeshed_key_store.rs b/pumpkin/src/server/bikeshed_key_store.rs new file mode 100644 index 000000000..e3b447af6 --- /dev/null +++ b/pumpkin/src/server/bikeshed_key_store.rs @@ -0,0 +1,74 @@ +use num_bigint::BigInt; +use pumpkin_protocol::client::login::CEncryptionRequest; +use rsa::{traits::PublicKeyParts as _, Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey}; +use sha1::Sha1; +use sha2::Digest; + +use crate::client::EncryptionError; + +pub struct BikeShedKeyStore { + pub _public_key: RsaPublicKey, + pub private_key: RsaPrivateKey, + pub public_key_der: Box<[u8]>, +} + +impl BikeShedKeyStore { + pub fn new() -> Self { + log::debug!("Creating encryption keys..."); + let (public_key, private_key) = Self::generate_keys(); + + let public_key_der = rsa_der::public_key_to_der( + &private_key.n().to_bytes_be(), + &private_key.e().to_bytes_be(), + ) + .into_boxed_slice(); + BikeShedKeyStore { + _public_key: public_key, + private_key, + public_key_der, + } + } + + fn generate_keys() -> (RsaPublicKey, RsaPrivateKey) { + let mut rng = rand::thread_rng(); + + let priv_key = RsaPrivateKey::new(&mut rng, 1024).expect("failed to generate a key"); + let pub_key = RsaPublicKey::from(&priv_key); + (pub_key, priv_key) + } + + pub fn encryption_request<'a>( + &'a self, + server_id: &'a str, + verification_token: &'a [u8; 4], + should_authenticate: bool, + ) -> CEncryptionRequest<'_> { + CEncryptionRequest::new( + server_id, + &self.public_key_der, + verification_token, + should_authenticate, + ) + } + + pub fn decrypt(&self, data: &[u8]) -> Result, EncryptionError> { + let decrypted = self + .private_key + .decrypt(Pkcs1v15Encrypt, data) + .map_err(|_| EncryptionError::FailedDecrypt)?; + Ok(decrypted) + } + + pub fn get_digest(&self, secret: &[u8]) -> String { + auth_digest( + &Sha1::new() + .chain_update(secret) + .chain_update(&self.public_key_der) + .finalize(), + ) + } +} + +pub fn auth_digest(bytes: &[u8]) -> String { + BigInt::from_signed_bytes_be(bytes).to_str_radix(16) +} diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 86c18851b..9569e14c4 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -1,4 +1,5 @@ use base64::{engine::general_purpose, Engine}; +use bikeshed_key_store::BikeShedKeyStore; use image::GenericImageView; use mio::Token; use parking_lot::{Mutex, RwLock}; @@ -6,6 +7,7 @@ use pumpkin_config::{BasicConfiguration, BASIC_CONFIG}; use pumpkin_core::GameMode; use pumpkin_entity::EntityId; use pumpkin_plugin::PluginLoader; +use pumpkin_protocol::client::login::CEncryptionRequest; use pumpkin_protocol::{ client::config::CPluginMessage, ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL, @@ -25,8 +27,8 @@ use std::{ use pumpkin_inventory::drag_handler::DragHandler; use pumpkin_inventory::{Container, OpenContainer}; use pumpkin_registry::Registry; -use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; +use crate::client::EncryptionError; use crate::{ client::Client, commands::{default_dispatcher, dispatcher::CommandDispatcher}, @@ -34,13 +36,11 @@ use crate::{ world::World, }; +mod bikeshed_key_store; pub const CURRENT_MC_VERSION: &str = "1.21.1"; pub struct Server { - pub public_key: RsaPublicKey, - pub private_key: RsaPrivateKey, - pub public_key_der: Box<[u8]>, - + key_store: BikeShedKeyStore, pub plugin_loader: PluginLoader, pub command_dispatcher: Arc>, @@ -74,14 +74,7 @@ impl Server { let cached_server_brand = Self::build_brand(); // TODO: only create when needed - log::debug!("Creating encryption keys..."); - let (public_key, private_key) = Self::generate_keys(); - - let public_key_der = rsa_der::public_key_to_der( - &private_key.n().to_bytes_be(), - &private_key.e().to_bytes_be(), - ) - .into_boxed_slice(); + let key_store = BikeShedKeyStore::new(); let auth_client = if BASIC_CONFIG.online_mode { Some( reqwest::Client::builder() @@ -110,14 +103,12 @@ impl Server { // 0 is invalid entity_id: 2.into(), worlds: vec![Arc::new(world)], - public_key, - cached_server_brand, - private_key, command_dispatcher: Arc::new(command_dispatcher), + auth_client, + key_store, status_response, status_response_json, - public_key_der, - auth_client, + cached_server_brand, } } @@ -174,6 +165,23 @@ impl Server { self.entity_id.fetch_add(1, Ordering::SeqCst) } + pub fn encryption_request<'a>( + &'a self, + verification_token: &'a [u8; 4], + should_authenticate: bool, + ) -> CEncryptionRequest<'_> { + self.key_store + .encryption_request("", verification_token, should_authenticate) + } + + pub fn decrypt(&self, data: &[u8]) -> Result, EncryptionError> { + self.key_store.decrypt(data) + } + + pub fn digest_secret(&self, secret: &[u8]) -> String { + self.key_store.get_digest(secret) + } + pub fn build_brand() -> Vec { let brand = "Pumpkin"; let mut buf = vec![]; @@ -233,12 +241,4 @@ impl Server { general_purpose::STANDARD.encode_string(image, &mut result); result } - - pub fn generate_keys() -> (RsaPublicKey, RsaPrivateKey) { - let mut rng = rand::thread_rng(); - - let priv_key = RsaPrivateKey::new(&mut rng, 1024).expect("failed to generate a key"); - let pub_key = RsaPublicKey::from(&priv_key); - (pub_key, priv_key) - } }