renetcode/
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::NETCODE_MAC_BYTES;
5
6pub fn dencrypted_in_place(buffer: &mut [u8], sequence: u64, private_key: &[u8; 32], aad: &[u8]) -> Result<(), CryptoError> {
7    let mut nonce = [0; 12];
8    nonce[4..12].copy_from_slice(&sequence.to_le_bytes());
9    let nonce = Nonce::from(nonce);
10    let (buffer, tag) = buffer.split_at_mut(buffer.len() - NETCODE_MAC_BYTES);
11    let tag = Tag::from_slice(tag);
12
13    let key = Key::from_slice(private_key);
14    let cipher = ChaCha20Poly1305::new(key);
15
16    cipher.decrypt_in_place_detached(&nonce, aad, buffer, tag)
17}
18
19pub fn dencrypted_in_place_xnonce(buffer: &mut [u8], xnonce: &[u8; 24], private_key: &[u8; 32], aad: &[u8]) -> Result<(), CryptoError> {
20    let xnonce = XNonce::from_slice(xnonce);
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 = XChaCha20Poly1305::new(key);
26
27    cipher.decrypt_in_place_detached(xnonce, aad, buffer, tag)
28}
29
30pub fn encrypt_in_place(buffer: &mut [u8], sequence: u64, key: &[u8; 32], aad: &[u8]) -> Result<(), CryptoError> {
31    let mut nonce = [0; 12];
32    nonce[4..12].copy_from_slice(&sequence.to_le_bytes());
33    let nonce = Nonce::from(nonce);
34    let (buffer, buffer_tag) = buffer.split_at_mut(buffer.len() - NETCODE_MAC_BYTES);
35
36    let key = Key::from_slice(key);
37    let cipher = ChaCha20Poly1305::new(key);
38    let tag = cipher.encrypt_in_place_detached(&nonce, aad, buffer)?;
39    buffer_tag.copy_from_slice(&tag);
40
41    Ok(())
42}
43
44pub fn encrypt_in_place_xnonce(buffer: &mut [u8], xnonce: &[u8; 24], key: &[u8; 32], aad: &[u8]) -> Result<(), CryptoError> {
45    let (buffer, buffer_tag) = buffer.split_at_mut(buffer.len() - NETCODE_MAC_BYTES);
46
47    let xnonce = XNonce::from_slice(xnonce);
48    let key = Key::from_slice(key);
49    let cipher = XChaCha20Poly1305::new(key);
50    let tag = cipher.encrypt_in_place_detached(xnonce, aad, buffer)?;
51    buffer_tag.copy_from_slice(&tag);
52
53    Ok(())
54}
55
56/// Generate a buffer with random bytes using randomness from the operating system.
57///
58/// The implementation is provided by the `getrandom` crate. Refer to
59/// `getrandom` documentation for details.
60pub fn generate_random_bytes<const N: usize>() -> [u8; N] {
61    let mut bytes = [0; N];
62    OsRng.fill_bytes(&mut bytes);
63    bytes
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_encrypt_decrypt_in_place() {
72        let key = b"an example very very secret key."; // 32-bytes
73        let sequence = 2;
74        let aad = b"test";
75
76        let mut data = b"some packet data".to_vec();
77        let data_len = data.len();
78        data.extend_from_slice(&[0u8; NETCODE_MAC_BYTES]);
79
80        encrypt_in_place(&mut data, sequence, key, aad).unwrap();
81        dencrypted_in_place(&mut data, sequence, key, aad).unwrap();
82        assert_eq!(&data[..data_len], b"some packet data");
83    }
84}