1use blake3::Hasher;
35use rand::RngCore;
36use serde::{Deserialize, Serialize};
37use std::fmt;
38use zeroize::{Zeroize, ZeroizeOnDrop};
39
40pub type AggregateMacResult<T> = Result<T, AggregateMacError>;
42
43#[derive(Debug, Clone, PartialEq, Eq)]
45pub enum AggregateMacError {
46 EmptyMessages,
48 InvalidTag,
50 SerializationFailed,
52 DeserializationFailed,
54}
55
56impl fmt::Display for AggregateMacError {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 match self {
59 AggregateMacError::EmptyMessages => write!(f, "Empty message list"),
60 AggregateMacError::InvalidTag => write!(f, "Invalid tag"),
61 AggregateMacError::SerializationFailed => write!(f, "Serialization failed"),
62 AggregateMacError::DeserializationFailed => write!(f, "Deserialization failed"),
63 }
64 }
65}
66
67impl std::error::Error for AggregateMacError {}
68
69#[derive(Clone, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
73pub struct AggregateMacKey {
74 key: [u8; 32],
75}
76
77#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
81pub struct AggregateTag {
82 tag: [u8; 32],
83 count: usize,
84}
85
86pub struct AggregateMacBuilder<'a> {
88 key: &'a AggregateMacKey,
89}
90
91#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
93pub struct MacTag {
94 tag: [u8; 32],
95}
96
97impl AggregateMacKey {
98 pub fn generate() -> Self {
100 let mut key = [0u8; 32];
101 rand::thread_rng().fill_bytes(&mut key);
102 Self { key }
103 }
104
105 pub fn from_bytes(bytes: [u8; 32]) -> Self {
107 Self { key: bytes }
108 }
109
110 pub fn to_bytes(&self) -> [u8; 32] {
112 self.key
113 }
114
115 pub fn authenticate(&self, message: &[u8]) -> MacTag {
117 self.authenticate_with_index(message, 0)
118 }
119
120 pub fn authenticate_with_index(&self, message: &[u8], index: usize) -> MacTag {
122 let mut hasher = Hasher::new_keyed(&self.key);
123 hasher.update(b"AggregateMac-v1:");
124 hasher.update(&index.to_le_bytes());
125 hasher.update(b":");
126 hasher.update(message);
127
128 let hash = hasher.finalize();
129 let mut tag = [0u8; 32];
130 tag.copy_from_slice(hash.as_bytes());
131
132 MacTag { tag }
133 }
134
135 pub fn authenticate_batch(&self, messages: &[&[u8]]) -> AggregateMacResult<AggregateTag> {
137 if messages.is_empty() {
138 return Err(AggregateMacError::EmptyMessages);
139 }
140
141 let mut aggregate_tag = [0u8; 32];
142
143 for (index, message) in messages.iter().enumerate() {
144 let mac_tag = self.authenticate_with_index(message, index);
145 for (i, byte) in aggregate_tag.iter_mut().enumerate() {
147 *byte ^= mac_tag.tag[i];
148 }
149 }
150
151 Ok(AggregateTag {
152 tag: aggregate_tag,
153 count: messages.len(),
154 })
155 }
156
157 pub fn verify(&self, message: &[u8], tag: &MacTag) -> bool {
159 let expected = self.authenticate(message);
160 constant_time_eq(&expected.tag, &tag.tag)
161 }
162
163 pub fn verify_with_index(&self, message: &[u8], index: usize, tag: &MacTag) -> bool {
165 let expected = self.authenticate_with_index(message, index);
166 constant_time_eq(&expected.tag, &tag.tag)
167 }
168
169 pub fn verify_batch(&self, messages: &[&[u8]], aggregate_tag: &AggregateTag) -> bool {
171 if messages.is_empty() || messages.len() != aggregate_tag.count {
172 return false;
173 }
174
175 let expected = match self.authenticate_batch(messages) {
176 Ok(tag) => tag,
177 Err(_) => return false,
178 };
179
180 constant_time_eq(&expected.tag, &aggregate_tag.tag)
181 }
182
183 pub fn builder(&self) -> AggregateMacBuilder<'_> {
185 AggregateMacBuilder { key: self }
186 }
187}
188
189impl<'a> AggregateMacBuilder<'a> {
190 pub fn new(key: &'a AggregateMacKey) -> Self {
192 Self { key }
193 }
194
195 pub fn authenticate_batch(&self, messages: &[&[u8]]) -> AggregateTag {
197 self.key
198 .authenticate_batch(messages)
199 .expect("messages should not be empty")
200 }
201
202 pub fn authenticate_iter<I>(&self, messages: I) -> AggregateMacResult<AggregateTag>
204 where
205 I: IntoIterator<Item = Vec<u8>>,
206 {
207 let messages: Vec<Vec<u8>> = messages.into_iter().collect();
208 let message_refs: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
209 self.key.authenticate_batch(&message_refs)
210 }
211
212 pub fn verify_batch(&self, messages: &[&[u8]], tag: &AggregateTag) -> bool {
214 self.key.verify_batch(messages, tag)
215 }
216}
217
218impl AggregateTag {
219 pub fn count(&self) -> usize {
221 self.count
222 }
223
224 pub fn to_bytes(&self) -> Vec<u8> {
226 crate::codec::encode(self).unwrap_or_default()
227 }
228
229 pub fn from_bytes(bytes: &[u8]) -> AggregateMacResult<Self> {
231 crate::codec::decode(bytes).map_err(|_| AggregateMacError::DeserializationFailed)
232 }
233}
234
235impl MacTag {
236 pub fn to_bytes(&self) -> [u8; 32] {
238 self.tag
239 }
240
241 pub fn from_bytes(bytes: [u8; 32]) -> Self {
243 Self { tag: bytes }
244 }
245}
246
247fn constant_time_eq(a: &[u8; 32], b: &[u8; 32]) -> bool {
249 use subtle::ConstantTimeEq;
250 a.ct_eq(b).into()
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn test_single_message_authentication() {
259 let key = AggregateMacKey::generate();
260 let message = b"test message";
261
262 let tag = key.authenticate(message);
263 assert!(key.verify(message, &tag));
264 }
265
266 #[test]
267 fn test_single_message_wrong_key() {
268 let key1 = AggregateMacKey::generate();
269 let key2 = AggregateMacKey::generate();
270
271 let message = b"test message";
272 let tag = key1.authenticate(message);
273
274 assert!(!key2.verify(message, &tag));
275 }
276
277 #[test]
278 fn test_single_message_tampered() {
279 let key = AggregateMacKey::generate();
280 let message = b"test message";
281
282 let tag = key.authenticate(message);
283
284 let tampered = b"tampered message";
285 assert!(!key.verify(tampered, &tag));
286 }
287
288 #[test]
289 fn test_batch_authentication() {
290 let key = AggregateMacKey::generate();
291 let messages = vec![b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()];
292
293 let tag = key.authenticate_batch(&messages).unwrap();
294 assert!(key.verify_batch(&messages, &tag));
295 }
296
297 #[test]
298 fn test_batch_wrong_order() {
299 let key = AggregateMacKey::generate();
300 let messages = vec![b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()];
301
302 let tag = key.authenticate_batch(&messages).unwrap();
303
304 let reordered = vec![b"msg2".as_slice(), b"msg1".as_slice(), b"msg3".as_slice()];
306 assert!(!key.verify_batch(&reordered, &tag));
307 }
308
309 #[test]
310 fn test_batch_tampered_message() {
311 let key = AggregateMacKey::generate();
312 let messages = vec![b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()];
313
314 let tag = key.authenticate_batch(&messages).unwrap();
315
316 let tampered = vec![
317 b"msg1".as_slice(),
318 b"TAMPERED".as_slice(),
319 b"msg3".as_slice(),
320 ];
321 assert!(!key.verify_batch(&tampered, &tag));
322 }
323
324 #[test]
325 fn test_batch_wrong_count() {
326 let key = AggregateMacKey::generate();
327 let messages = vec![b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()];
328
329 let tag = key.authenticate_batch(&messages).unwrap();
330
331 let fewer = vec![b"msg1".as_slice(), b"msg2".as_slice()];
333 assert!(!key.verify_batch(&fewer, &tag));
334
335 let more = vec![
336 b"msg1".as_slice(),
337 b"msg2".as_slice(),
338 b"msg3".as_slice(),
339 b"msg4".as_slice(),
340 ];
341 assert!(!key.verify_batch(&more, &tag));
342 }
343
344 #[test]
345 fn test_batch_empty() {
346 let key = AggregateMacKey::generate();
347 let messages: Vec<&[u8]> = vec![];
348
349 assert!(key.authenticate_batch(&messages).is_err());
350 }
351
352 #[test]
353 fn test_indexed_authentication() {
354 let key = AggregateMacKey::generate();
355 let message = b"test message";
356
357 let tag0 = key.authenticate_with_index(message, 0);
358 let tag1 = key.authenticate_with_index(message, 1);
359
360 assert_ne!(tag0, tag1);
362
363 assert!(key.verify_with_index(message, 0, &tag0));
364 assert!(key.verify_with_index(message, 1, &tag1));
365
366 assert!(!key.verify_with_index(message, 0, &tag1));
368 assert!(!key.verify_with_index(message, 1, &tag0));
369 }
370
371 #[test]
372 fn test_builder_pattern() {
373 let key = AggregateMacKey::generate();
374 let messages = vec![b"msg1".as_slice(), b"msg2".as_slice()];
375
376 let builder = AggregateMacBuilder::new(&key);
377 let tag = builder.authenticate_batch(&messages);
378
379 assert!(builder.verify_batch(&messages, &tag));
380 }
381
382 #[test]
383 fn test_aggregate_tag_count() {
384 let key = AggregateMacKey::generate();
385 let messages = vec![b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()];
386
387 let tag = key.authenticate_batch(&messages).unwrap();
388 assert_eq!(tag.count(), 3);
389 }
390
391 #[test]
392 fn test_tag_serialization() {
393 let key = AggregateMacKey::generate();
394 let message = b"test message";
395
396 let tag = key.authenticate(message);
397 let bytes = tag.to_bytes();
398 let deserialized = MacTag::from_bytes(bytes);
399
400 assert_eq!(tag, deserialized);
401 assert!(key.verify(message, &deserialized));
402 }
403
404 #[test]
405 fn test_aggregate_tag_serialization() {
406 let key = AggregateMacKey::generate();
407 let messages = vec![b"msg1".as_slice(), b"msg2".as_slice()];
408
409 let tag = key.authenticate_batch(&messages).unwrap();
410 let bytes = tag.to_bytes();
411 let deserialized = AggregateTag::from_bytes(&bytes).unwrap();
412
413 assert_eq!(tag, deserialized);
414 assert!(key.verify_batch(&messages, &deserialized));
415 }
416
417 #[test]
418 fn test_key_serialization() {
419 let key = AggregateMacKey::generate();
420 let message = b"test message";
421
422 let bytes = key.to_bytes();
423 let deserialized = AggregateMacKey::from_bytes(bytes);
424
425 let tag = key.authenticate(message);
426 assert!(deserialized.verify(message, &tag));
427 }
428
429 #[test]
430 fn test_large_batch() {
431 let key = AggregateMacKey::generate();
432 let messages: Vec<Vec<u8>> = (0..100)
433 .map(|i| format!("message{}", i).into_bytes())
434 .collect();
435 let message_refs: Vec<&[u8]> = messages.iter().map(|m| m.as_slice()).collect();
436
437 let tag = key.authenticate_batch(&message_refs).unwrap();
438 assert!(key.verify_batch(&message_refs, &tag));
439 assert_eq!(tag.count(), 100);
440 }
441
442 #[test]
443 fn test_deterministic_tags() {
444 let key = AggregateMacKey::from_bytes([0x42; 32]);
445 let message = b"test message";
446
447 let tag1 = key.authenticate(message);
448 let tag2 = key.authenticate(message);
449
450 assert_eq!(tag1, tag2);
451 }
452
453 #[test]
454 fn test_different_message_sizes() {
455 let key = AggregateMacKey::generate();
456 let large_msg = vec![0x42u8; 1000];
457 let messages = vec![
458 b"short".as_slice(),
459 b"medium length message".as_slice(),
460 large_msg.as_slice(),
461 ];
462
463 let tag = key.authenticate_batch(&messages).unwrap();
464 assert!(key.verify_batch(&messages, &tag));
465 }
466
467 #[test]
468 fn test_xor_properties() {
469 let key = AggregateMacKey::generate();
470
471 let msg1 = vec![b"msg1".as_slice()];
473 let tag1 = key.authenticate_batch(&msg1).unwrap();
474
475 let msg2 = vec![b"msg1".as_slice(), b"msg1".as_slice()];
477 let tag2 = key.authenticate_batch(&msg2).unwrap();
478
479 assert_ne!(tag1.tag, tag2.tag);
481 }
482}