extract authentication client v2

This commit is contained in:
StripedMonkey 2024-09-09 21:43:01 -04:00
parent f9bf750b65
commit e7fc40632d
4 changed files with 108 additions and 59 deletions

View File

@ -1,7 +1,6 @@
use std::{collections::HashMap, net::IpAddr, sync::Arc}; use std::{collections::HashMap, net::IpAddr, sync::Arc};
use base64::{engine::general_purpose, Engine}; use base64::{engine::general_purpose, Engine};
use num_bigint::BigInt;
use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG}; use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG};
use pumpkin_core::ProfileAction; use pumpkin_core::ProfileAction;
use pumpkin_protocol::Property; use pumpkin_protocol::Property;
@ -93,15 +92,10 @@ pub fn unpack_textures(property: Property, config: &TextureConfig) -> Result<(),
for texture in textures.textures { for texture in textures.textures {
let url = let url =
Url::parse(&texture.1.url).map_err(|e| TextureError::InvalidURL(e.to_string()))?; 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(()) 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> { pub fn is_texture_url_valid(url: Url, config: &TextureConfig) -> Result<(), TextureError> {
let scheme = url.scheme(); let scheme = url.scheme();
if !config.allowed_url_schemes.contains(&scheme.to_string()) { if !config.allowed_url_schemes.contains(&scheme.to_string()) {

View File

@ -6,7 +6,7 @@ use pumpkin_core::text::TextComponent;
use pumpkin_protocol::{ use pumpkin_protocol::{
client::{ client::{
config::{CConfigAddResourcePack, CFinishConfig, CKnownPacks, CRegistryData}, config::{CConfigAddResourcePack, CFinishConfig, CKnownPacks, CRegistryData},
login::{CEncryptionRequest, CLoginSuccess, CSetCompression}, login::{CLoginSuccess, CSetCompression},
status::{CPingResponse, CStatusResponse}, status::{CPingResponse, CStatusResponse},
}, },
server::{ server::{
@ -17,8 +17,6 @@ use pumpkin_protocol::{
}, },
ConnectionState, KnownPack, CURRENT_MC_PROTOCOL, ConnectionState, KnownPack, CURRENT_MC_PROTOCOL,
}; };
use rsa::Pkcs1v15Encrypt;
use sha1::{Digest, Sha1};
use crate::{ use crate::{
client::authentication::{self, GameProfile}, client::authentication::{self, GameProfile},
@ -27,10 +25,7 @@ use crate::{
server::{Server, CURRENT_MC_VERSION}, server::{Server, CURRENT_MC_VERSION},
}; };
use super::{ use super::{authentication::unpack_textures, Client, PlayerConfig};
authentication::{auth_digest, unpack_textures},
Client, EncryptionError, PlayerConfig,
};
/// Processes incoming Packets from the Client to the Server /// Processes incoming Packets from the Client to the Server
/// Implements the `Client` Packets /// Implements the `Client` Packets
@ -101,14 +96,7 @@ impl Client {
// TODO: check config for encryption // TODO: check config for encryption
let verify_token: [u8; 4] = rand::random(); let verify_token: [u8; 4] = rand::random();
let public_key_der = &server.public_key_der; self.send_packet(&server.encryption_request(&verify_token, BASIC_CONFIG.online_mode));
let packet = CEncryptionRequest::new(
"",
public_key_der,
&verify_token,
BASIC_CONFIG.online_mode, // TODO
);
self.send_packet(&packet);
} }
pub async fn handle_encryption_response( pub async fn handle_encryption_response(
@ -116,22 +104,15 @@ impl Client {
server: &Arc<Server>, server: &Arc<Server>,
encryption_response: SEncryptionResponse, encryption_response: SEncryptionResponse,
) { ) {
let shared_secret = server let shared_secret = server.decrypt(&encryption_response.shared_secret).unwrap();
.private_key
.decrypt(Pkcs1v15Encrypt, &encryption_response.shared_secret)
.map_err(|_| EncryptionError::FailedDecrypt)
.unwrap();
self.enable_encryption(&shared_secret) self.enable_encryption(&shared_secret)
.unwrap_or_else(|e| self.kick(&e.to_string())); .unwrap_or_else(|e| self.kick(&e.to_string()));
let mut gameprofile = self.gameprofile.lock(); let mut gameprofile = self.gameprofile.lock();
if BASIC_CONFIG.online_mode { if BASIC_CONFIG.online_mode {
let hash = Sha1::new() let hash = server.digest_secret(&shared_secret);
.chain_update(&shared_secret)
.chain_update(&server.public_key_der)
.finalize();
let hash = auth_digest(&hash);
let ip = self.address.lock().ip(); let ip = self.address.lock().ip();
match authentication::authenticate( match authentication::authenticate(
&gameprofile.as_ref().unwrap().name, &gameprofile.as_ref().unwrap().name,
@ -231,7 +212,7 @@ impl Client {
id: "core", id: "core",
version: "1.21", version: "1.21",
}])); }]));
dbg!("login achnowlaged"); dbg!("login acknowledged");
} }
pub fn handle_client_information_config( pub fn handle_client_information_config(
&self, &self,

View File

@ -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<Vec<u8>, 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)
}

View File

@ -1,4 +1,5 @@
use base64::{engine::general_purpose, Engine}; use base64::{engine::general_purpose, Engine};
use bikeshed_key_store::BikeShedKeyStore;
use image::GenericImageView; use image::GenericImageView;
use mio::Token; use mio::Token;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
@ -6,6 +7,7 @@ use pumpkin_config::{BasicConfiguration, BASIC_CONFIG};
use pumpkin_core::GameMode; use pumpkin_core::GameMode;
use pumpkin_entity::EntityId; use pumpkin_entity::EntityId;
use pumpkin_plugin::PluginLoader; use pumpkin_plugin::PluginLoader;
use pumpkin_protocol::client::login::CEncryptionRequest;
use pumpkin_protocol::{ use pumpkin_protocol::{
client::config::CPluginMessage, ClientPacket, Players, Sample, StatusResponse, VarInt, Version, client::config::CPluginMessage, ClientPacket, Players, Sample, StatusResponse, VarInt, Version,
CURRENT_MC_PROTOCOL, CURRENT_MC_PROTOCOL,
@ -25,8 +27,8 @@ use std::{
use pumpkin_inventory::drag_handler::DragHandler; use pumpkin_inventory::drag_handler::DragHandler;
use pumpkin_inventory::{Container, OpenContainer}; use pumpkin_inventory::{Container, OpenContainer};
use pumpkin_registry::Registry; use pumpkin_registry::Registry;
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
use crate::client::EncryptionError;
use crate::{ use crate::{
client::Client, client::Client,
commands::{default_dispatcher, dispatcher::CommandDispatcher}, commands::{default_dispatcher, dispatcher::CommandDispatcher},
@ -34,13 +36,11 @@ use crate::{
world::World, world::World,
}; };
mod bikeshed_key_store;
pub const CURRENT_MC_VERSION: &str = "1.21.1"; pub const CURRENT_MC_VERSION: &str = "1.21.1";
pub struct Server { pub struct Server {
pub public_key: RsaPublicKey, key_store: BikeShedKeyStore,
pub private_key: RsaPrivateKey,
pub public_key_der: Box<[u8]>,
pub plugin_loader: PluginLoader, pub plugin_loader: PluginLoader,
pub command_dispatcher: Arc<CommandDispatcher<'static>>, pub command_dispatcher: Arc<CommandDispatcher<'static>>,
@ -74,14 +74,7 @@ impl Server {
let cached_server_brand = Self::build_brand(); let cached_server_brand = Self::build_brand();
// TODO: only create when needed // TODO: only create when needed
log::debug!("Creating encryption keys..."); let key_store = BikeShedKeyStore::new();
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 auth_client = if BASIC_CONFIG.online_mode { let auth_client = if BASIC_CONFIG.online_mode {
Some( Some(
reqwest::Client::builder() reqwest::Client::builder()
@ -110,14 +103,12 @@ impl Server {
// 0 is invalid // 0 is invalid
entity_id: 2.into(), entity_id: 2.into(),
worlds: vec![Arc::new(world)], worlds: vec![Arc::new(world)],
public_key,
cached_server_brand,
private_key,
command_dispatcher: Arc::new(command_dispatcher), command_dispatcher: Arc::new(command_dispatcher),
auth_client,
key_store,
status_response, status_response,
status_response_json, status_response_json,
public_key_der, cached_server_brand,
auth_client,
} }
} }
@ -174,6 +165,23 @@ impl Server {
self.entity_id.fetch_add(1, Ordering::SeqCst) 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<Vec<u8>, 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<u8> { pub fn build_brand() -> Vec<u8> {
let brand = "Pumpkin"; let brand = "Pumpkin";
let mut buf = vec![]; let mut buf = vec![];
@ -233,12 +241,4 @@ impl Server {
general_purpose::STANDARD.encode_string(image, &mut result); general_purpose::STANDARD.encode_string(image, &mut result);
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)
}
} }