use super::Zuc256Keystream;
use crate::internal::mac::{MacCore, MacKeyPair, MacWord};
use core::mem::size_of;
use stdx::default::default;
pub trait MacTag: MacWord {}
impl MacTag for u32 {}
impl MacTag for u64 {}
impl MacTag for u128 {}
pub struct Zuc256Mac<T: MacTag>(MacCore<Zuc256Keystream, T>);
impl<T: MacTag> Zuc256Mac<T> {
#[must_use]
pub fn compute(ik: &[u8; 32], iv: &[u8; 23], msg: &[u8], bitlen: usize) -> T {
Self::new(ik, iv).finish(msg, bitlen)
}
#[must_use]
pub fn new(ik: &[u8; 32], iv: &[u8; 23]) -> Self {
let d = match size_of::<T>() {
4 => &D_32,
8 => &D_64,
16 => &D_128,
_ => unreachable!(),
};
let mut zuc = Zuc256Keystream::new_with_d(ik, iv, d);
let tag: T = T::gen_word(&mut zuc);
let key: T::KeyPair = T::KeyPair::gen_key_pair(&mut zuc);
Self(MacCore {
zuc,
key,
tag,
rem: default(),
cnt: 0,
})
}
pub fn update(&mut self, msg: &[u8]) {
self.0.update(msg);
}
pub fn finish(mut self, tail: &[u8], bitlen: usize) -> T {
let _ = self.0.finish(tail, bitlen);
self.0.tag ^= self.0.key.high();
self.0.tag
}
}
const D_32: [u8; 16] = [
0b010_0010, 0b010_1111, 0b010_0101, 0b010_1010, 0b110_1101, 0b100_0000, 0b100_0000, 0b100_0000,
0b100_0000, 0b100_0000, 0b100_0000, 0b100_0000, 0b100_0000, 0b101_0010, 0b001_0000, 0b011_0000,
];
const D_64: [u8; 16] = [
0b010_0011, 0b010_1111, 0b010_0100, 0b010_1010, 0b110_1101, 0b100_0000, 0b100_0000, 0b100_0000,
0b100_0000, 0b100_0000, 0b100_0000, 0b100_0000, 0b100_0000, 0b101_0010, 0b001_0000, 0b011_0000,
];
const D_128: [u8; 16] = [
0b010_0011, 0b010_1111, 0b010_0101, 0b010_1010, 0b110_1101, 0b100_0000, 0b100_0000, 0b100_0000,
0b100_0000, 0b100_0000, 0b100_0000, 0b100_0000, 0b100_0000, 0b101_0010, 0b001_0000, 0b011_0000,
];
impl<T: MacTag> digest::Update for Zuc256Mac<T> {
fn update(&mut self, data: &[u8]) {
Zuc256Mac::update(self, data);
}
}
impl<T: MacTag> digest::OutputSizeUser for Zuc256Mac<T> {
type OutputSize = <T as MacWord>::ByteSize;
}
impl<T: MacTag> digest::FixedOutput for Zuc256Mac<T> {
fn finalize_into(self, out: &mut digest::Output<Self>) {
let tag = self.finish(&[], 0);
*out = tag.to_be_array();
}
}
impl<T: MacTag> digest::MacMarker for Zuc256Mac<T> {}
#[cfg(test)]
mod tests {
use super::*;
struct Example {
k: [u8; 32],
iv: [u8; 23],
length: u32,
m: &'static [u8],
expected_32: u32,
expected_64: u64,
expected_128: u128,
}
static EXAMPLE_MAC_1: Example = Example {
k: [0; 32],
iv: [0; 23],
length: 400,
m: &[0; 50],
expected_32: 0x9b97_2a74,
expected_64: 0x673e_5499_0034_d38c,
expected_128: 0xd85e_54bb_cb96_0096_7084_c952_a165_4b26,
};
static EXAMPLE_MAC_2: Example = Example {
k: [0; 32],
iv: [0; 23],
length: 4000,
m: &[0x11; 500],
expected_32: 0x8754_f5cf,
expected_64: 0x130d_c225_e722_40cc,
expected_128: 0xdf1e_8307_b31c_c62b_eca1_ac6f_8190_c22f,
};
static EXAMPLE_MAC_3: Example = Example {
k: [0xff; 32],
iv: [0xff; 23],
length: 400,
m: &[0x00; 50],
expected_32: 0x1f30_79b4,
expected_64: 0x8c71_394d_3995_7725,
expected_128: 0xa35b_b274_b567_c48b_2831_9f11_1af3_4fbd,
};
static EXAMPLE_MAC_4: Example = Example {
k: [0xff; 32],
iv: [0xff; 23],
length: 4000,
m: &[0x11; 500],
expected_32: 0x5c7c_8b88,
expected_64: 0xea1d_ee54_4bb6_223b,
expected_128: 0x3a83_b554_be40_8ca5_4941_24ed_9d47_3205,
};
static ALL_EXAMPLES: &[&Example] = &[
&EXAMPLE_MAC_1,
&EXAMPLE_MAC_2,
&EXAMPLE_MAC_3,
&EXAMPLE_MAC_4,
];
#[test]
fn examples() {
for x in ALL_EXAMPLES {
let mac_32 = <Zuc256Mac<u32>>::compute(&x.k, &x.iv, x.m, x.length as usize);
assert_eq!(mac_32, x.expected_32);
let mac_64 = <Zuc256Mac<u64>>::compute(&x.k, &x.iv, x.m, x.length as usize);
assert_eq!(mac_64, x.expected_64);
let mac_128 = <Zuc256Mac<u128>>::compute(&x.k, &x.iv, x.m, x.length as usize);
assert_eq!(mac_128, x.expected_128);
}
}
#[test]
fn special_bitlen() {
let x = &EXAMPLE_MAC_2;
let bitlen = 145;
let mac_32 = <Zuc256Mac<u32>>::compute(&x.k, &x.iv, x.m, bitlen);
let expected_32 = 0x213e_1ce5; assert_eq!(mac_32, expected_32, "actual = {mac_32:08x}");
}
#[test]
fn zero_bitlen() {
let examples = [&EXAMPLE_MAC_1, &EXAMPLE_MAC_2];
for x in examples {
let bitlen = 0;
let mac_32 = <Zuc256Mac<u32>>::compute(&x.k, &x.iv, x.m, bitlen);
let expected_32 = 0x68dc_aaba; assert_eq!(mac_32, expected_32, "actual = {mac_32:08x}");
}
}
#[test]
fn streaming() {
fn check<T: MacTag>(x: &Example, parts: &[usize], expected: T) {
let mut mac = Zuc256Mac::<T>::new(&x.k, &x.iv);
for i in 1..parts.len() {
mac.update(&x.m[parts[i - 1]..parts[i]]);
}
let last = parts[parts.len() - 1];
let ans = mac.finish(&x.m[last..], x.length as usize - last * 8);
assert_eq!(ans, expected);
}
for x in ALL_EXAMPLES {
for bp in 0..(x.length as usize) / 8 {
check(x, &[0, bp], x.expected_32);
check(x, &[0, bp], x.expected_64);
check(x, &[0, bp], x.expected_128);
}
}
for x in ALL_EXAMPLES {
for bp1 in 0..16 {
for bp2 in 16..32 {
check(x, &[0, bp1, bp2], x.expected_32);
check(x, &[0, bp1, bp2], x.expected_64);
check(x, &[0, bp1, bp2], x.expected_128);
}
}
}
}
#[test]
fn test_digest() {
fn require_digest_mac<T: digest::Mac>() {}
require_digest_mac::<Zuc256Mac<u32>>();
require_digest_mac::<Zuc256Mac<u64>>();
require_digest_mac::<Zuc256Mac<u128>>();
}
}