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
60 changes: 60 additions & 0 deletions .github/workflows/kmac.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: kmac

on:
pull_request:
paths:
- ".github/workflows/kmac.yml"
- "kmac/**"
- "Cargo.*"
push:
branches: master

defaults:
run:
working-directory: kmac

env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-Dwarnings"

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- 1.85.0 # MSRV
- stable
target:
- thumbv7em-none-eabi
- wasm32-unknown-unknown
steps:
- uses: actions/checkout@v6
- uses: RustCrypto/actions/cargo-cache@master
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
- run: cargo build --no-default-features --target ${{ matrix.target }}

minimal-versions:
uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
with:
working-directory: ${{ github.workflow }}

test:
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- 1.85.0 # MSRV
- stable
steps:
- uses: actions/checkout@v6
- uses: RustCrypto/actions/cargo-cache@master
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- uses: RustCrypto/actions/cargo-hack-install@master
- run: cargo hack test --feature-powerset
- run: cargo test --release --all-features
42 changes: 42 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"cbc-mac",
"cmac",
"hmac",
"kmac",
"pmac",
"retail-mac",
]
Expand Down
22 changes: 22 additions & 0 deletions kmac/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "kmac"
version = "0.1.0"
description = "Keccak Message Authentication Code (KMAC)"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
edition = "2024"
readme = "README.md"
documentation = "https://docs.rs/kmac"
repository = "https://github.com/RustCrypto/MACs"
keywords = ["crypto", "mac", "kmac", "digest"]
categories = ["cryptography", "no-std"]
rust-version = "1.85"

[dependencies]
cshake = { git = "https://github.com/RustCrypto/hashes", branch = "master" }
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently using a pre-release version of cshake after the sha3 release to 0.11 refactors cshake into a separate crate. Will fix when cshake is released.

digest = { version = "0.11.0", features = ["mac"] }

[dev-dependencies]
digest = { version = "0.11.0", features = ["dev"] }
hex-literal = "1.1.0"
hex = "0.4.3"
122 changes: 122 additions & 0 deletions kmac/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# RustCrypto: KMAC

A rust implementation of [KMAC](https://en.wikipedia.org/wiki/SHA-3#Additional_instances), following the [NIST SP 800-185] specification.

This crate provides implementations for KMAC128, KMAC256, KMACXOF128, and KMACXOF256. KMAC is a PRF and keyed hash function based on the Keccak (SHA-3) sponge construction, designed for message authentication (MAC) and key derivation (KDF).

## NIST security guidance

Security guidance for KMAC is discussed in Section 8.4 of [NIST SP 800-185].

KMAC128 is built from cSHAKE128, giving it a security strength <= 128 bits. The `Kmac128` default MAC tag length is the NIST recommended 32 bytes (256 bits). The input key length must also be at least 16 bytes (128 bits) to achieve the full security strength.

KMAC256 is built from cSHAKE256, giving it a security strength <= 256 bits. The `Kmac256` default MAC tag length is the NIST recommended 64 bytes (512 bits). The input key length must also be at least 32 bytes (256 bits) to achieve the full security strength.

The below table summarises the equivalence with other MAC algorithms, where `K` is the input key, `text` is the input message, `L` is the output tag length, and `S` is an optional customization string.

| Existing MAC Algorithm | KMAC Equivalent |
|------------------------|------------------------------|
| `AES-CMAC(K, text)` | `KMAC128(K, text, L=128, S)` |
| `HMAC-SHA256(K, text)` | `KMAC256(K, text, L=256, S)` |
| `HMAC-SHA512(K, text)` | `KMAC256(K, text, L=512, S)` |

## Examples

### Generating a MAC
```rust
use kmac::{Kmac128, Mac, KeyInit};
use hex_literal::hex;

// Use KMAC128 to produce a MAC
let mut kmac = Kmac128::new_from_slice(b"key material").unwrap();
kmac.update(b"input message");

// `result` has type `CtOutput` which is a thin wrapper around array of
// bytes for providing constant time equality check
let result = kmac.finalize();

// To get underlying array use `into_bytes`, but be careful, since
// incorrect use of the code value may permit timing attacks which defeats
// the security provided by the `CtOutput`
let mac_bytes = result.into_bytes();
let expected = hex!("
c39a8f614f8821443599440df5402787
0f67e4c47919061584f14a616f3efcf5
");
assert_eq!(mac_bytes[..], expected[..]);
```

### Verifying a MAC
```rust
use kmac::{Kmac128, Mac, KeyInit};
use hex_literal::hex;

let mut kmac = Kmac128::new_from_slice(b"key material").unwrap();
kmac.update(b"input message");

let mac_bytes = hex!("
c39a8f614f8821443599440df5402787
0f67e4c47919061584f14a616f3efcf5
");

// `verify_slice` will return `Ok(())` if code is correct, `Err(MacError)` otherwise
kmac.verify_slice(&mac_bytes).unwrap();
```

### Producing a custom-length output

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).

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).

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

```rust
use kmac::{Kmac256, Mac};
use hex_literal::hex;

let mut mac = Kmac256::new_customization(b"key material", b"customization").unwrap();
mac.update(b"input message");
let mut output = [0u8; 32];
mac.finalize_into_buf(&mut output);

let expected = hex!("
85fb77da3a35e4c4b0057c3151e6cc54
ee401ffe65ec2f0239f439be8896f7b6
");
assert_eq!(output[..], expected[..]);
```

### KMACXOF: variable-length output

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.

```rust
use kmac::{Kmac256, Mac, ExtendableOutput, XofReader};
use hex_literal::hex;

let mut kmac = Kmac256::new_customization(b"key material", b"customization").unwrap();
kmac.update(b"input message");
let mut reader = kmac.finalize_xof();

let mut output = [0u8; 32];
reader.read(&mut output);

let expected = hex!("
b675b75668eab0706ab05650f34fa1b6
24051a9a42b5e42cfe9970e8f903d45b
");
assert_eq!(output[..], expected[..]);
```

## License

Licensed under either of:
- [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
- [MIT license](http://opensource.org/licenses/MIT)
at your option.

### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

[NIST SP 800-185]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf
22 changes: 22 additions & 0 deletions kmac/benches/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#![feature(test)]
extern crate test;

use core::hint::black_box;
use kmac::{KeyInit, Kmac128, Kmac256};
use test::Bencher;

digest::bench_update!(
Kmac128::new(black_box(&Default::default()));
kmac128_update_10 10;
kmac128_update_100 100;
kmac128_update_1000 1000;
kmac128_update_10000 10000;
);

digest::bench_update!(
Kmac256::new(black_box(&Default::default()));
kmac256_update_10 10;
kmac256_update_100 100;
kmac256_update_1000 1000;
kmac256_update_10000 10000;
);
Loading