use ic_core::traits::{Algorithm, SelfTest};
use ic_core::{ensure, Result};
use ic_hash::sp800_185::{right_encode, MAX_ENCODE};
const MAX_TAG: usize = 64;
macro_rules! kmac {
($name:ident, $cshake:ty, $id:literal, $disp:literal, $bits:literal) => {
#[doc = concat!("SP 800-185 ", $disp, ", offering ", $bits, "-bit security.")]
#[derive(Clone)]
pub struct $name {
inner: $cshake,
}
impl Algorithm for $name {
const ID: &'static str = $id;
const NAME: &'static str = $disp;
}
impl $name {
pub fn new(key: &[u8], custom: &[u8]) -> Self {
let mut inner = <$cshake>::new(b"KMAC", custom);
inner.absorb_bytepadded_string(key);
Self { inner }
}
pub fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
pub fn finalize(mut self, out: &mut [u8]) {
let mut buf = [0u8; MAX_ENCODE];
let used = right_encode((out.len() as u64) * 8, &mut buf);
self.inner.update(&buf[..used]);
self.inner.finalize_xof(out);
}
pub fn finalize_xof(mut self, out: &mut [u8]) {
let mut buf = [0u8; MAX_ENCODE];
let used = right_encode(0, &mut buf);
self.inner.update(&buf[..used]);
self.inner.finalize_xof(out);
}
pub fn mac(key: &[u8], custom: &[u8], data: &[u8], out: &mut [u8]) {
let mut k = Self::new(key, custom);
k.update(data);
k.finalize(out);
}
pub fn mac_xof(key: &[u8], custom: &[u8], data: &[u8], out: &mut [u8]) {
let mut k = Self::new(key, custom);
k.update(data);
k.finalize_xof(out);
}
pub fn verify(key: &[u8], custom: &[u8], data: &[u8], tag: &[u8]) -> Result<()> {
ensure!(
!tag.is_empty() && tag.len() <= MAX_TAG,
InvalidLength,
"kmac tag length"
);
let mut expected = [0u8; MAX_TAG];
Self::mac(key, custom, data, &mut expected[..tag.len()]);
ensure!(
ic_core::ct::verify(&expected[..tag.len()], tag),
AuthenticationFailed,
$id
);
Ok(())
}
}
impl SelfTest for $name {
fn self_test() -> Result<()> {
let key = [0x40u8; 32];
let mut short = [0u8; 32];
let mut long = [0u8; 64];
Self::mac(&key, b"self-test", b"message", &mut short);
Self::mac(&key, b"self-test", b"message", &mut long);
ensure!(short[..] != long[..32], SelfTestFailed, $id);
let mut again = [0u8; 32];
Self::mac(&key, b"self-test", b"message", &mut again);
ensure!(ic_core::ct::verify(&short, &again), SelfTestFailed, $id);
Self::verify(&key, b"self-test", b"message", &short)?;
let mut tampered = short;
tampered[0] ^= 1;
ensure!(
Self::verify(&key, b"self-test", b"message", &tampered).is_err(),
SelfTestFailed,
$id
);
ensure!(
Self::verify(&key, b"other", b"message", &short).is_err(),
SelfTestFailed,
$id
);
Ok(())
}
}
};
}
kmac!(Kmac128, ic_hash::CShake128, "kmac128", "KMAC128", "128");
kmac!(Kmac256, ic_hash::CShake256, "kmac256", "KMAC256", "256");
#[cfg(test)]
mod tests {
use super::*;
use ic_hash::sp800_185::{left_encode, CShake128, CShake256};
fn reference_kmac(
rate: usize,
key: &[u8],
custom: &[u8],
data: &[u8],
out: &mut [u8],
xof: bool,
) {
fn enc(x: u64) -> Vec<u8> {
let mut bytes = x.to_be_bytes().to_vec();
while bytes.len() > 1 && bytes[0] == 0 {
bytes.remove(0);
}
let mut v = vec![bytes.len() as u8];
v.extend_from_slice(&bytes);
v
}
fn renc(x: u64) -> Vec<u8> {
let mut bytes = x.to_be_bytes().to_vec();
while bytes.len() > 1 && bytes[0] == 0 {
bytes.remove(0);
}
let n = bytes.len() as u8;
bytes.push(n);
bytes
}
let mut message = enc(rate as u64);
message.extend_from_slice(&enc((key.len() as u64) * 8));
message.extend_from_slice(key);
while message.len() % rate != 0 {
message.push(0);
}
message.extend_from_slice(data);
message.extend_from_slice(&renc(if xof { 0 } else { (out.len() as u64) * 8 }));
if rate == 168 {
CShake128::xof(b"KMAC", custom, &message, out);
} else {
CShake256::xof(b"KMAC", custom, &message, out);
}
}
#[test]
fn kmac_matches_an_independent_construction() {
let cases: &[(&[u8], &[u8], &[u8])] = &[
(&[0x40u8; 32], b"", b""),
(&[0x40u8; 32], b"My Tagged Application", b"\x00\x01\x02\x03"),
(b"short key", b"S", &[0xa5u8; 500]),
(&[0x11u8; 200], b"", b"key longer than the rate"),
];
for (key, custom, data) in cases {
for len in [16usize, 32, 64] {
let mut want = vec![0u8; len];
let mut got = vec![0u8; len];
reference_kmac(168, key, custom, data, &mut want, false);
Kmac128::mac(key, custom, data, &mut got);
assert_eq!(got, want, "KMAC128 fixed, {len} bytes");
reference_kmac(136, key, custom, data, &mut want, false);
Kmac256::mac(key, custom, data, &mut got);
assert_eq!(got, want, "KMAC256 fixed, {len} bytes");
reference_kmac(168, key, custom, data, &mut want, true);
Kmac128::mac_xof(key, custom, data, &mut got);
assert_eq!(got, want, "KMAC128 xof, {len} bytes");
reference_kmac(136, key, custom, data, &mut want, true);
Kmac256::mac_xof(key, custom, data, &mut got);
assert_eq!(got, want, "KMAC256 xof, {len} bytes");
}
}
}
#[test]
fn tag_length_is_bound_into_the_tag() {
let key = [0x7fu8; 32];
let mut short = [0u8; 32];
let mut long = [0u8; 64];
Kmac128::mac(&key, b"", b"message", &mut short);
Kmac128::mac(&key, b"", b"message", &mut long);
assert_ne!(short[..], long[..32], "truncation must not forge");
}
#[test]
fn the_xof_variant_extends_rather_than_changes() {
let key = [0x7fu8; 32];
let mut short = [0u8; 32];
let mut long = [0u8; 64];
Kmac128::mac_xof(&key, b"", b"message", &mut short);
Kmac128::mac_xof(&key, b"", b"message", &mut long);
assert_eq!(short[..], long[..32], "the xof output is a prefix");
}
#[test]
fn streaming_matches_the_one_shot() {
let key = [0x31u8; 32];
let data = [0x62u8; 777];
let mut one = [0u8; 32];
Kmac256::mac(&key, b"S", &data, &mut one);
let mut k = Kmac256::new(&key, b"S");
for chunk in data.chunks(13) {
k.update(chunk);
}
let mut streamed = [0u8; 32];
k.finalize(&mut streamed);
assert_eq!(one, streamed);
}
#[test]
fn keys_and_customization_both_change_the_tag() {
let mut a = [0u8; 32];
let mut b = [0u8; 32];
Kmac128::mac(&[1u8; 32], b"S", b"m", &mut a);
Kmac128::mac(&[2u8; 32], b"S", b"m", &mut b);
assert_ne!(a, b, "the key matters");
Kmac128::mac(&[1u8; 32], b"T", b"m", &mut b);
assert_ne!(a, b, "the customization matters");
}
#[test]
fn verification_rejects_tampering_and_bad_lengths() {
let key = [0x55u8; 32];
let mut tag = [0u8; 32];
Kmac128::mac(&key, b"S", b"message", &mut tag);
Kmac128::verify(&key, b"S", b"message", &tag).unwrap();
for bit in [0usize, 7, 128, 255] {
let mut bad = tag;
bad[bit / 8] ^= 1 << (bit % 8);
assert!(Kmac128::verify(&key, b"S", b"message", &bad).is_err());
}
assert!(Kmac128::verify(&key, b"S", b"messagf", &tag).is_err());
assert!(Kmac128::verify(&[0u8; 32], b"S", b"message", &tag).is_err());
assert!(
Kmac128::verify(&key, b"S", b"message", &[]).is_err(),
"empty tag"
);
assert!(
Kmac128::verify(&key, b"S", b"message", &[0u8; 65]).is_err(),
"over-long tag"
);
}
#[test]
fn both_self_tests_pass() {
Kmac128::self_test().unwrap();
Kmac256::self_test().unwrap();
}
#[test]
fn the_encoding_helpers_are_reachable() {
let mut buf = [0u8; MAX_ENCODE];
assert_eq!(left_encode(168, &mut buf), 2);
assert_eq!(&buf[..2], &[0x01, 0xa8]);
}
}