1#![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 ad2msg_encryption,
28 g8a,
29 lfsr_gf56,
30 msg_decryption_m_inplace,
31 msg_encryption_m_inplace,
32 nonce_encryption,
33 reset_lfsr_gf56,
34 rho,
35 romulus_m_compute_w,
36};
37
38#[derive(Clone)]
40pub struct RomulusM {
41 key: Key<Self>,
42}
43
44impl Drop for RomulusM {
45 fn drop(&mut self) {
46 self.key.as_mut_slice().zeroize();
47 }
48}
49
50impl KeySizeUser for RomulusM {
51 type KeySize = U16;
52}
53
54impl KeyInit for RomulusM {
55 fn new(key: &Key<Self>) -> Self {
56 Self { key: *key }
57 }
58}
59
60impl AeadCore for RomulusM {
61 type NonceSize = U16;
62 type TagSize = U16;
63 type CiphertextOverhead = U0;
64}
65
66impl AeadInPlace for RomulusM {
67 fn encrypt_in_place_detached(
68 &self,
69 nonce: &Nonce<Self>,
70 associated_data: &[u8],
71 buffer: &mut [u8],
72 ) -> Result<Tag<Self>, Error> {
73 let k = crate::stack_secret::zeroizing_copy_16(self.key.as_slice());
74 let n = crate::stack_secret::zeroizing_copy_16(nonce.as_slice());
75 let tag = romulus_m_encrypt(&k, &n, associated_data, buffer)?;
76 Ok(Tag::<Self>::from(tag))
77 }
78
79 fn decrypt_in_place_detached(
80 &self,
81 nonce: &Nonce<Self>,
82 associated_data: &[u8],
83 buffer: &mut [u8],
84 tag: &Tag<Self>,
85 ) -> Result<(), Error> {
86 let k = crate::stack_secret::zeroizing_copy_16(self.key.as_slice());
87 let n = crate::stack_secret::zeroizing_copy_16(nonce.as_slice());
88 let tg = crate::stack_secret::zeroizing_copy_16(tag.as_slice());
89 romulus_m_decrypt(&k, &n, associated_data, buffer, &tg)
90 }
91}
92
93pub(crate) fn romulus_m_encrypt(
95 key: &[u8; 16],
96 nonce: &[u8; 16],
97 ad: &[u8],
98 buf: &mut [u8],
99) -> Result<[u8; 16], Error> {
100 let mut s = [0u8; 16];
101 let mut cnt = [0u8; 7];
102 reset_lfsr_gf56(&mut cnt);
103 let n_ad = AD_BLK_ODD;
104 let t_ad = AD_BLK_EVN;
105 let mlen_u = buf.len() as u64;
106 let xlen_init = mlen_u;
107 let mut ad_off = 0usize;
108 let mut adlen = ad.len() as u64;
109
110 let w = romulus_m_compute_w(adlen, xlen_init, n_ad, t_ad);
111
112 if adlen == 0 {
113 lfsr_gf56(&mut cnt);
114 } else {
115 while adlen > 0 {
116 adlen = ad_encryption(
117 ad,
118 &mut ad_off,
119 &mut s,
120 key,
121 adlen,
122 &mut cnt,
123 40,
124 n_ad,
125 t_ad,
126 );
127 }
128 }
129
130 let mut mac_off = 0usize;
131 let mut xlen = mlen_u;
132
133 if w & 8 == 0 {
134 xlen = ad2msg_encryption(buf, &mut mac_off, &mut cnt, &mut s, key, t_ad, 44, xlen);
135 } else if mlen_u == 0 {
136 lfsr_gf56(&mut cnt);
137 }
138
139 while xlen > 0 {
140 xlen = ad_encryption(
141 buf,
142 &mut mac_off,
143 &mut s,
144 key,
145 xlen,
146 &mut cnt,
147 44,
148 n_ad,
149 t_ad,
150 );
151 }
152
153 nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, w);
154
155 let mut tag = [0u8; 16];
156 g8a(&s, &mut tag);
157
158 reset_lfsr_gf56(&mut cnt);
159 s.copy_from_slice(&tag);
160
161 let msg_n = MSG_BLK;
162 let mut enc_off = 0usize;
163
164 if mlen_u > 0 {
165 nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 36);
166 let mut rem = mlen_u;
167 while rem > msg_n as u64 {
168 rem = msg_encryption_m_inplace(
169 buf,
170 &mut enc_off,
171 nonce,
172 &mut cnt,
173 &mut s,
174 key,
175 msg_n,
176 t_ad,
177 36,
178 rem,
179 );
180 }
181 let r = rem as usize;
182 let mut last = [0u8; 16];
183 last[..r].copy_from_slice(&buf[enc_off..enc_off + r]);
184 let mut ctmp = [0u8; 16];
185 rho(&last[..r], &mut ctmp, &mut s, r, 16);
186 buf[enc_off..enc_off + r].copy_from_slice(&ctmp[..r]);
187 }
188
189 Ok(tag)
190}
191
192pub(crate) fn romulus_m_decrypt(
196 key: &[u8; 16],
197 nonce: &[u8; 16],
198 ad: &[u8],
199 ct: &mut [u8],
200 tag: &[u8; 16],
201) -> Result<(), Error> {
202 let ok = romulus_m_decrypt_core(key, nonce, ad, ct, tag);
203 if ok {
204 Ok(())
205 } else {
206 ct.zeroize();
207 Err(Error)
208 }
209}
210
211pub(crate) fn romulus_m_decrypt_core(
213 key: &[u8; 16],
214 nonce: &[u8; 16],
215 ad: &[u8],
216 ct: &mut [u8],
217 tag: &[u8; 16],
218) -> bool {
219 let body_len = ct.len();
220 let xlen = body_len as u64;
221
222 let mut s = [0u8; 16];
223 let mut cnt = [0u8; 7];
224 reset_lfsr_gf56(&mut cnt);
225 let n_ad = AD_BLK_ODD;
226 let t_ad = AD_BLK_EVN;
227
228 s.copy_from_slice(tag);
229
230 let msg_n = MSG_BLK;
231 let mut clen = body_len as u64;
232 let mut off = 0usize;
233
234 if clen > 0 {
235 nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, 36);
236 while clen > msg_n as u64 {
237 clen = msg_decryption_m_inplace(
238 ct, &mut off, nonce, &mut cnt, &mut s, key, msg_n, t_ad, 36, clen,
239 );
240 }
241 let r = clen as usize;
242 let mut tmp = [0u8; 16];
243 tmp[..r].copy_from_slice(&ct[off..off + r]);
244 let mut ptmp = [0u8; 16];
245 crate::backend::irho(&mut ptmp, &tmp[..r], &mut s, r, 16);
246 ct[off..off + r].copy_from_slice(&ptmp[..r]);
247 }
248
249 s.fill(0);
250 reset_lfsr_gf56(&mut cnt);
251
252 let mut ad_off = 0usize;
253 let mut adlen = ad.len() as u64;
254 let w = romulus_m_compute_w(adlen, xlen, n_ad, t_ad);
255
256 if adlen == 0 {
257 lfsr_gf56(&mut cnt);
258 } else {
259 while adlen > 0 {
260 adlen = ad_encryption(
261 ad,
262 &mut ad_off,
263 &mut s,
264 key,
265 adlen,
266 &mut cnt,
267 40,
268 n_ad,
269 t_ad,
270 );
271 }
272 }
273
274 let mut mac_off = 0usize;
275 let mut xrem = xlen;
276
277 if w & 8 == 0 {
278 xrem = ad2msg_encryption(ct, &mut mac_off, &mut cnt, &mut s, key, t_ad, 44, xrem);
279 } else if body_len == 0 {
280 lfsr_gf56(&mut cnt);
281 }
282
283 while xrem > 0 {
284 xrem = ad_encryption(
285 ct,
286 &mut mac_off,
287 &mut s,
288 key,
289 xrem,
290 &mut cnt,
291 44,
292 n_ad,
293 t_ad,
294 );
295 }
296
297 nonce_encryption(nonce, &mut cnt, &mut s, key, t_ad, w);
298
299 let mut calc = [0u8; 16];
300 g8a(&s, &mut calc);
301 bool::from(calc.ct_eq(tag))
302}