Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions digest/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.11.4 (UNRELEASED)
### Fixed
- `dev` MAC test helpers check that a tag with its first or last byte changed is rejected, and `initialized_mac_test` also verifies inputs shorter than 2 bytes.

## 0.11.3 (2026-04-03)
### Added
- `dev::initialized_mac_test` function ([#2367])
Expand Down
47 changes: 45 additions & 2 deletions digest/src/dev/mac.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{FixedOutputReset, Mac, common::KeyInit};
use crate::{FixedOutputReset, Mac, MacError, Output, common::KeyInit};

/// Tag truncation side used in MAC tests
#[derive(Clone, Copy, Debug)]
Expand Down Expand Up @@ -71,7 +71,14 @@ pub fn initialized_mac_test<M: Mac + Clone>(
}
}

Ok(())
// test verification over the whole message, including empty inputs
let mut mac = mac0.clone();
mac.update(input);
if verify(mac, tag, trunc_side).is_err() {
return Err("whole message verification");
}

check_wrong_tags(&mac0, input, tag, trunc_side)
}

/// Run resettable MAC test
Expand Down Expand Up @@ -123,6 +130,42 @@ pub fn reset_mac_test<M: Mac + KeyInit + FixedOutputReset + Clone>(
}
}

check_wrong_tags(&mac0, input, tag, trunc_side)
}

/// Verify `tag` with the method that matches `trunc_side`.
fn verify<M: Mac>(mac: M, tag: &[u8], trunc_side: MacTruncSide) -> Result<(), MacError> {
match trunc_side {
MacTruncSide::Left => mac.verify_truncated_left(tag),
MacTruncSide::Right => mac.verify_truncated_right(tag),
MacTruncSide::None => mac.verify_slice(tag),
}
}

/// Check that `tag` with its first or its last byte changed is rejected.
fn check_wrong_tags<M: Mac + Clone>(
mac0: &M,
input: &[u8],
tag: &[u8],
trunc_side: MacTruncSide,
) -> Result<(), &'static str> {
let n = tag.len();
if n == 0 {
return Ok(());
}
// The callers have already compared `tag` with the output, so `n` is at most the output size.
let mut buf = Output::<M>::default();
let wrong_tag = &mut buf[..n];
wrong_tag.copy_from_slice(tag);
for i in [0, n - 1] {
wrong_tag[i] ^= 1;
let mut mac = mac0.clone();
Mac::update(&mut mac, input);
if verify(mac, wrong_tag, trunc_side).is_ok() {
return Err("wrong tag accepted");
}
wrong_tag[i] ^= 1;
}
Ok(())
}

Expand Down
Loading