Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement test serverside encryption #113

Merged
merged 6 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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_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 / Clippy

duplicate key `p384` in table `dependencies`

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`
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
Loading