Skip to content

Commit

Permalink
Merge pull request #113 from Lompandi/encryption
Browse files Browse the repository at this point in the history
Implement test serverside encryption
  • Loading branch information
RadiatedMonkey authored Jan 4, 2025
2 parents 930f5f6 + 2e18b3c commit a8600f2
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 7 deletions.
4 changes: 4 additions & 0 deletions crates/proto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1.19"

rak-rs = { version = "0.3", default-features = false, features = ["async_tokio", "mcpe"] }

p384 = "0.13.0"

Check failure on line 39 in crates/proto/Cargo.toml

View workflow job for this annotation

GitHub Actions / Test Suite

duplicate key `p384` in table `dependencies`

Check failure on line 39 in crates/proto/Cargo.toml

View workflow job for this annotation

GitHub Actions / Check

duplicate key `p384` in table `dependencies`

Check failure on line 39 in crates/proto/Cargo.toml

View workflow job for this annotation

GitHub Actions / Clippy

duplicate key `p384` in table `dependencies`
sha2 = "0.10.8"
aes-gcm = "0.10"
108 changes: 101 additions & 7 deletions crates/proto/src/encryption.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,126 @@

use base64::{engine::general_purpose::STANDARD, Engine};
use p384::{self, ecdh::diffie_hellman, pkcs8::DecodePublicKey, PublicKey, SecretKey};

use sha2::{Sha256, Digest};
use rand::Rng;

use aes_gcm::{aead::Aead, Aes256Gcm, Key, KeyInit, Nonce};

use bedrockrs_proto_core::error::EncryptionError;
use crate::v729::packets::login::LoginPacket;

#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct Encryption {
recv_counter: u64,
send_counter: u64,
buf: [u8; 8],
key: Vec<u8>,
iv: Vec<u8>,
cipher: Aes256Gcm,
}

impl std::fmt::Debug for Encryption {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Encryption")
.field("send_counter", &self.send_counter)
.field("buf", &self.buf)
.field("key", &self.key)
.field("iv", &self.iv)
.finish()
}
}

//Reversed from bedrock dedicated server v 1.21.51.02
impl Encryption {
pub fn new() -> Self {
Self {
recv_counter: 0,
let key = vec![0u8; 32]; // Initialize with 32 zero bytes (can be set to your desired key)

let cipher = Aes256Gcm::new(&Key::<Aes256Gcm>::from_slice(&key));

Encryption{
send_counter: 0,
buf: [0; 8],
key: Vec::new(),
iv: Vec::new(),
cipher,
}
}

pub fn decrypt(&mut self, _src: Vec<u8>) -> Result<Vec<u8>, EncryptionError> {
unimplemented!()
pub fn decrypt(&mut self, ciphertext: Vec<u8>) -> Result<Vec<u8>, EncryptionError> {
// In bedrock dedicated server, there are serveral encryption method, but it turned out they are likely to always use
// AES-256-GCM
let nonce = Nonce::from_slice(&self.iv);

self.cipher.decrypt(nonce, ciphertext.as_slice())
.map_err(|_| EncryptionError::DecryptionFailed())
}

pub fn encrypt(&mut self, _src: Vec<u8>) -> Result<Vec<u8>, EncryptionError> {
unimplemented!()
pub fn encrypt(&mut self, plaintext: Vec<u8>) -> Result<Vec<u8>, EncryptionError> {
let nonce = Nonce::from_slice(&self.iv);

self.cipher.encrypt(nonce, plaintext.as_slice())
.map_err(|_| EncryptionError::EncryptionFailed())
}

pub fn verify(&mut self, _src: &[u8]) -> Result<(), EncryptionError> {
unimplemented!()
}

pub fn get_ident_key(&mut self, login_packet: &LoginPacket) -> Result<String, EncryptionError> {
let cert_chain = &login_packet.connection_request.certificate_chain;

let last_chain = cert_chain.last().unwrap();

let identity_public_key = last_chain.get("identityPublicKey").unwrap();

return Ok(identity_public_key.clone().to_string());
}

pub fn compute_shared_secret_ecc(
&mut self,
server_private_key: &[u8],
in_public_key: &[u8]
)->Result<Vec<u8>, EncryptionError>{
let server_private = SecretKey::from_sec1_der(server_private_key)
.map_err(|_| EncryptionError::StartupFailed())?;

let peer_public = PublicKey::from_public_key_der(in_public_key).unwrap();

let secret = diffie_hellman(server_private.to_nonzero_scalar(), peer_public.as_affine());

Ok(secret.raw_secret_bytes().to_vec())
}

pub fn init_encryption(&mut self, server_private_key: Vec<u8>, login_packet: LoginPacket) -> Result<Vec<u8>, EncryptionError> {

//The "identityPublicKey from the last part of the chain will be used as encryption seed
let identity_publickey = self.get_ident_key(&login_packet)
.map_err(|_| EncryptionError::MissingKey)?;

//Decode the peer public key using base64
let peer_pub_key_der = STANDARD.decode(identity_publickey).unwrap();
let shared_secret = self.compute_shared_secret_ecc(server_private_key.as_slice(), &peer_pub_key_der)
.map_err(|_| EncryptionError::StartupFailed())?;

//Generate 16-byte random slice for the first 8 byte
let mut rng = rand::thread_rng();
let mut final_key_seed : Vec<u8> = (0..16).map(|_| rng.gen()).collect();
final_key_seed.extend_from_slice(&shared_secret);


//Reversed from bds, uses sha-256 for applying hash
let mut hasher = Sha256::new();
hasher.update(final_key_seed);

let encryption_symmetric_key = hasher.finalize().to_vec();

//Note that after some reversing, I notice that the first 16 byte of the key will also be used as IV
self.key = encryption_symmetric_key.clone();
self.iv = encryption_symmetric_key[0..16].to_vec();

self.cipher = Aes256Gcm::new(&Key::<Aes256Gcm>::from_slice(&self.key.as_slice()));

Ok(encryption_symmetric_key)
}
}
9 changes: 9 additions & 0 deletions crates/proto/src/version/v766/packets/login.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use bedrockrs_macros::{gamepacket, ProtoCodec};

#[gamepacket(id = 1)]
#[derive(ProtoCodec, Clone, Debug)]
pub struct LoginPacket {
#[endianness(be)]
pub client_network_version: i32,
pub connection_request: String,
}
12 changes: 12 additions & 0 deletions crates/proto_core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ pub enum CompressionError {
pub enum EncryptionError {
#[error("IO Error: {0}")]
IOError(IOError),

#[error("Encryption Error")]
EncryptionFailed(),

#[error("Decryption Error")]
DecryptionFailed(),

#[error("Startup Error")]
StartupFailed(),

#[error("Missing Key")]
MissingKey,
}

#[derive(Error, Debug)]
Expand Down

0 comments on commit a8600f2

Please sign in to comment.