Skip to main content

lib_q_romulus/
romulus_n.rs

1//! Romulus-N: nonce-based AEAD (Romulus v1.3).
2
3#![deny(unsafe_code)]
4
5use aead::consts::{
6    U0,
7    U16,
8};
9use aead::{
10    AeadCore,
11    AeadInPlace,
12    Error,
13    Key,
14    KeyInit,
15    KeySizeUser,
16    Nonce,
17    Tag,
18};
19use subtle::ConstantTimeEq;
20use zeroize::Zeroize;
21
22use crate::backend::{
23    AD_BLK_EVN,
24    AD_BLK_ODD,
25    MSG_BLK,
26    ad_encryption,
27    g8a,
28    lfsr_gf56,
29    msg_encryption_n_inplace,
30    nonce_encryption,
31    reset_lfsr_gf56,
32};
33
34/// Romulus-N AEAD with 128-bit key, 128-bit nonce, 128-bit tag.
35#[derive(Clone)]
36pub struct RomulusN {
37    key: Key<Self>,
38}
39
40impl Drop for RomulusN {
41    fn drop(&mut self) {
42        self.key.as_mut_slice().zeroize();
43    }
44}
45
46impl KeySizeUser for RomulusN {
47    type KeySize = U16;
48}
49
50impl KeyInit for RomulusN {
51    fn new(key: &Key<Self>) -> Self {
52        Self { key: *key }
53    }
54}
55
56impl AeadCore for RomulusN {
57    type NonceSize = U16;
58    type TagSize = U16;
59    type CiphertextOverhead = U0;
60}
61
62impl AeadInPlace for RomulusN {
63    fn encrypt_in_place_detached(
64        &self,
65        nonce: &Nonce<Self>,
66        associated_data: &[u8],
67        buffer: &mut [u8],
68    ) -> Result<Tag<Self>, Error> {
69        let k = crate::stack_secret::zeroizing_copy_16(self.key.as_slice());
70        let n = crate::stack_secret::zeroizing_copy_16(nonce.as_slice());
71        let tag = romulus_n_encrypt(&k, &n, associated_data, buffer)?;
72        Ok(Tag::<Self>::from(tag))
73    }
74
75    fn decrypt_in_place_detached(
76        &self,
77        nonce: &Nonce<Self>,
78        associated_data: &[u8],
79        buffer: &mut [u8],
80        tag: &Tag<Self>,
81    ) -> Result<(), Error> {
82        let k = crate::stack_secret::zeroizing_copy_16(self.key.as_slice());
83        let n = crate::stack_secret::zeroizing_copy_16(nonce.as_slice());
84        let tg = crate::stack_secret::zeroizing_copy_16(tag.as_slice());
85        romulus_n_decrypt(&k, &n, associated_data, buffer, &tg)
86    }
87}
88
89/// Encrypt plaintext in `buf` in place; ciphertext is written to `buf`. Returns authentication tag.
90pub(crate) fn romulus_n_encrypt(
91    key: &[u8; 16],
92    nonce: &[u8; 16],
93    ad: &[u8],
94    buf: &mut [u8],
95) -> Result<[u8; 16], Error> {
96    let mut s = [0u8; 16];
97    let mut cnt = [0u8; 7];
98    reset_lfsr_gf56(&mut cnt);
99    let n_ad = AD_BLK_ODD;
100    let t_ad = AD_BLK_EVN;
101    let mut a_off = 0usize;
102    let mut adlen = ad.len() as u64;
103
104    if adlen == 0 {
105        lfsr_gf56(&mut cnt);
106        nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x1A);
107    } else {
108        while adlen > 0 {
109            if adlen < n_ad as u64 {
110                adlen = ad_encryption(
111                    ad, &mut a_off, &mut s, key, adlen, &mut cnt, 0x08, n_ad, t_ad,
112                );
113                nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x1A);
114            } else if adlen == n_ad as u64 {
115                adlen = ad_encryption(
116                    ad, &mut a_off, &mut s, key, adlen, &mut cnt, 0x08, n_ad, t_ad,
117                );
118                nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x18);
119            } else if adlen < (n_ad + t_ad) as u64 {
120                adlen = ad_encryption(
121                    ad, &mut a_off, &mut s, key, adlen, &mut cnt, 0x08, n_ad, t_ad,
122                );
123                nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x1A);
124            } else if adlen == (n_ad + t_ad) as u64 {
125                adlen = ad_encryption(
126                    ad, &mut a_off, &mut s, key, adlen, &mut cnt, 0x08, n_ad, t_ad,
127                );
128                nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x18);
129            } else {
130                adlen = ad_encryption(
131                    ad, &mut a_off, &mut s, key, adlen, &mut cnt, 0x08, n_ad, t_ad,
132                );
133            }
134        }
135    }
136
137    reset_lfsr_gf56(&mut cnt);
138    let msg_n = MSG_BLK;
139    let mut mlen = buf.len() as u64;
140    let mut off = 0usize;
141
142    if mlen == 0 {
143        lfsr_gf56(&mut cnt);
144        nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x15);
145    } else {
146        while mlen > 0 {
147            if mlen < msg_n as u64 {
148                mlen = msg_encryption_n_inplace(
149                    buf, &mut off, nonce, &mut cnt, &mut s, key, msg_n, t_ad, 0x15, mlen, false,
150                );
151            } else if mlen == msg_n as u64 {
152                mlen = msg_encryption_n_inplace(
153                    buf, &mut off, nonce, &mut cnt, &mut s, key, msg_n, t_ad, 0x14, mlen, false,
154                );
155            } else {
156                mlen = msg_encryption_n_inplace(
157                    buf, &mut off, nonce, &mut cnt, &mut s, key, msg_n, t_ad, 0x04, mlen, false,
158                );
159            }
160        }
161    }
162
163    let mut tag = [0u8; 16];
164    g8a(&s, &mut tag);
165    Ok(tag)
166}
167
168/// Decrypt ciphertext in `buffer` to plaintext in place; verify `tag`.
169///
170/// On failure, `buffer` is zeroized. For Layer B semantic outcomes without double decryption,
171/// use [`romulus_n_decrypt_core`] and map the `bool` yourself.
172pub(crate) fn romulus_n_decrypt(
173    key: &[u8; 16],
174    nonce: &[u8; 16],
175    ad: &[u8],
176    ct: &mut [u8],
177    tag: &[u8; 16],
178) -> Result<(), Error> {
179    let ok = romulus_n_decrypt_core(key, nonce, ad, ct, tag);
180    if ok {
181        Ok(())
182    } else {
183        ct.zeroize();
184        Err(Error)
185    }
186}
187
188/// In-place Romulus-N decrypt; returns whether `tag` matches after the decrypt schedule.
189pub(crate) fn romulus_n_decrypt_core(
190    key: &[u8; 16],
191    nonce: &[u8; 16],
192    ad: &[u8],
193    ct: &mut [u8],
194    tag: &[u8; 16],
195) -> bool {
196    let mut s = [0u8; 16];
197    let mut cnt = [0u8; 7];
198    reset_lfsr_gf56(&mut cnt);
199    let n_ad = AD_BLK_ODD;
200    let t_ad = AD_BLK_EVN;
201    let mut a_off = 0usize;
202    let mut adlen = ad.len() as u64;
203
204    if adlen == 0 {
205        lfsr_gf56(&mut cnt);
206        nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x1A);
207    } else {
208        while adlen > 0 {
209            if adlen < n_ad as u64 {
210                adlen = ad_encryption(
211                    ad, &mut a_off, &mut s, key, adlen, &mut cnt, 0x08, n_ad, t_ad,
212                );
213                nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x1A);
214            } else if adlen == n_ad as u64 {
215                adlen = ad_encryption(
216                    ad, &mut a_off, &mut s, key, adlen, &mut cnt, 0x08, n_ad, t_ad,
217                );
218                nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x18);
219            } else if adlen < (n_ad + t_ad) as u64 {
220                adlen = ad_encryption(
221                    ad, &mut a_off, &mut s, key, adlen, &mut cnt, 0x08, n_ad, t_ad,
222                );
223                nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x1A);
224            } else if adlen == (n_ad + t_ad) as u64 {
225                adlen = ad_encryption(
226                    ad, &mut a_off, &mut s, key, adlen, &mut cnt, 0x08, n_ad, t_ad,
227                );
228                nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x18);
229            } else {
230                adlen = ad_encryption(
231                    ad, &mut a_off, &mut s, key, adlen, &mut cnt, 0x08, n_ad, t_ad,
232                );
233            }
234        }
235    }
236
237    reset_lfsr_gf56(&mut cnt);
238    let msg_n = MSG_BLK;
239    let mut mlen = ct.len() as u64;
240    let mut off = 0usize;
241
242    if mlen == 0 {
243        lfsr_gf56(&mut cnt);
244        nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 0x15);
245    } else {
246        while mlen > 0 {
247            if mlen < msg_n as u64 {
248                mlen = msg_encryption_n_inplace(
249                    ct, &mut off, nonce, &mut cnt, &mut s, key, msg_n, t_ad, 0x15, mlen, true,
250                );
251            } else if mlen == msg_n as u64 {
252                mlen = msg_encryption_n_inplace(
253                    ct, &mut off, nonce, &mut cnt, &mut s, key, msg_n, t_ad, 0x14, mlen, true,
254                );
255            } else {
256                mlen = msg_encryption_n_inplace(
257                    ct, &mut off, nonce, &mut cnt, &mut s, key, msg_n, t_ad, 0x04, mlen, true,
258                );
259            }
260        }
261    }
262
263    let mut calc = [0u8; 16];
264    g8a(&s, &mut calc);
265    bool::from(calc.ct_eq(tag))
266}