azalea_crypto/
signing.rs

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
28/// Generates a random u64 to use as a salt
29pub 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    /// Can be acquired with [`make_salt`].
40    pub salt: u64,
41
42    /// The current time that we're sending the message at.
43    pub timestamp: SystemTime,
44
45    /// The message that we're sending in chat.
46    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    // always 1 for some reason
54    1i32.azalea_write(&mut data_to_sign).unwrap();
55    // player uuid
56    opts.account_uuid.azalea_write(&mut data_to_sign).unwrap();
57    // chat session uuid
58    opts.chat_session_uuid
59        .azalea_write(&mut data_to_sign)
60        .unwrap();
61    // message index
62    opts.message_index.azalea_write(&mut data_to_sign).unwrap();
63    // salt
64    opts.salt.azalea_write(&mut data_to_sign).unwrap();
65
66    // timestamp as seconds
67    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    // message length as u32
75    let message_len: u32 = opts.message.len().try_into().unwrap();
76    message_len.azalea_write(&mut data_to_sign).unwrap();
77    // message bytes
78    data_to_sign.extend_from_slice(opts.message.as_bytes());
79
80    // last seen messages length
81    0i32.azalea_write(&mut data_to_sign).unwrap();
82    // signatures of last seen messages
83    // ... not implemented yet
84
85    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}