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