extract authentication client v2
This commit is contained in:
parent
f9bf750b65
commit
e7fc40632d
@ -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()) {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
74
pumpkin/src/server/bikeshed_key_store.rs
Normal file
74
pumpkin/src/server/bikeshed_key_store.rs
Normal 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)
|
||||||
|
}
|
||||||
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user