diff --git a/azalea-crypto/Cargo.toml b/azalea-crypto/Cargo.toml index aed12e04..f9847645 100644 --- a/azalea-crypto/Cargo.toml +++ b/azalea-crypto/Cargo.toml @@ -14,8 +14,10 @@ azalea-buf = { path = "../azalea-buf", version = "^0.7.0" } cfb8 = "0.8.1" num-bigint = "^0.4.3" rand = { version = "^0.8.4", features = ["getrandom"] } +rsa = { version = "0.9.2", features = ["sha2"] } rsa_public_encrypt_pkcs1 = "0.4.0" sha-1 = "^0.10.0" +sha2 = "0.10.6" uuid = "^1.1.2" [dev-dependencies] diff --git a/azalea-crypto/src/signing.rs b/azalea-crypto/src/signing.rs index 93892c17..ebab4356 100755 --- a/azalea-crypto/src/signing.rs +++ b/azalea-crypto/src/signing.rs @@ -1,4 +1,11 @@ -use azalea_buf::McBuf; +use std::time::{SystemTime, UNIX_EPOCH}; + +use azalea_buf::{McBuf, McBufWritable}; +use rsa::{ + signature::{RandomizedSigner, SignatureEncoding}, + RsaPrivateKey, +}; +use sha2::Sha256; use uuid::Uuid; #[derive(Debug, Clone, McBuf)] @@ -22,3 +29,79 @@ pub struct SignedMessageHeader { pub fn make_salt() -> u64 { rand::random() } + +pub struct SignChatMessageOptions { + pub account_uuid: Uuid, + pub chat_session_uuid: Uuid, + + pub message_index: u32, + + /// Can be acquired with [`make_salt`]. + pub salt: u64, + + /// The current time that we're sending the message at. + pub timestamp: SystemTime, + + /// The message that we're sending in chat. + pub message: String, + + pub private_key: RsaPrivateKey, +} + +pub fn sign_chat_message(opts: &SignChatMessageOptions) -> MessageSignature { + let mut data_to_sign = Vec::new(); + // always 1 for some reason + 1i32.write_into(&mut data_to_sign).unwrap(); + // player uuid + opts.account_uuid.write_into(&mut data_to_sign).unwrap(); + // chat session uuid + opts.chat_session_uuid + .write_into(&mut data_to_sign) + .unwrap(); + // message index + opts.message_index.write_into(&mut data_to_sign).unwrap(); + // salt + opts.salt.write_into(&mut data_to_sign).unwrap(); + + // timestamp as seconds + let seconds_since_epoch = opts + .timestamp + .duration_since(UNIX_EPOCH) + .expect("timestamp must be after epoch") + .as_secs(); + seconds_since_epoch.write_into(&mut data_to_sign).unwrap(); + + // message length as u32 + let message_len: u32 = opts.message.len().try_into().unwrap(); + message_len.write_into(&mut data_to_sign).unwrap(); + // message bytes + data_to_sign.extend_from_slice(opts.message.as_bytes()); + + // last seen messages length + 0i32.write_into(&mut data_to_sign).unwrap(); + // signatures of last seen messages + // ... not implemented yet + + // hash with sha256 and then sign with rsa + + // let mut hasher = Sha256::new(); + // hasher.update(data_to_sign.as_slice()); + // let hash = hasher.finalize(); + + // println!("hash: {:?}", hash); + + let signing_key = rsa::pkcs1v15::SigningKey::::new(opts.private_key.clone()); + let mut rng = rand::thread_rng(); + let signature = signing_key + .sign_with_rng(&mut rng, &data_to_sign) + .to_bytes(); + + println!("signature: {:?}", signature); + + MessageSignature { + bytes: signature + .as_ref() + .try_into() + .expect("signature must be 256 bytes"), + } +}