renetcode2/
crypto.rs

1use chacha20poly1305::aead::{rand_core::RngCore, OsRng};
2use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, Error as CryptoError, Key, KeyInit, Nonce, Tag, XChaCha20Poly1305, XNonce};
3
4use crate::{ENCODED_PACKET_TAG_BYTES, NETCODE_MAC_BYTES};
5
6pub fn decode_and_check_buffer(buffer: &[u8], protocol_id: u64) -> Result<(), ()> {
7    let (_, buffer_tag) = buffer.split_at(buffer.len() - ENCODED_PACKET_TAG_BYTES);
8    let protocol_tag = u64::from_le_bytes(buffer_tag.try_into().unwrap());
9
10    if protocol_tag != protocol_id {
11        return Err(());
12    }
13
14    Ok(())
15}
16
17pub fn dencrypted_in_place(buffer: &mut [u8], sequence: u64, private_key: &[u8; 32], aad: &[u8]) -> Result<(), CryptoError> {
18    let mut nonce = [0; 12];
19    nonce[4..12].copy_from_slice(&sequence.to_le_bytes());
20    let nonce = Nonce::from(nonce);
21    let (buffer, tag) = buffer.split_at_mut(buffer.len() - NETCODE_MAC_BYTES);
22    let tag = Tag::from_slice(tag);
23
24    let key = Key::from_slice(private_key);
25    let cipher = ChaCha20Poly1305::new(key);
26
27    cipher.decrypt_in_place_detached(&nonce, aad, buffer, tag)
28}
29
30pub fn dencrypted_in_place_xnonce(buffer: &mut [u8], xnonce: &[u8; 24], private_key: &[u8; 32], aad: &[u8]) -> Result<(), CryptoError> {
31    let xnonce = XNonce::from_slice(xnonce);
32    let (buffer, tag) = buffer.split_at_mut(buffer.len() - NETCODE_MAC_BYTES);
33    let tag = Tag::from_slice(tag);
34
35    let key = Key::from_slice(private_key);
36    let cipher = XChaCha20Poly1305::new(key);
37
38    cipher.decrypt_in_place_detached(xnonce, aad, buffer, tag)
39}
40
41pub fn encode_in_place(buffer: &mut [u8], protocol_id: u64) {
42    let (_, buffer_tag) = buffer.split_at_mut(buffer.len() - ENCODED_PACKET_TAG_BYTES);
43    buffer_tag.copy_from_slice(&protocol_id.to_le_bytes());
44}
45
46pub fn encrypt_in_place(buffer: &mut [u8], sequence: u64, key: &[u8; 32], aad: &[u8]) -> Result<(), CryptoError> {
47    let mut nonce = [0; 12];
48    nonce[4..12].copy_from_slice(&sequence.to_le_bytes());
49    let nonce = Nonce::from(nonce);
50    let (buffer, buffer_tag) = buffer.split_at_mut(buffer.len() - NETCODE_MAC_BYTES);
51
52    let key = Key::from_slice(key);
53    let cipher = ChaCha20Poly1305::new(key);
54    let tag = cipher.encrypt_in_place_detached(&nonce, aad, buffer)?;
55    buffer_tag.copy_from_slice(&tag);
56
57    Ok(())
58}
59
60pub fn encrypt_in_place_xnonce(buffer: &mut [u8], xnonce: &[u8; 24], key: &[u8; 32], aad: &[u8]) -> Result<(), CryptoError> {
61    let (buffer, buffer_tag) = buffer.split_at_mut(buffer.len() - NETCODE_MAC_BYTES);
62
63    let xnonce = XNonce::from_slice(xnonce);
64    let key = Key::from_slice(key);
65    let cipher = XChaCha20Poly1305::new(key);
66    let tag = cipher.encrypt_in_place_detached(xnonce, aad, buffer)?;
67    buffer_tag.copy_from_slice(&tag);
68
69    Ok(())
70}
71
72/// Generate a buffer with random bytes using randomness from the operating system.
73///
74/// The implementation is provided by the `getrandom` crate. Refer to
75/// `getrandom` documentation for details.
76pub fn generate_random_bytes<const N: usize>() -> [u8; N] {
77    let mut bytes = [0; N];
78    OsRng.fill_bytes(&mut bytes);
79    bytes
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn test_encode_decode_in_place() {
88        let protocol_id = 42;
89
90        let mut data = b"some packet data".to_vec();
91        let data_len = data.len();
92        data.extend_from_slice(&[0u8; ENCODED_PACKET_TAG_BYTES]);
93
94        encode_in_place(&mut data, protocol_id);
95        decode_and_check_buffer(&data, protocol_id).unwrap();
96        assert_eq!(&data[..data_len], b"some packet data");
97    }
98
99    #[test]
100    fn test_encrypt_decrypt_in_place() {
101        let key = b"an example very very secret key."; // 32-bytes
102        let sequence = 2;
103        let aad = b"test";
104
105        let mut data = b"some packet data".to_vec();
106        let data_len = data.len();
107        data.extend_from_slice(&[0u8; NETCODE_MAC_BYTES]);
108
109        encrypt_in_place(&mut data, sequence, key, aad).unwrap();
110        dencrypted_in_place(&mut data, sequence, key, aad).unwrap();
111        assert_eq!(&data[..data_len], b"some packet data");
112    }
113}