use crate::zerokms::IndexKey;
use hmac::{Hmac, Mac};
use sha2::Sha256;
use zeroize::{Zeroize, ZeroizeOnDrop};
use super::{path_values::PathSegment, ste_vec::TokenizedSelector};
type HmacSha256 = Hmac<Sha256>;
pub(crate) trait PrefixMac {
fn finalize_reset<const N: usize>(&mut self) -> [u8; N];
}
pub(crate) trait UpdatePrefixMac<T> {
fn update(&mut self, value: T);
}
pub(crate) trait NewPrefixMac: Sized {
fn new_prefix_mac(key: &IndexKey, prefix: Vec<u8>) -> Self;
}
impl<'p, M> UpdatePrefixMac<PathSegment<'p>> for M
where
for<'s> M: PrefixMac + UpdatePrefixMac<&'s str>,
{
fn update(&mut self, value: PathSegment<'p>) {
match value {
PathSegment::Root => self.update("$"),
PathSegment::ArrayWildcardItem => self.update("[*]"),
PathSegment::ArrayItem => self.update("[@]"),
PathSegment::ArrayPositionItem(i) => self.update(&format!("{i}")),
PathSegment::ObjectItem(key) => self.update(&format!(".{key}")),
}
}
}
impl<const N: usize, M> UpdatePrefixMac<TokenizedSelector<N>> for M
where
M: PrefixMac + UpdatePrefixMac<[u8; N]>,
{
fn update(&mut self, value: TokenizedSelector<N>) {
self.update(value.as_bytes());
}
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub(super) struct Blake3PrefixMac {
prefix: Vec<u8>,
hasher: blake3::Hasher,
}
impl Blake3PrefixMac {
pub fn new(key: &IndexKey, prefix: Vec<u8>) -> Self {
let mut hasher = blake3::Hasher::new_keyed(key.key());
hasher.update(&prefix);
Self { prefix, hasher }
}
fn reset(&mut self) {
self.hasher.reset();
self.hasher.update(&self.prefix);
}
}
impl PrefixMac for Blake3PrefixMac {
fn finalize_reset<const N: usize>(&mut self) -> [u8; N] {
let mut output = [0; N];
self.hasher.finalize_xof().fill(&mut output);
self.reset();
output
}
}
impl NewPrefixMac for Blake3PrefixMac {
fn new_prefix_mac(key: &IndexKey, prefix: Vec<u8>) -> Self {
Self::new(key, prefix)
}
}
impl UpdatePrefixMac<String> for Blake3PrefixMac {
fn update(&mut self, mut value: String) {
self.hasher.update(value.as_bytes());
value.zeroize();
}
}
impl UpdatePrefixMac<&str> for Blake3PrefixMac {
fn update(&mut self, value: &str) {
self.hasher.update(value.as_bytes());
}
}
impl UpdatePrefixMac<[u8; 16]> for Blake3PrefixMac {
fn update(&mut self, mut value: [u8; 16]) {
self.hasher.update(&value);
value.zeroize();
}
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub(super) struct HmacSha256PrefixMac {
prefix: Vec<u8>,
#[zeroize(skip)]
mac: HmacSha256,
}
impl HmacSha256PrefixMac {
pub fn new(key: &IndexKey, prefix: Vec<u8>) -> Self {
let mut mac = HmacSha256::new_from_slice(key.key()).expect("HMAC accepts any key length");
mac.update(&prefix);
Self { prefix, mac }
}
fn reset(&mut self) {
self.mac.reset();
self.mac.update(&self.prefix);
}
}
impl PrefixMac for HmacSha256PrefixMac {
fn finalize_reset<const N: usize>(&mut self) -> [u8; N] {
const {
assert!(N <= 32, "HmacSha256PrefixMac supports N <= 32 only");
}
let tag = self.mac.finalize_reset().into_bytes();
let mut out = [0u8; N];
out.copy_from_slice(&tag[..N]);
self.reset();
out
}
}
impl NewPrefixMac for HmacSha256PrefixMac {
fn new_prefix_mac(key: &IndexKey, prefix: Vec<u8>) -> Self {
Self::new(key, prefix)
}
}
impl UpdatePrefixMac<String> for HmacSha256PrefixMac {
fn update(&mut self, mut value: String) {
self.mac.update(value.as_bytes());
value.zeroize();
}
}
impl UpdatePrefixMac<&str> for HmacSha256PrefixMac {
fn update(&mut self, value: &str) {
self.mac.update(value.as_bytes());
}
}
impl UpdatePrefixMac<[u8; 16]> for HmacSha256PrefixMac {
fn update(&mut self, mut value: [u8; 16]) {
self.mac.update(&value);
value.zeroize();
}
}
#[cfg(test)]
mod test {
}
#[cfg(test)]
mod new_prefix_mac_tests {
use super::*;
use crate::zerokms::IndexKey;
fn make_with<M: NewPrefixMac>(key: &IndexKey, prefix: Vec<u8>) -> M {
M::new_prefix_mac(key, prefix)
}
#[test]
fn test_blake3_constructs_via_trait() {
let key = IndexKey::from([0u8; 32]);
let mut m: Blake3PrefixMac = make_with(&key, b"p".to_vec());
m.update("x");
let _: [u8; 16] = m.finalize_reset();
}
#[test]
fn test_hmac_constructs_via_trait() {
let key = IndexKey::from([0u8; 32]);
let mut m: HmacSha256PrefixMac = make_with(&key, b"p".to_vec());
m.update("x");
let _: [u8; 16] = m.finalize_reset();
}
}
#[cfg(test)]
mod hmac_tests {
use super::*;
use crate::zerokms::IndexKey;
#[test]
fn test_hmac_finalize_reset_16_truncates_first_16_bytes() {
let key = IndexKey::from([1u8; 32]);
let mut macca = HmacSha256PrefixMac::new(&key, b"prefix".to_vec());
macca.update("hello");
let out_16: [u8; 16] = macca.finalize_reset();
let mut macca2 = HmacSha256PrefixMac::new(&key, b"prefix".to_vec());
macca2.update("hello");
let out_32: [u8; 32] = macca2.finalize_reset();
assert_eq!(&out_32[..16], &out_16[..]);
}
#[test]
fn test_hmac_different_keys_produce_different_output() {
let mut a = HmacSha256PrefixMac::new(&IndexKey::from([1u8; 32]), b"p".to_vec());
a.update("x");
let mut b = HmacSha256PrefixMac::new(&IndexKey::from([2u8; 32]), b"p".to_vec());
b.update("x");
assert_ne!(a.finalize_reset::<32>(), b.finalize_reset::<32>());
}
#[test]
fn test_hmac_different_prefixes_produce_different_output() {
let key = IndexKey::from([1u8; 32]);
let mut a = HmacSha256PrefixMac::new(&key, b"p1".to_vec());
a.update("x");
let mut b = HmacSha256PrefixMac::new(&key, b"p2".to_vec());
b.update("x");
assert_ne!(a.finalize_reset::<32>(), b.finalize_reset::<32>());
}
#[test]
fn test_hmac_reset_after_finalize() {
let key = IndexKey::from([1u8; 32]);
let mut macca = HmacSha256PrefixMac::new(&key, b"p".to_vec());
macca.update("first");
let _first: [u8; 32] = macca.finalize_reset();
macca.update("first");
let second: [u8; 32] = macca.finalize_reset();
let mut fresh = HmacSha256PrefixMac::new(&key, b"p".to_vec());
fresh.update("first");
let fresh_out: [u8; 32] = fresh.finalize_reset();
assert_eq!(second, fresh_out);
}
}