1use chacha20poly1305::aead::AeadInOut;
10use chacha20poly1305::{ChaCha20Poly1305, KeyInit, Tag, XChaCha20Poly1305};
11
12use crate::{Error, Key, MAC_BYTES};
13
14pub(crate) const NONCE_BYTES: usize = 12;
15pub(crate) const XNONCE_BYTES: usize = 24;
16
17pub(crate) fn sequence_nonce(sequence: u64) -> [u8; NONCE_BYTES] {
20 let mut nonce = [0u8; NONCE_BYTES];
21 nonce[4..].copy_from_slice(&sequence.to_le_bytes());
22 nonce
23}
24
25pub(crate) fn encrypt_aead(
28 buffer: &mut [u8],
29 additional_data: &[u8],
30 nonce: &[u8; NONCE_BYTES],
31 key: &Key,
32) -> Result<(), Error> {
33 let (message, mac) = buffer.split_last_chunk_mut::<MAC_BYTES>().ok_or(Error::EncryptFailed)?;
34 let tag = ChaCha20Poly1305::new(key.into())
35 .encrypt_inout_detached(nonce.into(), additional_data, message.into())
36 .map_err(|_| Error::EncryptFailed)?;
37 mac.copy_from_slice(&tag);
38 Ok(())
39}
40
41pub(crate) fn decrypt_aead(
44 buffer: &mut [u8],
45 additional_data: &[u8],
46 nonce: &[u8; NONCE_BYTES],
47 key: &Key,
48) -> Result<(), Error> {
49 let (message, mac) = buffer.split_last_chunk_mut::<MAC_BYTES>().ok_or(Error::DecryptFailed)?;
50 ChaCha20Poly1305::new(key.into())
51 .decrypt_inout_detached(nonce.into(), additional_data, message.into(), &Tag::from(*mac))
52 .map_err(|_| Error::DecryptFailed)
53}
54
55pub(crate) fn encrypt_aead_big_nonce(
57 buffer: &mut [u8],
58 additional_data: &[u8],
59 nonce: &[u8; XNONCE_BYTES],
60 key: &Key,
61) -> Result<(), Error> {
62 let (message, mac) = buffer.split_last_chunk_mut::<MAC_BYTES>().ok_or(Error::EncryptFailed)?;
63 let tag = XChaCha20Poly1305::new(key.into())
64 .encrypt_inout_detached(nonce.into(), additional_data, message.into())
65 .map_err(|_| Error::EncryptFailed)?;
66 mac.copy_from_slice(&tag);
67 Ok(())
68}
69
70pub(crate) fn decrypt_aead_big_nonce(
72 buffer: &mut [u8],
73 additional_data: &[u8],
74 nonce: &[u8; XNONCE_BYTES],
75 key: &Key,
76) -> Result<(), Error> {
77 let (message, mac) = buffer.split_last_chunk_mut::<MAC_BYTES>().ok_or(Error::DecryptFailed)?;
78 XChaCha20Poly1305::new(key.into())
79 .decrypt_inout_detached(nonce.into(), additional_data, message.into(), &Tag::from(*mac))
80 .map_err(|_| Error::DecryptFailed)
81}
82
83pub(crate) fn random_bytes(buffer: &mut [u8]) {
84 getrandom::fill(buffer).expect("the operating system random number generator failed");
85}
86
87pub fn generate_key() -> Key {
89 let mut key = [0u8; crate::KEY_BYTES];
90 random_bytes(&mut key);
91 key
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn aead_round_trip() {
100 let key = generate_key();
101 let nonce = sequence_nonce(1000);
102 let additional_data = b"additional data";
103
104 let mut buffer = [0u8; 32 + MAC_BYTES];
105 buffer[..32].copy_from_slice(&[0x42; 32]);
106
107 encrypt_aead(&mut buffer, additional_data, &nonce, &key).unwrap();
108 assert_ne!(&buffer[..32], &[0x42; 32]);
109
110 decrypt_aead(&mut buffer, additional_data, &nonce, &key).unwrap();
111 assert_eq!(&buffer[..32], &[0x42; 32]);
112 }
113
114 #[test]
115 fn aead_rejects_tampering() {
116 let key = generate_key();
117 let nonce = sequence_nonce(1);
118
119 let mut buffer = [0u8; 32 + MAC_BYTES];
120 encrypt_aead(&mut buffer, &[], &nonce, &key).unwrap();
121
122 buffer[0] ^= 1;
123 assert!(matches!(decrypt_aead(&mut buffer, &[], &nonce, &key), Err(Error::DecryptFailed)));
124 }
125
126 #[test]
127 fn aead_rejects_wrong_key() {
128 let key = generate_key();
129 let nonce = sequence_nonce(1);
130
131 let mut buffer = [0u8; 32 + MAC_BYTES];
132 encrypt_aead(&mut buffer, &[], &nonce, &key).unwrap();
133
134 let wrong_key = generate_key();
135 assert!(matches!(
136 decrypt_aead(&mut buffer, &[], &nonce, &wrong_key),
137 Err(Error::DecryptFailed)
138 ));
139 }
140
141 #[test]
142 fn big_nonce_round_trip() {
143 let key = generate_key();
144 let mut nonce = [0u8; XNONCE_BYTES];
145 random_bytes(&mut nonce);
146
147 let mut buffer = [0u8; 64 + MAC_BYTES];
148 buffer[..64].copy_from_slice(&[0x37; 64]);
149
150 encrypt_aead_big_nonce(&mut buffer, b"aad", &nonce, &key).unwrap();
151 decrypt_aead_big_nonce(&mut buffer, b"aad", &nonce, &key).unwrap();
152 assert_eq!(&buffer[..64], &[0x37; 64]);
153 }
154
155 #[test]
156 fn nonce_is_little_endian_sequence_in_high_bytes() {
157 let nonce = sequence_nonce(0x1122334455667788);
160 assert_eq!(nonce, [0, 0, 0, 0, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11]);
161 }
162}