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