Server RSA
This commit is contained in:
parent
4cc58fbf81
commit
d5a87ed4f5
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -236,6 +236,7 @@ dependencies = [
|
|||||||
"rsa",
|
"rsa",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"spki",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
@ -44,3 +44,4 @@ der = { version = "0.7.10", features = ["alloc", "derive"] }
|
|||||||
aes = "0.8.4"
|
aes = "0.8.4"
|
||||||
cfb8 = { version = "0.8.1", features = ["alloc"] }
|
cfb8 = { version = "0.8.1", features = ["alloc"] }
|
||||||
generic-array = "0.14.7"
|
generic-array = "0.14.7"
|
||||||
|
spki = { version = "0.7.3", features = ["std"] }
|
||||||
|
@ -55,17 +55,17 @@ impl DownstreamConnection {
|
|||||||
pub async fn handle_handshake(&mut self) -> Result<(), Error> {
|
pub async fn handle_handshake(&mut self) -> Result<(), Error> {
|
||||||
use packets::handshake::serverbound::Handshake;
|
use packets::handshake::serverbound::Handshake;
|
||||||
|
|
||||||
let handshake = self
|
let handshake = self.read_specific_packet::<Handshake>().await?;
|
||||||
.read_specific_packet::<Handshake>()
|
|
||||||
.await
|
|
||||||
.ok_or(Error::Unexpected)??;
|
|
||||||
|
|
||||||
match handshake.next_state {
|
match handshake.next_state {
|
||||||
ClientState::Status => {
|
ClientState::Status => {
|
||||||
*self.client_state_mut() = DownstreamConnectionState::StatusRequest;
|
*self.client_state_mut() = DownstreamConnectionState::StatusRequest;
|
||||||
*self.inner_state_mut() = ClientState::Status;
|
*self.inner_state_mut() = ClientState::Status;
|
||||||
}
|
}
|
||||||
ClientState::Login => todo!(),
|
ClientState::Login => {
|
||||||
|
*self.client_state_mut() = DownstreamConnectionState::LoginStart;
|
||||||
|
*self.inner_state_mut() = ClientState::Login;
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.disconnect(Some(
|
self.disconnect(Some(
|
||||||
serde_json::json!({ "text": "Received invalid handshake." }),
|
serde_json::json!({ "text": "Received invalid handshake." }),
|
||||||
@ -79,14 +79,13 @@ impl DownstreamConnection {
|
|||||||
pub async fn handle_status_ping(&mut self, online_player_count: usize) -> Result<(), Error> {
|
pub async fn handle_status_ping(&mut self, online_player_count: usize) -> Result<(), Error> {
|
||||||
// The state just changed from Handshake to Status.
|
// The state just changed from Handshake to Status.
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use packets::status::clientbound::{PingResponse, StatusResponse};
|
use packets::status::{
|
||||||
|
clientbound::{PingResponse, StatusResponse},
|
||||||
|
serverbound::{PingRequest, StatusRequest},
|
||||||
|
};
|
||||||
|
|
||||||
// Read the status request packet.
|
// Read the status request packet.
|
||||||
let Packet::StatusRequest(_status_request) =
|
let _status_request = self.read_specific_packet::<StatusRequest>().await?;
|
||||||
self.read_packet().await.ok_or(Error::Unexpected)??
|
|
||||||
else {
|
|
||||||
return Err(Error::Unexpected);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send the status response packet.
|
// Send the status response packet.
|
||||||
let config = Config::instance();
|
let config = Config::instance();
|
||||||
@ -110,19 +109,101 @@ impl DownstreamConnection {
|
|||||||
}).await?;
|
}).await?;
|
||||||
|
|
||||||
// Read the ping request packet.
|
// Read the ping request packet.
|
||||||
let Packet::PingRequest(ping_request) =
|
let payload = self.read_specific_packet::<PingRequest>().await?.payload;
|
||||||
self.read_packet().await.ok_or(Error::Unexpected)??
|
|
||||||
else {
|
|
||||||
return Err(Error::Unexpected);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Send the ping response packet.
|
// Send the ping response packet.
|
||||||
self.send_packet(PingResponse {
|
self.send_packet(PingResponse { payload }).await?;
|
||||||
payload: ping_request.payload,
|
|
||||||
|
self.disconnect(None).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub async fn handle_login(&mut self) -> Result<(), Error> {
|
||||||
|
// The state just changed from Handshake to Login.
|
||||||
|
use packets::login::{clientbound::LoginSuccess, serverbound::LoginStart};
|
||||||
|
|
||||||
|
// Read login start packet.
|
||||||
|
let login_start = self.read_specific_packet::<LoginStart>().await?;
|
||||||
|
|
||||||
|
// Enable encryption and authenticate with Mojang.
|
||||||
|
self.enable_encryption().await?;
|
||||||
|
|
||||||
|
// Enable compression.
|
||||||
|
self.enable_compression().await?;
|
||||||
|
|
||||||
|
// Send login success packet.
|
||||||
|
self.send_packet(LoginSuccess {
|
||||||
|
// Generate a random UUID if none was provided.
|
||||||
|
uuid: login_start.uuid.unwrap_or(uuid::Uuid::new_v4()),
|
||||||
|
username: login_start.name,
|
||||||
|
properties: vec![],
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self.disconnect(None).await
|
Ok(())
|
||||||
|
}
|
||||||
|
pub async fn enable_encryption(&mut self) -> Result<(), Error> {
|
||||||
|
use crate::protocol::encryption::*;
|
||||||
|
use packets::login::{clientbound::EncryptionRequest, serverbound::EncryptionResponse};
|
||||||
|
use rand::{rngs::StdRng, Rng, SeedableRng};
|
||||||
|
|
||||||
|
assert!(matches!(self.inner_state(), ClientState::Login));
|
||||||
|
|
||||||
|
// RSA keys were generated on startup.
|
||||||
|
let config = Config::instance();
|
||||||
|
let (public_key, private_key) = &config.rsa_key_pair;
|
||||||
|
tracing::trace!(
|
||||||
|
"{}",
|
||||||
|
public_key
|
||||||
|
.serialize()
|
||||||
|
.iter()
|
||||||
|
.map(|b| format!("{b:02X?}"))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Generate a verify token.
|
||||||
|
let mut rng = StdRng::from_entropy();
|
||||||
|
let verify_token: [u8; 16] = rng.gen();
|
||||||
|
|
||||||
|
// Send the encryption request packet.
|
||||||
|
self.send_packet(EncryptionRequest {
|
||||||
|
server_id: "".into(),
|
||||||
|
public_key: public_key.serialize(),
|
||||||
|
verify_token: verify_token.to_vec(),
|
||||||
|
// TODO: Implement Mojang authentication.
|
||||||
|
use_mojang_authentication: false,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Read the encryption response packet.
|
||||||
|
let encryption_response = self.read_specific_packet::<EncryptionResponse>().await?;
|
||||||
|
|
||||||
|
// Verify the response.
|
||||||
|
let decrypted_verify_token = private_key
|
||||||
|
.decrypt(Pkcs1v15Encrypt, &encryption_response.verify_token)
|
||||||
|
.expect("failed to decrypt verify token");
|
||||||
|
if decrypted_verify_token != verify_token {
|
||||||
|
return Err(Error::Invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt the shared secret.
|
||||||
|
let shared_secret = private_key
|
||||||
|
.decrypt(Pkcs1v15Encrypt, &encryption_response.shared_secret)
|
||||||
|
.expect("failed to decrypt shared secret");
|
||||||
|
|
||||||
|
// Enable encryption on the connection.
|
||||||
|
let encryptor =
|
||||||
|
Aes128Cfb8Encryptor::new((&(*shared_secret)).into(), (&(*shared_secret)).into());
|
||||||
|
let decryptor =
|
||||||
|
Aes128Cfb8Decryptor::new((&(*shared_secret)).into(), (&(*shared_secret)).into());
|
||||||
|
self.inner.stream.codec_mut().aes_cipher = Some((encryptor, decryptor, 0));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub async fn enable_compression(&mut self) -> Result<(), Error> {
|
||||||
|
// TODO: Implement compression.
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
pub async fn read_packet(&mut self) -> Option<Result<Packet, Error>> {
|
pub async fn read_packet(&mut self) -> Option<Result<Packet, Error>> {
|
||||||
self.inner.read_packet().await
|
self.inner.read_packet().await
|
||||||
|
@ -78,14 +78,9 @@ impl GenericConnection {
|
|||||||
|
|
||||||
packet
|
packet
|
||||||
}
|
}
|
||||||
pub async fn read_specific_packet<P: TryFrom<Packet>>(&mut self) -> Option<Result<P, Error>> {
|
pub async fn read_specific_packet<P: TryFrom<Packet>>(&mut self) -> Result<P, Error> {
|
||||||
self.read_packet()
|
let packet = self.read_packet().await.ok_or(Error::Unexpected)??;
|
||||||
.await
|
P::try_from(packet).map_err(|_| Error::Unexpected)
|
||||||
.map(|packet| match packet.map(P::try_from) {
|
|
||||||
Ok(Ok(p)) => Ok(p),
|
|
||||||
Ok(Err(_)) => Err(Error::Unexpected),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
pub async fn send_packet<P: Into<Packet>>(&mut self, packet: P) -> Result<(), Error> {
|
pub async fn send_packet<P: Into<Packet>>(&mut self, packet: P) -> Result<(), Error> {
|
||||||
let packet: Packet = packet.into();
|
let packet: Packet = packet.into();
|
||||||
|
@ -25,6 +25,15 @@ impl UpstreamConnection {
|
|||||||
match packet {
|
match packet {
|
||||||
Packet::EncryptionRequest(ref packet) => {
|
Packet::EncryptionRequest(ref packet) => {
|
||||||
// Extract the public key from the packet.
|
// Extract the public key from the packet.
|
||||||
|
tracing::trace!(
|
||||||
|
"{}",
|
||||||
|
packet
|
||||||
|
.public_key
|
||||||
|
.iter()
|
||||||
|
.map(|b| format!("{b:02X?}"))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("")
|
||||||
|
);
|
||||||
let public_key = rsa::RsaPublicKey::parse(&packet.public_key)
|
let public_key = rsa::RsaPublicKey::parse(&packet.public_key)
|
||||||
.expect("Failed to parse RSA public key from packet")
|
.expect("Failed to parse RSA public key from packet")
|
||||||
.1;
|
.1;
|
||||||
|
@ -12,6 +12,8 @@ pub enum Error {
|
|||||||
Unexpected,
|
Unexpected,
|
||||||
#[error("Internal channel disconnected")]
|
#[error("Internal channel disconnected")]
|
||||||
ConnectionChannelDisconnnection,
|
ConnectionChannelDisconnnection,
|
||||||
|
#[error("Invalid response")]
|
||||||
|
Invalid,
|
||||||
}
|
}
|
||||||
impl From<std::io::Error> for Error {
|
impl From<std::io::Error> for Error {
|
||||||
fn from(value: std::io::Error) -> Self {
|
fn from(value: std::io::Error) -> Self {
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use der::{
|
use der::Encode;
|
||||||
asn1::{AnyRef, ObjectIdentifier},
|
use spki::{DecodePublicKey, SubjectPublicKeyInfo};
|
||||||
Decode, DecodeValue, Encode, EncodeValue, Header, Reader, Sequence, Tag,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use crate::protocol::parsing::Parsable;
|
pub use crate::protocol::parsing::Parsable;
|
||||||
pub use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
|
pub use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
|
||||||
@ -9,85 +7,19 @@ pub use generic_array::{
|
|||||||
typenum::{UInt, UTerm, B1},
|
typenum::{UInt, UTerm, B1},
|
||||||
GenericArray,
|
GenericArray,
|
||||||
};
|
};
|
||||||
pub use rsa::{RsaPrivateKey, RsaPublicKey};
|
pub use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey};
|
||||||
pub type Aes128Cfb8Encryptor = cfb8::Encryptor<aes::Aes128>;
|
pub type Aes128Cfb8Encryptor = cfb8::Encryptor<aes::Aes128>;
|
||||||
pub type Aes128Cfb8Decryptor = cfb8::Decryptor<aes::Aes128>;
|
pub type Aes128Cfb8Decryptor = cfb8::Decryptor<aes::Aes128>;
|
||||||
pub type GenericCFB8BlockArray = GenericArray<u8, UInt<UTerm, B1>>;
|
pub type GenericCFB8BlockArray = GenericArray<u8, UInt<UTerm, B1>>;
|
||||||
|
|
||||||
impl Parsable for RsaPublicKey {
|
impl Parsable for RsaPublicKey {
|
||||||
fn parse(data: &[u8]) -> nom::IResult<&[u8], Self> {
|
fn parse(data: &[u8]) -> nom::IResult<&[u8], Self> {
|
||||||
let spki = SubjectPublicKeyInfo::from_der(data).unwrap();
|
Ok((&[], RsaPublicKey::from_public_key_der(data).unwrap()))
|
||||||
|
|
||||||
let modulus = rsa::BigUint::from_bytes_be(spki.subject_public_key.modulus.as_bytes());
|
|
||||||
let exponent =
|
|
||||||
rsa::BigUint::from_bytes_be(spki.subject_public_key.public_exponent.as_bytes());
|
|
||||||
|
|
||||||
Ok((&[], RsaPublicKey::new(modulus, exponent).unwrap()))
|
|
||||||
}
|
}
|
||||||
fn serialize(&self) -> Vec<u8> {
|
fn serialize(&self) -> Vec<u8> {
|
||||||
use rsa::traits::PublicKeyParts;
|
SubjectPublicKeyInfo::from_key(self.clone())
|
||||||
let algorithm = PublicKeyAlgorithm::default();
|
.unwrap()
|
||||||
let subject_public_key = SubjectPublicKey {
|
.to_der()
|
||||||
modulus: der::asn1::Int::new(&self.n().to_bytes_be()).unwrap(),
|
.unwrap()
|
||||||
public_exponent: der::asn1::Int::new(&self.e().to_bytes_be()).unwrap(),
|
|
||||||
};
|
|
||||||
let spki = SubjectPublicKeyInfo {
|
|
||||||
algorithm,
|
|
||||||
subject_public_key,
|
|
||||||
};
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
spki.encode(&mut buf).unwrap();
|
|
||||||
buf
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom decode implementation for SubjectPublicKeyInfo.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
struct SubjectPublicKeyInfo<'a> {
|
|
||||||
algorithm: PublicKeyAlgorithm<'a>,
|
|
||||||
subject_public_key: SubjectPublicKey,
|
|
||||||
}
|
|
||||||
impl<'a> DecodeValue<'a> for SubjectPublicKeyInfo<'a> {
|
|
||||||
fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> der::Result<Self> {
|
|
||||||
let algorithm = reader.decode()?;
|
|
||||||
let spk_der: der::asn1::BitString = reader.decode()?;
|
|
||||||
let spk_der = spk_der.as_bytes().unwrap();
|
|
||||||
let subject_public_key = SubjectPublicKey::from_der(spk_der).unwrap();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
algorithm,
|
|
||||||
subject_public_key,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl EncodeValue for SubjectPublicKeyInfo<'_> {
|
|
||||||
fn value_len(&self) -> der::Result<der::Length> {
|
|
||||||
self.algorithm.value_len()? + self.subject_public_key.value_len()?
|
|
||||||
}
|
|
||||||
fn encode_value(&self, writer: &mut impl der::Writer) -> der::Result<()> {
|
|
||||||
self.algorithm.encode_value(writer)?;
|
|
||||||
self.subject_public_key.encode_value(writer)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> Sequence<'a> for SubjectPublicKeyInfo<'a> {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Sequence)]
|
|
||||||
struct PublicKeyAlgorithm<'a> {
|
|
||||||
pub algorithm: ObjectIdentifier,
|
|
||||||
pub parameters: Option<AnyRef<'a>>,
|
|
||||||
}
|
|
||||||
impl Default for PublicKeyAlgorithm<'_> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
algorithm: ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"),
|
|
||||||
parameters: Some(AnyRef::new(Tag::Null, &[]).unwrap()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Sequence)]
|
|
||||||
struct SubjectPublicKey {
|
|
||||||
pub modulus: der::asn1::Int,
|
|
||||||
pub public_exponent: der::asn1::Int,
|
|
||||||
}
|
|
||||||
|
@ -7,14 +7,9 @@ pub use tokio::task::JoinError as TaskError;
|
|||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(IoError),
|
Io(#[from] IoError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Task(TaskError),
|
Task(#[from] TaskError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Network(NetworkError),
|
Network(#[from] NetworkError),
|
||||||
}
|
|
||||||
impl From<NetworkError> for Error {
|
|
||||||
fn from(err: NetworkError) -> Self {
|
|
||||||
Error::Network(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ use tokio_util::sync::CancellationToken;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
running: CancellationToken,
|
running: CancellationToken,
|
||||||
connections: DownstreamConnectionManager,
|
pub connections: DownstreamConnectionManager,
|
||||||
listener: JoinHandle<()>,
|
listener: JoinHandle<()>,
|
||||||
}
|
}
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@ -80,6 +80,14 @@ impl App for Server {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Handle login connections.
|
// Handle login connections.
|
||||||
|
let _ = futures::future::join_all(
|
||||||
|
self.connections
|
||||||
|
.clients_mut()
|
||||||
|
.filter(|c| matches!(c.client_state(), DownstreamConnectionState::LoginStart))
|
||||||
|
.map(|c| c.handle_login()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
// Handle play connection packets.
|
// Handle play connection packets.
|
||||||
// Process world updates.
|
// Process world updates.
|
||||||
// Send out play connection updates.
|
// Send out play connection updates.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user