Skip to content

Commit fc96a6f

Browse files
committed
Make all the AEGIS-MAC tests pass in pure-rust
1 parent 67e5008 commit fc96a6f

File tree

9 files changed

+615
-7
lines changed

9 files changed

+615
-7
lines changed

src/c/libaegis

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ mod basic_tests {
152152
assert_eq!(m2, m);
153153
}
154154

155-
#[cfg(not(feature = "pure-rust"))]
155+
#[cfg(feature = "std")]
156156
#[test]
157157
fn test_aegis128l_mac() {
158158
use crate::aegis128l::Aegis128LMac;

src/pure_rust/aegis128l.rs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,52 @@ impl State {
156156
}
157157
tag
158158
}
159+
160+
#[inline(always)]
161+
fn mac_finalize<const TAG_BYTES: usize>(&mut self, data_len: usize) -> Tag<TAG_BYTES> {
162+
let tmp = {
163+
let blocks = &self.blocks;
164+
let mut sizes = [0u8; 16];
165+
sizes[..8].copy_from_slice(&(data_len as u64 * 8).to_le_bytes());
166+
sizes[8..16].copy_from_slice(&(TAG_BYTES as u64 * 8).to_le_bytes());
167+
AesBlock::from_bytes(&sizes).xor(blocks[2])
168+
};
169+
for _ in 0..7 {
170+
self.update(tmp, tmp);
171+
}
172+
let blocks = &self.blocks;
173+
let mut tag = [0u8; TAG_BYTES];
174+
match TAG_BYTES {
175+
16 => tag.copy_from_slice(
176+
&blocks[0]
177+
.xor(blocks[1])
178+
.xor(blocks[2])
179+
.xor(blocks[3])
180+
.xor(blocks[4])
181+
.xor(blocks[5])
182+
.xor(blocks[6])
183+
.to_bytes(),
184+
),
185+
32 => {
186+
tag[..16].copy_from_slice(
187+
&blocks[0]
188+
.xor(blocks[1])
189+
.xor(blocks[2])
190+
.xor(blocks[3])
191+
.to_bytes(),
192+
);
193+
tag[16..].copy_from_slice(
194+
&blocks[4]
195+
.xor(blocks[5])
196+
.xor(blocks[6])
197+
.xor(blocks[7])
198+
.to_bytes(),
199+
);
200+
}
201+
_ => unreachable!(),
202+
}
203+
tag
204+
}
159205
}
160206

161207
/// Tag length in bits must be 128 or 256
@@ -357,3 +403,103 @@ impl<const TAG_BYTES: usize> Aegis128L<TAG_BYTES> {
357403
Ok(())
358404
}
359405
}
406+
407+
/// AEGIS-128L MAC with incremental update support.
408+
///
409+
/// The state can be cloned to authenticate multiple messages with the same key.
410+
///
411+
/// 256-bit output tags are recommended for security.
412+
///
413+
/// Note that AEGIS is not a hash function. It is a MAC that requires a secret key.
414+
/// Inputs leading to a state collision can be efficiently computed if the key is known.
415+
#[derive(Clone)]
416+
pub struct Aegis128LMac<const TAG_BYTES: usize> {
417+
state: State,
418+
buf: [u8; 32],
419+
buf_len: usize,
420+
msg_len: usize,
421+
}
422+
423+
impl<const TAG_BYTES: usize> Aegis128LMac<TAG_BYTES> {
424+
/// Initializes the MAC state with a key.
425+
///
426+
/// The state can be cloned to authenticate multiple messages with the same key.
427+
pub fn new(key: &Key) -> Self {
428+
let nonce = [0u8; 16];
429+
Self::new_with_nonce(key, &nonce)
430+
}
431+
432+
/// Initializes the MAC state with a key and a nonce.
433+
///
434+
/// The state can be cloned to authenticate multiple messages with the same key.
435+
pub fn new_with_nonce(key: &Key, nonce: &Nonce) -> Self {
436+
assert!(
437+
TAG_BYTES == 16 || TAG_BYTES == 32,
438+
"Invalid tag length, must be 16 or 32"
439+
);
440+
Aegis128LMac {
441+
state: State::new(key, nonce),
442+
buf: [0u8; 32],
443+
buf_len: 0,
444+
msg_len: 0,
445+
}
446+
}
447+
448+
/// Updates the MAC state with a message.
449+
///
450+
/// This function can be called multiple times to update the MAC state with additional data.
451+
pub fn update(&mut self, data: &[u8]) {
452+
self.msg_len += data.len();
453+
let mut offset = 0;
454+
455+
// Process buffered data first
456+
if self.buf_len > 0 {
457+
let needed = 32 - self.buf_len;
458+
if data.len() < needed {
459+
self.buf[self.buf_len..self.buf_len + data.len()].copy_from_slice(data);
460+
self.buf_len += data.len();
461+
return;
462+
}
463+
self.buf[self.buf_len..].copy_from_slice(&data[..needed]);
464+
self.state.absorb(&self.buf);
465+
self.buf_len = 0;
466+
offset = needed;
467+
}
468+
469+
// Process full blocks
470+
while offset + 32 <= data.len() {
471+
let mut block = [0u8; 32];
472+
block.copy_from_slice(&data[offset..offset + 32]);
473+
self.state.absorb(&block);
474+
offset += 32;
475+
}
476+
477+
// Buffer remaining
478+
if offset < data.len() {
479+
self.buf_len = data.len() - offset;
480+
self.buf[..self.buf_len].copy_from_slice(&data[offset..]);
481+
}
482+
}
483+
484+
/// Finalizes the MAC and returns the authentication tag.
485+
pub fn finalize(mut self) -> Tag<TAG_BYTES> {
486+
if self.buf_len > 0 || self.msg_len == 0 {
487+
self.buf[self.buf_len..].fill(0);
488+
self.state.absorb(&self.buf);
489+
}
490+
self.state.mac_finalize::<TAG_BYTES>(self.msg_len)
491+
}
492+
493+
/// Verifies the authentication tag.
494+
pub fn verify(self, expected: &Tag<TAG_BYTES>) -> Result<(), Error> {
495+
let tag = self.finalize();
496+
let mut acc = 0u8;
497+
for (a, b) in tag.iter().zip(expected.iter()) {
498+
acc |= a ^ b;
499+
}
500+
if acc != 0 {
501+
return Err(Error::InvalidTag);
502+
}
503+
Ok(())
504+
}
505+
}

src/pure_rust/aegis128x2.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,79 @@ impl State {
235235
}
236236
tag
237237
}
238+
239+
#[inline(always)]
240+
fn mac_finalize<const TAG_BYTES: usize>(&mut self, data_len: usize) -> Tag<TAG_BYTES> {
241+
let mut sizes = [0u8; 16];
242+
sizes[..8].copy_from_slice(&(data_len as u64 * 8).to_le_bytes());
243+
sizes[8..16].copy_from_slice(&(TAG_BYTES as u64 * 8).to_le_bytes());
244+
let u = AesBlock::from_bytes(&sizes);
245+
246+
let (s20, s21) = self.s2.as_blocks();
247+
let t0 = s20.xor(u);
248+
let t1 = s21.xor(u);
249+
let t = AesBlock2::from_blocks(t0, t1);
250+
251+
for _ in 0..7 {
252+
self.update(t, t);
253+
}
254+
255+
let (s00, s01) = self.s0.as_blocks();
256+
let (s10, s11) = self.s1.as_blocks();
257+
let (s20, s21) = self.s2.as_blocks();
258+
let (s30, s31) = self.s3.as_blocks();
259+
let (s40, s41) = self.s4.as_blocks();
260+
let (s50, s51) = self.s5.as_blocks();
261+
let (s60, s61) = self.s6.as_blocks();
262+
let (_s70, s71) = self.s7.as_blocks();
263+
let zeros = AesBlock::from_bytes(&[0u8; 16]);
264+
265+
if TAG_BYTES == 16 {
266+
let tag0 = s00.xor(s10).xor(s20).xor(s30).xor(s40).xor(s50).xor(s60);
267+
let tag1 = s01.xor(s11).xor(s21).xor(s31).xor(s41).xor(s51).xor(s61);
268+
let m0 = AesBlock2::from_blocks(tag0, zeros);
269+
let m1 = AesBlock2::from_blocks(tag1, zeros);
270+
self.update(m0, m1);
271+
} else {
272+
let tag1_lo = s01.xor(s11).xor(s21).xor(s31);
273+
let tag1_hi = s41.xor(s51).xor(s61).xor(s71);
274+
let m0 = AesBlock2::from_blocks(tag1_lo, zeros);
275+
let m1 = AesBlock2::from_blocks(tag1_hi, zeros);
276+
self.update(m0, m1);
277+
}
278+
279+
let (s20, _) = self.s2.as_blocks();
280+
let mut extra_sizes = [0u8; 16];
281+
extra_sizes[..8].copy_from_slice(&2u64.to_le_bytes());
282+
extra_sizes[8..16].copy_from_slice(&(TAG_BYTES as u64 * 8).to_le_bytes());
283+
let extra_block = s20.xor(AesBlock::from_bytes(&extra_sizes));
284+
let extra = AesBlock2::from_blocks(extra_block, zeros);
285+
286+
for _ in 0..7 {
287+
self.update(extra, extra);
288+
}
289+
290+
let (s00, _) = self.s0.as_blocks();
291+
let (s10, _) = self.s1.as_blocks();
292+
let (s20, _) = self.s2.as_blocks();
293+
let (s30, _) = self.s3.as_blocks();
294+
let (s40, _) = self.s4.as_blocks();
295+
let (s50, _) = self.s5.as_blocks();
296+
let (s60, _) = self.s6.as_blocks();
297+
let (s70, _) = self.s7.as_blocks();
298+
299+
let mut tag = [0u8; TAG_BYTES];
300+
if TAG_BYTES == 16 {
301+
let final_tag = s00.xor(s10).xor(s20).xor(s30).xor(s40).xor(s50).xor(s60);
302+
tag.copy_from_slice(&final_tag.to_bytes());
303+
} else {
304+
let final_lo = s00.xor(s10).xor(s20).xor(s30);
305+
let final_hi = s40.xor(s50).xor(s60).xor(s70);
306+
tag[..16].copy_from_slice(&final_lo.to_bytes());
307+
tag[16..].copy_from_slice(&final_hi.to_bytes());
308+
}
309+
tag
310+
}
238311
}
239312

240313
/// AEGIS-128X2 authenticated encryption
@@ -503,7 +576,7 @@ impl<const TAG_BYTES: usize> Aegis128X2Mac<TAG_BYTES> {
503576
self.buf[self.buf_len..].fill(0);
504577
self.state.absorb(&self.buf);
505578
}
506-
self.state.mac::<TAG_BYTES>(0, self.msg_len)
579+
self.state.mac_finalize::<TAG_BYTES>(self.msg_len)
507580
}
508581

509582
pub fn verify(self, expected: &Tag<TAG_BYTES>) -> Result<(), Error> {

src/pure_rust/aegis128x4.rs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,102 @@ impl State {
277277
}
278278
tag
279279
}
280+
281+
#[inline(always)]
282+
fn mac_finalize<const TAG_BYTES: usize>(&mut self, data_len: usize) -> Tag<TAG_BYTES> {
283+
let mut sizes = [0u8; 16];
284+
sizes[..8].copy_from_slice(&(data_len as u64 * 8).to_le_bytes());
285+
sizes[8..16].copy_from_slice(&(TAG_BYTES as u64 * 8).to_le_bytes());
286+
let u = AesBlock::from_bytes(&sizes);
287+
288+
let (s20, s21, s22, s23) = self.s2.as_blocks();
289+
let t0 = s20.xor(u);
290+
let t1 = s21.xor(u);
291+
let t2 = s22.xor(u);
292+
let t3 = s23.xor(u);
293+
let t = AesBlock4::from_blocks(t0, t1, t2, t3);
294+
295+
for _ in 0..7 {
296+
self.update(t, t);
297+
}
298+
299+
let (s00, s01, s02, s03) = self.s0.as_blocks();
300+
let (s10, s11, s12, s13) = self.s1.as_blocks();
301+
let (s20, s21, s22, s23) = self.s2.as_blocks();
302+
let (s30, s31, s32, s33) = self.s3.as_blocks();
303+
let (s40, s41, s42, s43) = self.s4.as_blocks();
304+
let (s50, s51, s52, s53) = self.s5.as_blocks();
305+
let (s60, s61, s62, s63) = self.s6.as_blocks();
306+
let (_s70, s71, s72, s73) = self.s7.as_blocks();
307+
308+
let zeros = AesBlock::from_bytes(&[0u8; 16]);
309+
310+
if TAG_BYTES == 16 {
311+
let tag0 = s00.xor(s10).xor(s20).xor(s30).xor(s40).xor(s50).xor(s60);
312+
let tag1 = s01.xor(s11).xor(s21).xor(s31).xor(s41).xor(s51).xor(s61);
313+
let tag2 = s02.xor(s12).xor(s22).xor(s32).xor(s42).xor(s52).xor(s62);
314+
let tag3 = s03.xor(s13).xor(s23).xor(s33).xor(s43).xor(s53).xor(s63);
315+
316+
let m0 = AesBlock4::from_blocks(tag0, zeros, zeros, zeros);
317+
let m1 = AesBlock4::from_blocks(tag1, zeros, zeros, zeros);
318+
self.update(m0, m1);
319+
320+
let m0 = AesBlock4::from_blocks(tag2, zeros, zeros, zeros);
321+
let m1 = AesBlock4::from_blocks(tag3, zeros, zeros, zeros);
322+
self.update(m0, m1);
323+
} else {
324+
let tag1_lo = s01.xor(s11).xor(s21).xor(s31);
325+
let tag1_hi = s41.xor(s51).xor(s61).xor(s71);
326+
let tag2_lo = s02.xor(s12).xor(s22).xor(s32);
327+
let tag2_hi = s42.xor(s52).xor(s62).xor(s72);
328+
let tag3_lo = s03.xor(s13).xor(s23).xor(s33);
329+
let tag3_hi = s43.xor(s53).xor(s63).xor(s73);
330+
331+
let m0 = AesBlock4::from_blocks(tag1_lo, zeros, zeros, zeros);
332+
let m1 = AesBlock4::from_blocks(tag1_hi, zeros, zeros, zeros);
333+
self.update(m0, m1);
334+
335+
let m0 = AesBlock4::from_blocks(tag2_lo, zeros, zeros, zeros);
336+
let m1 = AesBlock4::from_blocks(tag2_hi, zeros, zeros, zeros);
337+
self.update(m0, m1);
338+
339+
let m0 = AesBlock4::from_blocks(tag3_lo, zeros, zeros, zeros);
340+
let m1 = AesBlock4::from_blocks(tag3_hi, zeros, zeros, zeros);
341+
self.update(m0, m1);
342+
}
343+
344+
let (s20, _, _, _) = self.s2.as_blocks();
345+
let mut extra_sizes = [0u8; 16];
346+
extra_sizes[..8].copy_from_slice(&4u64.to_le_bytes());
347+
extra_sizes[8..16].copy_from_slice(&(TAG_BYTES as u64 * 8).to_le_bytes());
348+
let extra_block = s20.xor(AesBlock::from_bytes(&extra_sizes));
349+
let extra = AesBlock4::from_blocks(extra_block, zeros, zeros, zeros);
350+
351+
for _ in 0..7 {
352+
self.update(extra, extra);
353+
}
354+
355+
let (s00, _, _, _) = self.s0.as_blocks();
356+
let (s10, _, _, _) = self.s1.as_blocks();
357+
let (s20, _, _, _) = self.s2.as_blocks();
358+
let (s30, _, _, _) = self.s3.as_blocks();
359+
let (s40, _, _, _) = self.s4.as_blocks();
360+
let (s50, _, _, _) = self.s5.as_blocks();
361+
let (s60, _, _, _) = self.s6.as_blocks();
362+
let (s70, _, _, _) = self.s7.as_blocks();
363+
364+
let mut tag = [0u8; TAG_BYTES];
365+
if TAG_BYTES == 16 {
366+
let final_tag = s00.xor(s10).xor(s20).xor(s30).xor(s40).xor(s50).xor(s60);
367+
tag.copy_from_slice(&final_tag.to_bytes());
368+
} else {
369+
let final_lo = s00.xor(s10).xor(s20).xor(s30);
370+
let final_hi = s40.xor(s50).xor(s60).xor(s70);
371+
tag[..16].copy_from_slice(&final_lo.to_bytes());
372+
tag[16..].copy_from_slice(&final_hi.to_bytes());
373+
}
374+
tag
375+
}
280376
}
281377

282378
/// AEGIS-128X4 authenticated encryption
@@ -545,7 +641,7 @@ impl<const TAG_BYTES: usize> Aegis128X4Mac<TAG_BYTES> {
545641
self.buf[self.buf_len..].fill(0);
546642
self.state.absorb(&self.buf);
547643
}
548-
self.state.mac::<TAG_BYTES>(0, self.msg_len)
644+
self.state.mac_finalize::<TAG_BYTES>(self.msg_len)
549645
}
550646

551647
pub fn verify(self, expected: &Tag<TAG_BYTES>) -> Result<(), Error> {

0 commit comments

Comments
 (0)