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::array::Array;
11use aead::{
12    AeadInOut,
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_arr =
87            Array::try_from(nonce_z.as_slice()).map_err(|_| Error::InvalidNonceSize {
88                expected: Self::nonce_size(),
89                actual: nonce_z.len(),
90            })?;
91        let cipher = {
92            let kz = stack_secret::zeroizing_copy_16(kb);
93            let key_arr = Array::try_from(kz.as_slice()).map_err(|_| Error::InvalidKeySize {
94                expected: Self::key_size(),
95                actual: kz.len(),
96            })?;
97            RomulusN::new(&key_arr)
98        };
99        let mut buf = plaintext.to_vec();
100        let tag = cipher
101            .encrypt_inout_detached(&nonce_arr, ad, buf.as_mut_slice().into())
102            .map_err(|_| Error::EncryptionFailed {
103                operation: "Romulus-N encrypt".to_string(),
104            })?;
105        buf.extend_from_slice(tag.as_slice());
106        Ok(buf)
107    }
108
109    fn decrypt(
110        &self,
111        key: &AeadKey,
112        nonce: &Nonce,
113        ciphertext: &[u8],
114        associated_data: Option<&[u8]>,
115    ) -> Result<Vec<u8>> {
116        let kb = key.as_bytes();
117        if kb.len() != Self::key_size() {
118            return Err(Error::InvalidKeySize {
119                expected: Self::key_size(),
120                actual: kb.len(),
121            });
122        }
123        let nb = nonce.as_bytes();
124        if nb.len() != Self::nonce_size() {
125            return Err(Error::InvalidNonceSize {
126                expected: Self::nonce_size(),
127                actual: nb.len(),
128            });
129        }
130        if ciphertext.len() < Self::tag_size() {
131            return Err(Error::aead_ciphertext_shorter_than_tag(
132                Self::tag_size(),
133                ciphertext.len(),
134            ));
135        }
136        let ad = associated_data.unwrap_or(&[]);
137        let body_len = ciphertext.len() - Self::tag_size();
138        let key_z = stack_secret::zeroizing_copy_16(kb);
139        let nonce_z = stack_secret::zeroizing_copy_16(nb);
140        let tag_arr =
141            <[u8; stack_secret::LEN]>::try_from(&ciphertext[body_len..]).map_err(|_| {
142                Error::VerificationFailed {
143                    operation: "AEAD tag verification".to_string(),
144                }
145            })?;
146        let mut buf = ciphertext[..body_len].to_vec();
147        crate::romulus_n::romulus_n_decrypt(&key_z, &nonce_z, ad, &mut buf, &tag_arr).map_err(
148            |_| Error::VerificationFailed {
149                operation: "AEAD tag verification".to_string(),
150            },
151        )?;
152        Ok(buf)
153    }
154}
155
156impl AeadDecryptSemantic for RomulusNAead {
157    fn decrypt_semantic(
158        &self,
159        key: &AeadKey,
160        nonce: &Nonce,
161        ciphertext: &[u8],
162        associated_data: Option<&[u8]>,
163    ) -> Result<DecryptSemanticOutcome> {
164        let kb = key.as_bytes();
165        if kb.len() != Self::key_size() {
166            return Err(Error::InvalidKeySize {
167                expected: Self::key_size(),
168                actual: kb.len(),
169            });
170        }
171        let nb = nonce.as_bytes();
172        if nb.len() != Self::nonce_size() {
173            return Err(Error::InvalidNonceSize {
174                expected: Self::nonce_size(),
175                actual: nb.len(),
176            });
177        }
178        if ciphertext.len() < Self::tag_size() {
179            return Err(Error::aead_ciphertext_shorter_than_tag(
180                Self::tag_size(),
181                ciphertext.len(),
182            ));
183        }
184        let ad = associated_data.unwrap_or(&[]);
185        let body_len = ciphertext.len() - Self::tag_size();
186        let key_z = stack_secret::zeroizing_copy_16(kb);
187        let nonce_z = stack_secret::zeroizing_copy_16(nb);
188        let tag_arr =
189            <[u8; stack_secret::LEN]>::try_from(&ciphertext[body_len..]).map_err(|_| {
190                Error::VerificationFailed {
191                    operation: "AEAD tag verification".to_string(),
192                }
193            })?;
194        let mut buf = ciphertext[..body_len].to_vec();
195        if crate::romulus_n::romulus_n_decrypt_core(&key_z, &nonce_z, ad, &mut buf, &tag_arr) {
196            Ok(DecryptSemanticOutcome::Success(Zeroizing::new(buf)))
197        } else {
198            buf.zeroize();
199            Ok(DecryptSemanticOutcome::AuthenticationFailed)
200        }
201    }
202}
203
204/// Stateless Romulus-M facade using [`lib_q_core::Aead`].
205pub struct RomulusMAead;
206
207impl RomulusMAead {
208    pub const fn new() -> Self {
209        Self
210    }
211
212    pub const fn key_size() -> usize {
213        16
214    }
215
216    pub const fn nonce_size() -> usize {
217        16
218    }
219
220    pub const fn tag_size() -> usize {
221        16
222    }
223}
224
225impl Default for RomulusMAead {
226    fn default() -> Self {
227        Self::new()
228    }
229}
230
231impl Aead for RomulusMAead {
232    fn encrypt(
233        &self,
234        key: &AeadKey,
235        nonce: &Nonce,
236        plaintext: &[u8],
237        associated_data: Option<&[u8]>,
238    ) -> Result<Vec<u8>> {
239        let kb = key.as_bytes();
240        if kb.len() != Self::key_size() {
241            return Err(Error::InvalidKeySize {
242                expected: Self::key_size(),
243                actual: kb.len(),
244            });
245        }
246        let nb = nonce.as_bytes();
247        if nb.len() != Self::nonce_size() {
248            return Err(Error::InvalidNonceSize {
249                expected: Self::nonce_size(),
250                actual: nb.len(),
251            });
252        }
253        let ad = associated_data.unwrap_or(&[]);
254        let nonce_z = stack_secret::zeroizing_copy_16(nb);
255        let nonce_arr =
256            Array::try_from(nonce_z.as_slice()).map_err(|_| Error::InvalidNonceSize {
257                expected: Self::nonce_size(),
258                actual: nonce_z.len(),
259            })?;
260        let cipher = {
261            let kz = stack_secret::zeroizing_copy_16(kb);
262            let key_arr = Array::try_from(kz.as_slice()).map_err(|_| Error::InvalidKeySize {
263                expected: Self::key_size(),
264                actual: kz.len(),
265            })?;
266            RomulusM::new(&key_arr)
267        };
268        let mut buf = plaintext.to_vec();
269        let tag = cipher
270            .encrypt_inout_detached(&nonce_arr, ad, buf.as_mut_slice().into())
271            .map_err(|_| Error::EncryptionFailed {
272                operation: "Romulus-M encrypt".to_string(),
273            })?;
274        buf.extend_from_slice(tag.as_slice());
275        Ok(buf)
276    }
277
278    fn decrypt(
279        &self,
280        key: &AeadKey,
281        nonce: &Nonce,
282        ciphertext: &[u8],
283        associated_data: Option<&[u8]>,
284    ) -> Result<Vec<u8>> {
285        let kb = key.as_bytes();
286        if kb.len() != Self::key_size() {
287            return Err(Error::InvalidKeySize {
288                expected: Self::key_size(),
289                actual: kb.len(),
290            });
291        }
292        let nb = nonce.as_bytes();
293        if nb.len() != Self::nonce_size() {
294            return Err(Error::InvalidNonceSize {
295                expected: Self::nonce_size(),
296                actual: nb.len(),
297            });
298        }
299        if ciphertext.len() < Self::tag_size() {
300            return Err(Error::aead_ciphertext_shorter_than_tag(
301                Self::tag_size(),
302                ciphertext.len(),
303            ));
304        }
305        let ad = associated_data.unwrap_or(&[]);
306        let body_len = ciphertext.len() - Self::tag_size();
307        let key_z = stack_secret::zeroizing_copy_16(kb);
308        let nonce_z = stack_secret::zeroizing_copy_16(nb);
309        let tag_arr =
310            <[u8; stack_secret::LEN]>::try_from(&ciphertext[body_len..]).map_err(|_| {
311                Error::VerificationFailed {
312                    operation: "AEAD tag verification".to_string(),
313                }
314            })?;
315        let mut buf = ciphertext[..body_len].to_vec();
316        crate::romulus_m::romulus_m_decrypt(&key_z, &nonce_z, ad, &mut buf, &tag_arr).map_err(
317            |_| Error::VerificationFailed {
318                operation: "AEAD tag verification".to_string(),
319            },
320        )?;
321        Ok(buf)
322    }
323}
324
325impl AeadDecryptSemantic for RomulusMAead {
326    fn decrypt_semantic(
327        &self,
328        key: &AeadKey,
329        nonce: &Nonce,
330        ciphertext: &[u8],
331        associated_data: Option<&[u8]>,
332    ) -> Result<DecryptSemanticOutcome> {
333        let kb = key.as_bytes();
334        if kb.len() != Self::key_size() {
335            return Err(Error::InvalidKeySize {
336                expected: Self::key_size(),
337                actual: kb.len(),
338            });
339        }
340        let nb = nonce.as_bytes();
341        if nb.len() != Self::nonce_size() {
342            return Err(Error::InvalidNonceSize {
343                expected: Self::nonce_size(),
344                actual: nb.len(),
345            });
346        }
347        if ciphertext.len() < Self::tag_size() {
348            return Err(Error::aead_ciphertext_shorter_than_tag(
349                Self::tag_size(),
350                ciphertext.len(),
351            ));
352        }
353        let ad = associated_data.unwrap_or(&[]);
354        let body_len = ciphertext.len() - Self::tag_size();
355        let key_z = stack_secret::zeroizing_copy_16(kb);
356        let nonce_z = stack_secret::zeroizing_copy_16(nb);
357        let tag_arr =
358            <[u8; stack_secret::LEN]>::try_from(&ciphertext[body_len..]).map_err(|_| {
359                Error::VerificationFailed {
360                    operation: "AEAD tag verification".to_string(),
361                }
362            })?;
363        let mut buf = ciphertext[..body_len].to_vec();
364        if crate::romulus_m::romulus_m_decrypt_core(&key_z, &nonce_z, ad, &mut buf, &tag_arr) {
365            Ok(DecryptSemanticOutcome::Success(Zeroizing::new(buf)))
366        } else {
367            buf.zeroize();
368            Ok(DecryptSemanticOutcome::AuthenticationFailed)
369        }
370    }
371}