1use std::time::{SystemTime, UNIX_EPOCH};
4
5use azalea_buf::{AzBuf, AzaleaWrite};
6use rsa::{
7 RsaPrivateKey,
8 signature::{RandomizedSigner, SignatureEncoding},
9};
10use sha2::Sha256;
11use uuid::Uuid;
12
13#[derive(AzBuf, Clone, Debug)]
14pub struct SaltSignaturePair {
15 pub salt: u64,
16 pub signature: Vec<u8>,
17}
18
19#[derive(AzBuf, Clone, Debug, PartialEq)]
20pub struct MessageSignature {
21 pub bytes: [u8; 256],
22}
23
24#[derive(AzBuf, Clone, Debug, PartialEq)]
25pub struct SignedMessageHeader {
26 pub previous_signature: Option<MessageSignature>,
27 pub sender: Uuid,
28}
29
30pub fn make_salt() -> u64 {
32 rand::random()
33}
34
35pub struct SignChatMessageOptions {
36 pub account_uuid: Uuid,
37 pub chat_session_uuid: Uuid,
38
39 pub message_index: u32,
40
41 pub salt: u64,
43
44 pub timestamp: SystemTime,
46
47 pub message: String,
49
50 pub private_key: RsaPrivateKey,
51}
52
53pub fn sign_chat_message(opts: &SignChatMessageOptions) -> MessageSignature {
54 let mut data_to_sign = Vec::new();
55 1i32.azalea_write(&mut data_to_sign).unwrap();
57 opts.account_uuid.azalea_write(&mut data_to_sign).unwrap();
59 opts.chat_session_uuid
61 .azalea_write(&mut data_to_sign)
62 .unwrap();
63 opts.message_index.azalea_write(&mut data_to_sign).unwrap();
65 opts.salt.azalea_write(&mut data_to_sign).unwrap();
67
68 let seconds_since_epoch = opts
70 .timestamp
71 .duration_since(UNIX_EPOCH)
72 .expect("timestamp must be after epoch")
73 .as_secs();
74 seconds_since_epoch.azalea_write(&mut data_to_sign).unwrap();
75
76 let message_len: u32 = opts.message.len().try_into().unwrap();
78 message_len.azalea_write(&mut data_to_sign).unwrap();
79 data_to_sign.extend_from_slice(opts.message.as_bytes());
81
82 0i32.azalea_write(&mut data_to_sign).unwrap();
84 let signing_key = rsa::pkcs1v15::SigningKey::<Sha256>::new(opts.private_key.clone());
88 let mut rng = rand::rng();
89 let signature = signing_key
90 .sign_with_rng(&mut rng, &data_to_sign)
91 .to_bytes();
92
93 MessageSignature {
94 bytes: signature
95 .as_ref()
96 .try_into()
97 .expect("signature must be 256 bytes"),
98 }
99}