use base64::{Engine as _, engine::general_purpose::STANDARD as B64};
use hkdf::Hkdf;
use hmac::{Hmac, Mac};
use sha2::{Sha256, Sha512};
pub fn derive_auth_tag(host_id: &[u8], identifier: &[u8]) -> [u8; 8] {
let hk = Hkdf::<Sha512>::new(None, host_id);
let mut key = [0u8; 32];
hk.expand(&[], &mut key)
.expect("32 is a valid HKDF-SHA512 output length");
let mut mac = <Hmac<Sha256> as Mac>::new_from_slice(&key)
.expect("HMAC-SHA256 accepts keys of any length");
mac.update(identifier);
let tag = mac.finalize().into_bytes();
let mut out = [0u8; 8];
out.copy_from_slice(&tag[..8]);
out
}
pub fn decode_auth_tag(raw: &[u8]) -> Option<[u8; 8]> {
let trimmed = raw
.iter()
.position(|b| !b.is_ascii_whitespace())
.map(|start| {
let end = raw
.iter()
.rposition(|b| !b.is_ascii_whitespace())
.map(|i| i + 1)
.unwrap_or(raw.len());
&raw[start..end]
})
.unwrap_or(&[][..]);
let decoded = B64.decode(trimmed).ok()?;
decoded.as_slice().try_into().ok()
}
pub fn txt_record_matches(host_id: &[u8], identifier: &[u8], auth_tags: &[&[u8]]) -> bool {
let expected = derive_auth_tag(host_id, identifier);
auth_tags
.iter()
.filter_map(|tag| decode_auth_tag(tag))
.any(|tag| tag == expected)
}