Skip to main content

lib_q_romulus/
libq_aead.rs

1//! `lib_q_core::Aead` wrappers (allocating API) for registry integration.
2
3#![deny(unsafe_code)]
4
5extern crate alloc;
6
7use alloc::string::ToString;
8use alloc::vec::Vec;
9
10use aead::generic_array::GenericArray;
11use aead::{
12    AeadInPlace,
13    KeyInit,
14};
15use lib_q_core::{
16    Aead,
17    AeadDecryptSemantic,
18    AeadKey,
19    DecryptSemanticOutcome,
20    Error,
21    Nonce,
22    Result,
23};
24use zeroize::{
25    Zeroize,
26    Zeroizing,
27};
28
29use crate::{
30    RomulusM,
31    RomulusN,
32    stack_secret,
33};
34
35/// Stateless Romulus-N facade using [`lib_q_core::Aead`].
36pub struct RomulusNAead;
37
38impl RomulusNAead {
39    pub const fn new() -> Self {
40        Self
41    }
42
43    pub const fn key_size() -> usize {
44        16
45    }
46
47    pub const fn nonce_size() -> usize {
48        16
49    }
50
51    pub const fn tag_size() -> usize {
52        16
53    }
54}
55
56impl Default for RomulusNAead {
57    fn default() -> Self {
58        Self::new()
59    }
60}
61
62impl Aead for RomulusNAead {
63    fn encrypt(
64        &self,
65        key: &AeadKey,
66        nonce: &Nonce,
67        plaintext: &[u8],
68        associated_data: Option<&[u8]>,
69    ) -> Result<Vec<u8>> {
70        let kb = key.as_bytes();
71        if kb.len() != Self::key_size() {
72            return Err(Error::InvalidKeySize {
73                expected: Self::key_size(),
74                actual: kb.len(),
75            });
76        }
77        let nb = nonce.as_bytes();
78        if nb.len() != Self::nonce_size() {
79            return Err(Error::InvalidNonceSize {
80                expected: Self::nonce_size(),
81                actual: nb.len(),
82            });
83        }
84        let ad = associated_data.unwrap_or(&[]);
85        let nonce_z = stack_secret::zeroizing_copy_16(nb);
86        let nonce_ref = GenericArray::from_slice(nonce_z.as_slice());
87        let cipher = {
88            let kz = stack_secret::zeroizing_copy_16(kb);
89            RomulusN::new(GenericArray::from_slice(kz.as_slice()))
90        };
91        let mut buf = plaintext.to_vec();
92        let tag = cipher
93            .encrypt_in_place_detached(nonce_ref, ad, &mut buf)
94            .map_err(|_| Error::EncryptionFailed {
95                operation: "Romulus-N encrypt".to_string(),
96            })?;
97        buf.extend_from_slice(tag.as_slice());
98        Ok(buf)
99    }
100
101    fn decrypt(
102        &self,
103        key: &AeadKey,
104        nonce: &Nonce,
105        ciphertext: &[u8],
106        associated_data: Option<&[u8]>,
107    ) -> Result<Vec<u8>> {
108        let kb = key.as_bytes();
109        if kb.len() != Self::key_size() {
110            return Err(Error::InvalidKeySize {
111                expected: Self::key_size(),
112                actual: kb.len(),
113            });
114        }
115        let nb = nonce.as_bytes();
116        if nb.len() != Self::nonce_size() {
117            return Err(Error::InvalidNonceSize {
118                expected: Self::nonce_size(),
119                actual: nb.len(),
120            });
121        }
122        if ciphertext.len() < Self::tag_size() {
123            return Err(Error::aead_ciphertext_shorter_than_tag(
124                Self::tag_size(),
125                ciphertext.len(),
126            ));
127        }
128        let ad = associated_data.unwrap_or(&[]);
129        let body_len = ciphertext.len() - Self::tag_size();
130        let key_z = stack_secret::zeroizing_copy_16(kb);
131        let nonce_z = stack_secret::zeroizing_copy_16(nb);
132        let tag_arr =
133            <[u8; stack_secret::LEN]>::try_from(&ciphertext[body_len..]).map_err(|_| {
134                Error::VerificationFailed {
135                    operation: "AEAD tag verification".to_string(),
136                }
137            })?;
138        let mut buf = ciphertext[..body_len].to_vec();
139        crate::romulus_n::romulus_n_decrypt(&key_z, &nonce_z, ad, &mut buf, &tag_arr).map_err(
140            |_| Error::VerificationFailed {
141                operation: "AEAD tag verification".to_string(),
142            },
143        )?;
144        Ok(buf)
145    }
146}
147
148impl AeadDecryptSemantic for RomulusNAead {
149    fn decrypt_semantic(
150        &self,
151        key: &AeadKey,
152        nonce: &Nonce,
153        ciphertext: &[u8],
154        associated_data: Option<&[u8]>,
155    ) -> Result<DecryptSemanticOutcome> {
156        let kb = key.as_bytes();
157        if kb.len() != Self::key_size() {
158            return Err(Error::InvalidKeySize {
159                expected: Self::key_size(),
160                actual: kb.len(),
161            });
162        }
163        let nb = nonce.as_bytes();
164        if nb.len() != Self::nonce_size() {
165            return Err(Error::InvalidNonceSize {
166                expected: Self::nonce_size(),
167                actual: nb.len(),
168            });
169        }
170        if ciphertext.len() < Self::tag_size() {
171            return Err(Error::aead_ciphertext_shorter_than_tag(
172                Self::tag_size(),
173                ciphertext.len(),
174            ));
175        }
176        let ad = associated_data.unwrap_or(&[]);
177        let body_len = ciphertext.len() - Self::tag_size();
178        let key_z = stack_secret::zeroizing_copy_16(kb);
179        let nonce_z = stack_secret::zeroizing_copy_16(nb);
180        let tag_arr =
181            <[u8; stack_secret::LEN]>::try_from(&ciphertext[body_len..]).map_err(|_| {
182                Error::VerificationFailed {
183                    operation: "AEAD tag verification".to_string(),
184                }
185            })?;
186        let mut buf = ciphertext[..body_len].to_vec();
187        if crate::romulus_n::romulus_n_decrypt_core(&key_z, &nonce_z, ad, &mut buf, &tag_arr) {
188            Ok(DecryptSemanticOutcome::Success(Zeroizing::new(buf)))
189        } else {
190            buf.zeroize();
191            Ok(DecryptSemanticOutcome::AuthenticationFailed)
192        }
193    }
194}
195
196/// Stateless Romulus-M facade using [`lib_q_core::Aead`].
197pub struct RomulusMAead;
198
199impl RomulusMAead {
200    pub const fn new() -> Self {
201        Self
202    }
203
204    pub const fn key_size() -> usize {
205        16
206    }
207
208    pub const fn nonce_size() -> usize {
209        16
210    }
211
212    pub const fn tag_size() -> usize {
213        16
214    }
215}
216
217impl Default for RomulusMAead {
218    fn default() -> Self {
219        Self::new()
220    }
221}
222
223impl Aead for RomulusMAead {
224    fn encrypt(
225        &self,
226        key: &AeadKey,
227        nonce: &Nonce,
228        plaintext: &[u8],
229        associated_data: Option<&[u8]>,
230    ) -> Result<Vec<u8>> {
231        let kb = key.as_bytes();
232        if kb.len() != Self::key_size() {
233            return Err(Error::InvalidKeySize {
234                expected: Self::key_size(),
235                actual: kb.len(),
236            });
237        }
238        let nb = nonce.as_bytes();
239        if nb.len() != Self::nonce_size() {
240            return Err(Error::InvalidNonceSize {
241                expected: Self::nonce_size(),
242                actual: nb.len(),
243            });
244        }
245        let ad = associated_data.unwrap_or(&[]);
246        let nonce_z = stack_secret::zeroizing_copy_16(nb);
247        let nonce_ref = GenericArray::from_slice(nonce_z.as_slice());
248        let cipher = {
249            let kz = stack_secret::zeroizing_copy_16(kb);
250            RomulusM::new(GenericArray::from_slice(kz.as_slice()))
251        };
252        let mut buf = plaintext.to_vec();
253        let tag = cipher
254            .encrypt_in_place_detached(nonce_ref, ad, &mut buf)
255            .map_err(|_| Error::EncryptionFailed {
256                operation: "Romulus-M encrypt".to_string(),
257            })?;
258        buf.extend_from_slice(tag.as_slice());
259        Ok(buf)
260    }
261
262    fn decrypt(
263        &self,
264        key: &AeadKey,
265        nonce: &Nonce,
266        ciphertext: &[u8],
267        associated_data: Option<&[u8]>,
268    ) -> Result<Vec<u8>> {
269        let kb = key.as_bytes();
270        if kb.len() != Self::key_size() {
271            return Err(Error::InvalidKeySize {
272                expected: Self::key_size(),
273                actual: kb.len(),
274            });
275        }
276        let nb = nonce.as_bytes();
277        if nb.len() != Self::nonce_size() {
278            return Err(Error::InvalidNonceSize {
279                expected: Self::nonce_size(),
280                actual: nb.len(),
281            });
282        }
283        if ciphertext.len() < Self::tag_size() {
284            return Err(Error::aead_ciphertext_shorter_than_tag(
285                Self::tag_size(),
286                ciphertext.len(),
287            ));
288        }
289        let ad = associated_data.unwrap_or(&[]);
290        let body_len = ciphertext.len() - Self::tag_size();
291        let key_z = stack_secret::zeroizing_copy_16(kb);
292        let nonce_z = stack_secret::zeroizing_copy_16(nb);
293        let tag_arr =
294            <[u8; stack_secret::LEN]>::try_from(&ciphertext[body_len..]).map_err(|_| {
295                Error::VerificationFailed {
296                    operation: "AEAD tag verification".to_string(),
297                }
298            })?;
299        let mut buf = ciphertext[..body_len].to_vec();
300        crate::romulus_m::romulus_m_decrypt(&key_z, &nonce_z, ad, &mut buf, &tag_arr).map_err(
301            |_| Error::VerificationFailed {
302                operation: "AEAD tag verification".to_string(),
303            },
304        )?;
305        Ok(buf)
306    }
307}
308
309impl AeadDecryptSemantic for RomulusMAead {
310    fn decrypt_semantic(
311        &self,
312        key: &AeadKey,
313        nonce: &Nonce,
314        ciphertext: &[u8],
315        associated_data: Option<&[u8]>,
316    ) -> Result<DecryptSemanticOutcome> {
317        let kb = key.as_bytes();
318        if kb.len() != Self::key_size() {
319            return Err(Error::InvalidKeySize {
320                expected: Self::key_size(),
321                actual: kb.len(),
322            });
323        }
324        let nb = nonce.as_bytes();
325        if nb.len() != Self::nonce_size() {
326            return Err(Error::InvalidNonceSize {
327                expected: Self::nonce_size(),
328                actual: nb.len(),
329            });
330        }
331        if ciphertext.len() < Self::tag_size() {
332            return Err(Error::aead_ciphertext_shorter_than_tag(
333                Self::tag_size(),
334                ciphertext.len(),
335            ));
336        }
337        let ad = associated_data.unwrap_or(&[]);
338        let body_len = ciphertext.len() - Self::tag_size();
339        let key_z = stack_secret::zeroizing_copy_16(kb);
340        let nonce_z = stack_secret::zeroizing_copy_16(nb);
341        let tag_arr =
342            <[u8; stack_secret::LEN]>::try_from(&ciphertext[body_len..]).map_err(|_| {
343                Error::VerificationFailed {
344                    operation: "AEAD tag verification".to_string(),
345                }
346            })?;
347        let mut buf = ciphertext[..body_len].to_vec();
348        if crate::romulus_m::romulus_m_decrypt_core(&key_z, &nonce_z, ad, &mut buf, &tag_arr) {
349            Ok(DecryptSemanticOutcome::Success(Zeroizing::new(buf)))
350        } else {
351            buf.zeroize();
352            Ok(DecryptSemanticOutcome::AuthenticationFailed)
353        }
354    }
355}