use crate::sha3::{CShake128, CShake256, bytepad, left_encode, right_encode};
use alloc::vec::Vec;
macro_rules! kmac_impl {
($fn:ident, $xof:ident, $cshake:ty, $doc:literal, $xdoc:literal) => {
#[doc = $doc]
pub fn $fn(key: &[u8], data: &[u8], custom: &[u8], out: &mut [u8]) {
let mut c = <$cshake>::new(b"KMAC", custom);
let key_block = bytepad(&encode_string(key), <$cshake>::RATE);
c.update(&key_block);
c.update(data);
c.update(&right_encode((out.len() as u64) * 8));
c.squeeze(out);
}
#[doc = $xdoc]
pub fn $xof(key: &[u8], data: &[u8], custom: &[u8], out: &mut [u8]) {
let mut c = <$cshake>::new(b"KMAC", custom);
let key_block = bytepad(&encode_string(key), <$cshake>::RATE);
c.update(&key_block);
c.update(data);
c.update(&right_encode(0));
c.squeeze(out);
}
};
}
kmac_impl!(
kmac128,
kmac128_xof,
CShake128,
"KMAC128 keyed MAC (NIST SP 800-185 §4).",
"KMAC128 in XOF mode (KMACXOF128, NIST SP 800-185 §4)."
);
kmac_impl!(
kmac256,
kmac256_xof,
CShake256,
"KMAC256 keyed MAC (NIST SP 800-185 §4).",
"KMAC256 in XOF mode (KMACXOF256, NIST SP 800-185 §4)."
);
macro_rules! tuplehash_impl {
($fn:ident, $xof:ident, $cshake:ty, $doc:literal, $xdoc:literal) => {
#[doc = $doc]
pub fn $fn(tuple: &[&[u8]], custom: &[u8], out: &mut [u8]) {
let mut c = <$cshake>::new(b"TupleHash", custom);
for x in tuple {
c.update(&encode_string(x));
}
c.update(&right_encode((out.len() as u64) * 8));
c.squeeze(out);
}
#[doc = $xdoc]
pub fn $xof(tuple: &[&[u8]], custom: &[u8], out: &mut [u8]) {
let mut c = <$cshake>::new(b"TupleHash", custom);
for x in tuple {
c.update(&encode_string(x));
}
c.update(&right_encode(0));
c.squeeze(out);
}
};
}
tuplehash_impl!(
tuplehash128,
tuplehash128_xof,
CShake128,
"TupleHash128 (NIST SP 800-185 §5).",
"TupleHashXOF128 (NIST SP 800-185 §5)."
);
tuplehash_impl!(
tuplehash256,
tuplehash256_xof,
CShake256,
"TupleHash256 (NIST SP 800-185 §5).",
"TupleHashXOF256 (NIST SP 800-185 §5)."
);
const PARALLELHASH_CV_128: usize = 32;
const PARALLELHASH_CV_256: usize = 64;
macro_rules! parallelhash_impl {
($fn:ident, $xof:ident, $cshake:ty, $cv:expr, $doc:literal, $xdoc:literal) => {
#[doc = $doc]
#[doc = concat!(stringify!($cv), "-octet chaining value.")]
pub fn $fn(data: &[u8], block_size: usize, custom: &[u8], out: &mut [u8]) {
let z = parallelhash_inner::<$cshake>(data, block_size, $cv);
let mut c = <$cshake>::new(b"ParallelHash", custom);
c.update(&z);
c.update(&right_encode((out.len() as u64) * 8));
c.squeeze(out);
}
#[doc = $xdoc]
pub fn $xof(data: &[u8], block_size: usize, custom: &[u8], out: &mut [u8]) {
let z = parallelhash_inner::<$cshake>(data, block_size, $cv);
let mut c = <$cshake>::new(b"ParallelHash", custom);
c.update(&z);
c.update(&right_encode(0));
c.squeeze(out);
}
};
}
parallelhash_impl!(
parallelhash128,
parallelhash128_xof,
CShake128,
PARALLELHASH_CV_128,
"ParallelHash128 (NIST SP 800-185 §6).",
"ParallelHashXOF128 (NIST SP 800-185 §6)."
);
parallelhash_impl!(
parallelhash256,
parallelhash256_xof,
CShake256,
PARALLELHASH_CV_256,
"ParallelHash256 (NIST SP 800-185 §6).",
"ParallelHashXOF256 (NIST SP 800-185 §6)."
);
fn encode_string(s: &[u8]) -> Vec<u8> {
let mut out = left_encode((s.len() as u64) * 8);
out.extend_from_slice(s);
out
}
fn parallelhash_inner<C: CShakeBlock>(data: &[u8], block_size: usize, cv_len: usize) -> Vec<u8> {
assert!(block_size != 0, "ParallelHash block size must be non-zero");
let mut z = left_encode(block_size as u64);
let n_blocks = data.len().div_ceil(block_size);
for chunk in data.chunks(block_size) {
let mut cv = alloc::vec![0u8; cv_len];
let mut c = C::new_bare();
c.update_bare(chunk);
c.squeeze_bare(&mut cv);
z.extend_from_slice(&cv);
}
z.extend_from_slice(&right_encode(n_blocks as u64));
z
}
trait CShakeBlock: Sized {
fn new_bare() -> Self;
fn update_bare(&mut self, data: &[u8]);
fn squeeze_bare(&mut self, out: &mut [u8]);
}
macro_rules! cshake_block {
($t:ty) => {
impl CShakeBlock for $t {
fn new_bare() -> Self {
<$t>::new(b"", b"")
}
fn update_bare(&mut self, data: &[u8]) {
self.update(data);
}
fn squeeze_bare(&mut self, out: &mut [u8]) {
self.squeeze(out);
}
}
};
}
cshake_block!(CShake128);
cshake_block!(CShake256);
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn kmac128_smoke() {
let key: Vec<u8> = (0x40u8..=0x5F).collect();
let data = [0x00u8, 0x01, 0x02, 0x03];
let expected = [
0xe5, 0x78, 0x0b, 0x0d, 0x3e, 0xa6, 0xf7, 0xd3, 0xa4, 0x29, 0xc5, 0x70, 0x6a, 0xa4, 0x3a, 0x00, 0xfa, 0xdb,
0xd7, 0xd4, 0x96, 0x28, 0x83, 0x9e, 0x31, 0x87, 0x24, 0x3f, 0x45, 0x6e, 0xe1, 0x4e,
];
let mut out = vec![0u8; 32];
kmac128(&key, &data, b"", &mut out);
assert_eq!(out, expected);
}
}