Skip to content

Commit 0baa1de

Browse files
committed
kmac: Refactor Rate trait based implementation
Integrating comments from @newpavlov's code review. - Remove the messy impl kmac macro in favour of a cleaner trait system approach. - Clean up test vectors and use hex-literal, and use raw NIST vectors copy/pasted from the PDFs to make review easier. - Move public API tests into tests/kmac.rs - Remove std from the std tests. - Remove the bench_full benchmarks, we will discuss this in a separate PR. - Adjust docstrings and README for completeness.
1 parent ec417ab commit 0baa1de

File tree

8 files changed

+572
-743
lines changed

8 files changed

+572
-743
lines changed

.github/workflows/kmac.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
- thumbv7em-none-eabi
3030
- wasm32-unknown-unknown
3131
steps:
32-
- uses: actions/checkout@v5
32+
- uses: actions/checkout@v6
3333
- uses: RustCrypto/actions/cargo-cache@master
3434
- uses: dtolnay/rust-toolchain@master
3535
with:
@@ -38,8 +38,6 @@ jobs:
3838
- run: cargo build --no-default-features --target ${{ matrix.target }}
3939

4040
minimal-versions:
41-
# disabled until belt-block gets published
42-
if: false
4341
uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
4442
with:
4543
working-directory: ${{ github.workflow }}
@@ -52,7 +50,7 @@ jobs:
5250
- 1.85.0 # MSRV
5351
- stable
5452
steps:
55-
- uses: actions/checkout@v5
53+
- uses: actions/checkout@v6
5654
- uses: RustCrypto/actions/cargo-cache@master
5755
- uses: dtolnay/rust-toolchain@master
5856
with:

kmac/README.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The below table summarises the equivalence with other MAC algorithms, where `K`
1616

1717
| Existing MAC Algorithm | KMAC Equivalent |
1818
|------------------------|------------------------------|
19-
| `AES-KMAC(K, text)` | `KMAC128(K, text, L=128, S)` |
19+
| `AES-CMAC(K, text)` | `KMAC128(K, text, L=128, S)` |
2020
| `HMAC-SHA256(K, text)` | `KMAC256(K, text, L=256, S)` |
2121
| `HMAC-SHA512(K, text)` | `KMAC256(K, text, L=512, S)` |
2222

@@ -63,13 +63,13 @@ let mac_bytes = hex!("
6363
kmac.verify_slice(&mac_bytes).unwrap();
6464
```
6565

66-
### Producing a fixed-length output
66+
### Producing a custom-length output
6767

68-
KMAC can also be used to produce an output of any length, which is particularly useful when KMAC is being used as a [key-derivation function (KDF)](https://en.wikipedia.org/wiki/Key_derivation_function).
68+
KMAC can produce an output of any length via `finalize_into_buf`, which is particularly useful when KMAC is being used as a [key-derivation function (KDF)](https://en.wikipedia.org/wiki/Key_derivation_function).
6969

70-
This method finalizes the KMAC and mixes the requested output length into the KMAC domain separation. The resulting bytes are dependent on the exact length of `out`. Use this when the output length is part of the MAC/derivation semantics (for example when the length itself must influence the MAC result).
70+
This method mixes the requested output length into the KMAC domain separation, so the resulting bytes depend on the exact length of `out`. This is distinct from both `finalize()` (which uses the type's default output length) and `finalize_xof()` (KMACXOF, which does not bind output length at all).
7171

72-
A customisation string can also be provided to further domain-separate different uses of KMAC with the same key when initialising the KMAC instance with `new_customization`.
72+
A customisation string can also be provided via `new_customization` to further domain-separate different uses of KMAC with the same key.
7373

7474
```rust
7575
use kmac::{Kmac256, Mac};
@@ -87,11 +87,9 @@ let expected = hex!("
8787
assert_eq!(output[..], expected[..]);
8888
```
8989

90-
### Producing a variable-length output
90+
### KMACXOF: variable-length output
9191

92-
Variable length KMAC output uses the `ExtendableOutput` trait. This is useful when the desired output length is not immediately known, and will append data to a buffer until the desired length is reached.
93-
94-
The XOF variant finalizes the sponge state without binding the requested output length into the KMAC domain separation. The returned reader yields an effectively infinite stream of bytes; reading the first `N` bytes from the reader (and truncating) produces the same `N`-byte prefix regardless of whether more bytes will be read later.
92+
KMACXOF (defined in Section 4.3.1 of [NIST SP 800-185]) produces an arbitrary-length output via the `ExtendableOutput` trait. Unlike `finalize()` and `finalize_into_buf()`, KMACXOF does not bind the output length into the domain separation — the returned reader yields an effectively infinite stream of bytes, and reading the first `N` bytes produces the same `N`-byte prefix regardless of how many bytes are read in total.
9593

9694
```rust
9795
use kmac::{Kmac256, Mac, ExtendableOutput, XofReader};

kmac/benches/mod.rs

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,9 @@
22
extern crate test;
33

44
use core::hint::black_box;
5-
use kmac::{KeyInit, Kmac128, Kmac256, Mac};
5+
use kmac::{KeyInit, Kmac128, Kmac256};
66
use test::Bencher;
77

8-
#[macro_export]
9-
macro_rules! bench_full {
10-
(
11-
$init:expr;
12-
$($name:ident $bs:expr;)*
13-
) => {
14-
$(
15-
#[bench]
16-
fn $name(b: &mut Bencher) {
17-
let data = [0; $bs];
18-
19-
b.iter(|| {
20-
let mut d = $init;
21-
digest::Update::update(&mut d, black_box(&data[..]));
22-
black_box(d.finalize());
23-
});
24-
25-
b.bytes = $bs;
26-
}
27-
)*
28-
};
29-
}
30-
31-
bench_full!(
32-
Kmac128::new(black_box(&Default::default()));
33-
kmac128_10 10;
34-
kmac128_100 100;
35-
kmac128_1000 1000;
36-
kmac128_10000 10000;
37-
);
38-
39-
bench_full!(
40-
Kmac256::new(black_box(&Default::default()));
41-
kmac256_10 10;
42-
kmac256_100 100;
43-
kmac256_1000 1000;
44-
kmac256_10000 10000;
45-
);
46-
478
digest::bench_update!(
489
Kmac128::new(black_box(&Default::default()));
4910
kmac128_update_10 10;

kmac/src/encoding.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,9 @@ pub(crate) fn right_encode(num: u64, buffer: &mut [u8; 9]) -> &[u8] {
2525
#[cfg(test)]
2626
mod tests {
2727
use super::*;
28-
extern crate std;
2928

3029
#[test]
3130
fn test_num_encoding_size() {
32-
// sha3::block_api::Sha3ReaderCore::<sha3::Sha3_256>::new(&[0; 200]);
3331
let test_cases = [
3432
(0, 1),
3533
(1, 1),
@@ -75,10 +73,17 @@ mod tests {
7573

7674
for i in 0..usize::BITS {
7775
let x: usize = 1 << i;
78-
let mut want = std::vec![0; 1];
79-
want.extend(x.to_be_bytes().iter().skip_while(|&&v| v == 0));
80-
want[0] = (want.len() - 1) as u8;
81-
assert_eq!(left_encode(x as u64, &mut buf), want, "#{x}");
76+
let be_bytes = x.to_be_bytes();
77+
let skip = be_bytes
78+
.iter()
79+
.position(|&v| v != 0)
80+
.unwrap_or(be_bytes.len() - 1);
81+
let len = be_bytes.len() - skip;
82+
let mut want = [0u8; 9];
83+
want[0] = len as u8;
84+
want[1..=len].copy_from_slice(&be_bytes[skip..]);
85+
let total_len = len + 1;
86+
assert_eq!(left_encode(x as u64, &mut buf), &want[..total_len], "#{x}");
8287
}
8388
}
8489

@@ -93,10 +98,17 @@ mod tests {
9398

9499
for i in 0..usize::BITS {
95100
let x: usize = 1 << i;
96-
let mut want =
97-
std::vec::Vec::from_iter(x.to_be_bytes().iter().copied().skip_while(|&v| v == 0));
98-
want.push(want.len() as u8);
99-
assert_eq!(right_encode(x as u64, &mut buf), want, "#{x}");
101+
let be_bytes = x.to_be_bytes();
102+
let skip = be_bytes
103+
.iter()
104+
.position(|&v| v != 0)
105+
.unwrap_or(be_bytes.len() - 1);
106+
let len = be_bytes.len() - skip;
107+
let mut want = [0u8; 9];
108+
want[..len].copy_from_slice(&be_bytes[skip..]);
109+
want[len] = len as u8;
110+
let total_len = len + 1;
111+
assert_eq!(right_encode(x as u64, &mut buf), &want[..total_len], "#{x}");
100112
}
101113
}
102114
}

kmac/src/kmac.rs

Lines changed: 0 additions & 126 deletions
This file was deleted.

0 commit comments

Comments
 (0)