1use libcrux_secrets::U8;
2use libcrux_traits::aead::{slice::KeyGenError, typed_refs::KeyMut};
3#[cfg(any(feature = "chacha20poly1305", feature = "xchacha20poly1305"))]
4use libcrux_traits::{
5 aead,
6 aead::typed_refs::{DecryptError, EncryptError, Multiplexes},
7};
8
9#[derive(Debug, Clone, Copy, PartialEq)]
11pub enum Aead {
12 #[cfg(feature = "chacha20poly1305")]
14 ChaCha20Poly1305,
15
16 #[cfg(feature = "xchacha20poly1305")]
18 XChaCha20Poly1305,
19
20 #[cfg(feature = "aesgcm128")]
22 AesGcm128,
23
24 #[cfg(feature = "aesgcm256")]
26 AesGcm256,
27}
28
29#[cfg(any(
31 feature = "chacha20poly1305",
32 feature = "xchacha20poly1305",
33 feature = "aesgcm128",
34 feature = "aesgcm256"
35))]
36pub type KeyRef<'a> = aead::typed_refs::KeyRef<'a, Aead>;
37#[cfg(any(
39 feature = "chacha20poly1305",
40 feature = "xchacha20poly1305",
41 feature = "aesgcm128",
42 feature = "aesgcm256"
43))]
44pub type TagRef<'a> = aead::typed_refs::TagRef<'a, Aead>;
45#[cfg(any(
47 feature = "chacha20poly1305",
48 feature = "xchacha20poly1305",
49 feature = "aesgcm128",
50 feature = "aesgcm256"
51))]
52pub type TagMut<'a> = aead::typed_refs::TagMut<'a, Aead>;
53#[cfg(any(
55 feature = "chacha20poly1305",
56 feature = "xchacha20poly1305",
57 feature = "aesgcm128",
58 feature = "aesgcm256"
59))]
60pub type NonceRef<'a> = aead::typed_refs::NonceRef<'a, Aead>;
61
62#[cfg(feature = "chacha20poly1305")]
63impl Multiplexes<crate::chacha20poly1305::ChaCha20Poly1305> for Aead {
64 fn mux_algo(&self) -> Option<crate::chacha20poly1305::ChaCha20Poly1305> {
65 matches!(self, Self::ChaCha20Poly1305).then_some(crate::chacha20poly1305::ChaCha20Poly1305)
66 }
67 fn wrap_algo(_algo: crate::chacha20poly1305::ChaCha20Poly1305) -> Self {
68 Self::ChaCha20Poly1305
69 }
70}
71
72#[cfg(feature = "xchacha20poly1305")]
73impl Multiplexes<crate::xchacha20poly1305::XChaCha20Poly1305> for Aead {
74 fn mux_algo(&self) -> Option<crate::xchacha20poly1305::XChaCha20Poly1305> {
75 matches!(self, Self::XChaCha20Poly1305)
76 .then_some(crate::xchacha20poly1305::XChaCha20Poly1305)
77 }
78 fn wrap_algo(_algo: crate::xchacha20poly1305::XChaCha20Poly1305) -> Self {
79 Self::XChaCha20Poly1305
80 }
81}
82
83#[cfg(feature = "aesgcm128")]
84impl Multiplexes<crate::aesgcm128::AesGcm128> for Aead {
85 fn mux_algo(&self) -> Option<crate::aesgcm128::AesGcm128> {
86 matches!(self, Self::AesGcm128).then_some(crate::aesgcm128::AesGcm128)
87 }
88 fn wrap_algo(_algo: crate::aesgcm128::AesGcm128) -> Self {
89 Self::AesGcm128
90 }
91}
92
93#[cfg(feature = "aesgcm256")]
94impl Multiplexes<crate::aesgcm256::AesGcm256> for Aead {
95 fn mux_algo(&self) -> Option<crate::aesgcm256::AesGcm256> {
96 matches!(self, Self::AesGcm256).then_some(crate::aesgcm256::AesGcm256)
97 }
98 fn wrap_algo(_algo: crate::aesgcm256::AesGcm256) -> Self {
99 Self::AesGcm256
100 }
101}
102
103#[cfg(any(
104 feature = "chacha20poly1305",
105 feature = "xchacha20poly1305",
106 feature = "aesgcm128",
107 feature = "aesgcm256"
108))]
109impl aead::typed_refs::Aead for Aead {
110 fn key_len(&self) -> usize {
111 match *self {
112 #[cfg(feature = "chacha20poly1305")]
113 Aead::ChaCha20Poly1305 => 32,
114 #[cfg(feature = "xchacha20poly1305")]
115 Aead::XChaCha20Poly1305 => 32,
116 #[cfg(feature = "aesgcm128")]
117 Aead::AesGcm128 => 16,
118 #[cfg(feature = "aesgcm256")]
119 Aead::AesGcm256 => 32,
120 }
121 }
122
123 fn tag_len(&self) -> usize {
124 16
125 }
126
127 fn nonce_len(&self) -> usize {
128 match *self {
129 #[cfg(feature = "chacha20poly1305")]
130 Aead::ChaCha20Poly1305 => 12,
131 #[cfg(feature = "xchacha20poly1305")]
132 Aead::XChaCha20Poly1305 => 24,
133 #[cfg(feature = "aesgcm128")]
134 Aead::AesGcm128 => 12,
135 #[cfg(feature = "aesgcm256")]
136 Aead::AesGcm256 => 12,
137 }
138 }
139
140 fn keygen<'a>(&self, key: KeyMut<'a, Self>, rand: &[U8]) -> Result<(), KeyGenError> {
141 match *self {
142 #[cfg(feature = "chacha20poly1305")]
143 Aead::ChaCha20Poly1305 => {
144 use crate::chacha20poly1305::ChaCha20Poly1305;
145
146 let key = Self::mux_key_mut(key).ok_or(KeyGenError::WrongKeyLength)?;
147 ChaCha20Poly1305.keygen(key, rand)
148 }
149 #[cfg(feature = "xchacha20poly1305")]
150 Aead::XChaCha20Poly1305 => {
151 use crate::xchacha20poly1305::XChaCha20Poly1305;
152
153 let key = Self::mux_key_mut(key).ok_or(KeyGenError::WrongKeyLength)?;
154 XChaCha20Poly1305.keygen(key, rand)
155 }
156 #[cfg(feature = "aesgcm128")]
157 Aead::AesGcm128 => {
158 use crate::aesgcm128::AesGcm128;
159
160 let key = Self::mux_key_mut(key).ok_or(KeyGenError::WrongKeyLength)?;
161
162 AesGcm128.keygen(key, rand)
163 }
164 #[cfg(feature = "aesgcm256")]
165 Aead::AesGcm256 => {
166 use crate::aesgcm256::AesGcm256;
167
168 let key = Self::mux_key_mut(key).ok_or(KeyGenError::WrongKeyLength)?;
169
170 AesGcm256.keygen(key, rand)
171 }
172 }
173 }
174
175 fn encrypt<'a>(
176 &self,
177 ciphertext: &mut [u8],
178 tag: TagMut<'a>,
179 key: KeyRef<'a>,
180 nonce: NonceRef<'a>,
181 aad: &[u8],
182 plaintext: &[U8],
183 ) -> Result<(), aead::typed_refs::EncryptError> {
184 match *self {
185 #[cfg(feature = "chacha20poly1305")]
186 Aead::ChaCha20Poly1305 => {
187 use crate::chacha20poly1305::ChaCha20Poly1305;
188
189 let key = Self::mux_key(key).ok_or(EncryptError::WrongKey)?;
190 let tag = Self::mux_tag_mut(tag).ok_or(EncryptError::WrongTag)?;
191 let nonce = Self::mux_nonce(nonce).ok_or(EncryptError::WrongNonce)?;
192 ChaCha20Poly1305.encrypt(ciphertext, tag, key, nonce, aad, plaintext)
193 }
194 #[cfg(feature = "xchacha20poly1305")]
195 Aead::XChaCha20Poly1305 => {
196 use crate::xchacha20poly1305::XChaCha20Poly1305;
197
198 let key = Self::mux_key(key).ok_or(EncryptError::WrongKey)?;
199 let tag = Self::mux_tag_mut(tag).ok_or(EncryptError::WrongTag)?;
200 let nonce = Self::mux_nonce(nonce).ok_or(EncryptError::WrongNonce)?;
201 XChaCha20Poly1305.encrypt(ciphertext, tag, key, nonce, aad, plaintext)
202 }
203 #[cfg(feature = "aesgcm128")]
204 Aead::AesGcm128 => {
205 use crate::aesgcm128::AesGcm128;
206
207 let key = Self::mux_key(key).ok_or(EncryptError::WrongKey)?;
208 let tag = Self::mux_tag_mut(tag).ok_or(EncryptError::WrongTag)?;
209 let nonce = Self::mux_nonce(nonce).ok_or(EncryptError::WrongNonce)?;
210 AesGcm128.encrypt(ciphertext, tag, key, nonce, aad, plaintext)
211 }
212 #[cfg(feature = "aesgcm256")]
213 Aead::AesGcm256 => {
214 use crate::aesgcm256::AesGcm256;
215
216 let key = Self::mux_key(key).ok_or(EncryptError::WrongKey)?;
217 let tag = Self::mux_tag_mut(tag).ok_or(EncryptError::WrongTag)?;
218 let nonce = Self::mux_nonce(nonce).ok_or(EncryptError::WrongNonce)?;
219 AesGcm256.encrypt(ciphertext, tag, key, nonce, aad, plaintext)
220 }
221 }
222 }
223
224 fn decrypt<'a>(
225 &self,
226 plaintext: &mut [U8],
227 key: KeyRef<'a>,
228 nonce: NonceRef<'a>,
229 aad: &[u8],
230 ciphertext: &[u8],
231 tag: TagRef<'a>,
232 ) -> Result<(), aead::typed_refs::DecryptError> {
233 match *self {
234 #[cfg(feature = "chacha20poly1305")]
235 Aead::ChaCha20Poly1305 => {
236 use crate::chacha20poly1305::ChaCha20Poly1305;
237
238 let key = Self::mux_key(key).ok_or(DecryptError::WrongKey)?;
239 let tag = Self::mux_tag(tag).ok_or(DecryptError::WrongTag)?;
240 let nonce = Self::mux_nonce(nonce).ok_or(DecryptError::WrongNonce)?;
241 ChaCha20Poly1305.decrypt(plaintext, key, nonce, aad, ciphertext, tag)
242 }
243 #[cfg(feature = "xchacha20poly1305")]
244 Aead::XChaCha20Poly1305 => {
245 use crate::xchacha20poly1305::XChaCha20Poly1305;
246
247 let key = Self::mux_key(key).ok_or(DecryptError::WrongKey)?;
248 let tag = Self::mux_tag(tag).ok_or(DecryptError::WrongTag)?;
249 let nonce = Self::mux_nonce(nonce).ok_or(DecryptError::WrongNonce)?;
250 XChaCha20Poly1305.decrypt(plaintext, key, nonce, aad, ciphertext, tag)
251 }
252 #[cfg(feature = "aesgcm128")]
253 Aead::AesGcm128 => {
254 use crate::aesgcm128::AesGcm128;
255
256 let key = Self::mux_key(key).ok_or(DecryptError::WrongKey)?;
257 let tag = Self::mux_tag(tag).ok_or(DecryptError::WrongTag)?;
258 let nonce = Self::mux_nonce(nonce).ok_or(DecryptError::WrongNonce)?;
259 AesGcm128.decrypt(plaintext, key, nonce, aad, ciphertext, tag)
260 }
261 #[cfg(feature = "aesgcm256")]
262 Aead::AesGcm256 => {
263 use crate::aesgcm256::AesGcm256;
264
265 let key = Self::mux_key(key).ok_or(DecryptError::WrongKey)?;
266 let tag = Self::mux_tag(tag).ok_or(DecryptError::WrongTag)?;
267 let nonce = Self::mux_nonce(nonce).ok_or(DecryptError::WrongNonce)?;
268 AesGcm256.decrypt(plaintext, key, nonce, aad, ciphertext, tag)
269 }
270 }
271 }
272}
273
274#[cfg(any(
275 feature = "chacha20poly1305",
276 feature = "xchacha20poly1305",
277 feature = "aesgcm128",
278 feature = "aesgcm256"
279))]
280#[cfg(test)]
281mod tests {
282 use libcrux_traits::aead::typed_refs;
283 use typed_refs::Aead as _;
284
285 use super::Aead;
286
287 #[test]
288 #[cfg(feature = "chacha20poly1305")]
289 fn test_key_centric_multiplexed_chachapoly() {
290 use libcrux_traits::libcrux_secrets::{Classify, ClassifyRef, DeclassifyRef};
291
292 let algo = Aead::ChaCha20Poly1305;
293
294 algo.new_key(&[0.classify(); 33])
295 .expect_err("length should mismatch");
296
297 let mut tag_bytes = [0.classify(); 16];
298 let key_bytes = [0.classify(); 32];
299 let nonce_bytes = [0.classify(); 12];
300
301 let key = algo.new_key(&key_bytes).expect("length should match");
302 let nonce = algo.new_nonce(&nonce_bytes).expect("length should match");
303 let tag = algo
304 .new_tag_mut(&mut tag_bytes)
305 .expect("length should match");
306
307 let pt = b"the quick brown fox jumps over the lazy dog".classify_ref();
308 let mut ct = [0; 43];
309 let mut pt_out = [0.classify(); 43];
310
311 key.encrypt(&mut ct, tag, nonce, b"", pt).unwrap();
312 let tag = algo.new_tag(&tag_bytes).unwrap();
313 key.decrypt(&mut pt_out, nonce, b"", &ct, tag).unwrap();
314 assert_eq!(pt.declassify_ref(), pt_out.declassify_ref());
315 }
316
317 #[test]
318 #[cfg(feature = "xchacha20poly1305")]
319 fn test_key_centric_multiplexed_xchachapoly() {
320 use libcrux_traits::libcrux_secrets::{Classify, ClassifyRef, DeclassifyRef};
321
322 let algo = Aead::XChaCha20Poly1305;
323
324 let wrong_length_key_bytes = [0.classify(); 33];
325 algo.new_key(&wrong_length_key_bytes)
326 .expect_err("length should mismatch");
327
328 let mut tag_bytes = [0.classify(); 16];
329
330 let key_bytes = [0.classify(); 32];
331 let nonce_bytes = [0.classify(); 24];
332
333 let key = algo.new_key(&key_bytes).expect("length should match");
334 let nonce = algo.new_nonce(&nonce_bytes).expect("length should match");
335 let tag = algo
336 .new_tag_mut(&mut tag_bytes)
337 .expect("length should match");
338
339 let pt = b"the quick brown fox jumps over the lazy dog".classify_ref();
340 let mut ct = [0; 43];
341 let mut pt_out = [0.classify(); 43];
342
343 key.encrypt(&mut ct, tag, nonce, b"", pt).unwrap();
344 let tag = algo.new_tag(&tag_bytes).unwrap();
345 key.decrypt(&mut pt_out, nonce, b"", &ct, tag).unwrap();
346 assert_eq!(pt.declassify_ref(), pt_out.declassify_ref());
347 }
348
349 #[test]
350 #[cfg(feature = "aesgcm128")]
351 fn test_key_centric_multiplexed_aesgcm128() {
352 use libcrux_traits::libcrux_secrets::{Classify, ClassifyRef, DeclassifyRef};
353
354 let algo = Aead::AesGcm128;
355
356 algo.new_key(&[0.classify(); 33])
357 .expect_err("length should mismatch");
358
359 let mut tag_bytes = [0.classify(); 16];
360 let key_bytes = [0.classify(); 16];
361 let nonce_bytes = [0.classify(); 12];
362
363 let key = algo.new_key(&key_bytes).expect("length should match");
364 let nonce = algo.new_nonce(&nonce_bytes).expect("length should match");
365 let tag = algo
366 .new_tag_mut(&mut tag_bytes)
367 .expect("length should match");
368
369 let pt = b"the quick brown fox jumps over the lazy dog".classify_ref();
370 let mut ct = [0; 43];
371 let mut pt_out = [0.classify(); 43];
372
373 key.encrypt(&mut ct, tag, nonce, b"", pt).unwrap();
374 let tag = algo.new_tag(&tag_bytes).unwrap();
375 key.decrypt(&mut pt_out, nonce, b"", &ct, tag).unwrap();
376 assert_eq!(pt.declassify_ref(), pt_out.declassify_ref());
377 }
378
379 #[test]
380 #[cfg(feature = "aesgcm256")]
381 fn test_key_centric_multiplexed_aesgcm256() {
382 use libcrux_traits::libcrux_secrets::{Classify, ClassifyRef, DeclassifyRef};
383
384 let algo = Aead::AesGcm256;
385
386 algo.new_key(&[0.classify(); 33])
387 .expect_err("length should mismatch");
388
389 let mut tag_bytes = [0.classify(); 16];
390 let key_bytes = [0.classify(); 32];
391 let nonce_bytes = [0.classify(); 12];
392
393 let key = algo.new_key(&key_bytes).expect("length should match");
394 let nonce = algo.new_nonce(&nonce_bytes).expect("length should match");
395 let tag = algo
396 .new_tag_mut(&mut tag_bytes)
397 .expect("length should match");
398
399 let pt = b"the quick brown fox jumps over the lazy dog".classify_ref();
400 let mut ct = [0; 43];
401 let mut pt_out = [0.classify(); 43];
402
403 key.encrypt(&mut ct, tag, nonce, b"", pt).unwrap();
404 let tag = algo.new_tag(&tag_bytes).unwrap();
405 key.decrypt(&mut pt_out, nonce, b"", &ct, tag).unwrap();
406 assert_eq!(pt.declassify_ref(), pt_out.declassify_ref());
407 }
408}