use crate::error::Unspecified;
use native_ossl::digest::DigestAlg;
use native_ossl::mac::HmacCtx;
use native_ossl::util::SecretBuf;
#[derive(Debug, Clone, Copy)]
pub struct Algorithm(pub(crate) &'static crate::digest::Algorithm);
pub static HMAC_SHA256: Algorithm = Algorithm(&crate::digest::SHA256);
pub static HMAC_SHA384: Algorithm = Algorithm(&crate::digest::SHA384);
pub static HMAC_SHA512: Algorithm = Algorithm(&crate::digest::SHA512);
pub struct Key {
alg: Algorithm,
key_bytes: SecretBuf,
}
impl std::fmt::Debug for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Key")
.field("alg", &self.alg)
.finish_non_exhaustive()
}
}
impl Key {
#[must_use]
pub fn new(algorithm: Algorithm, key_value: &[u8]) -> Self {
Self {
alg: algorithm,
key_bytes: SecretBuf::from_slice(key_value),
}
}
#[must_use]
pub fn algorithm(&self) -> Algorithm {
self.alg
}
}
#[derive(Debug)]
pub struct Tag {
bytes: [u8; 64],
len: usize,
}
impl AsRef<[u8]> for Tag {
fn as_ref(&self) -> &[u8] {
&self.bytes[..self.len]
}
}
#[must_use]
pub fn sign(key: &Key, data: &[u8]) -> Tag {
let digest_alg = DigestAlg::fetch(key.alg.0.name, None)
.unwrap_or_else(|e| panic!("OpenSSL digest unavailable: {e}"));
let mut bytes = [0u8; 64];
let len = HmacCtx::oneshot(&digest_alg, key.key_bytes.as_ref(), data, &mut bytes)
.unwrap_or_else(|e| panic!("HmacCtx::oneshot failed: {e}"));
Tag { bytes, len }
}
pub fn verify(key: &Key, data: &[u8], tag: &[u8]) -> Result<(), Unspecified> {
let computed = sign(key, data);
if native_ossl::util::ct_eq(computed.as_ref(), tag) {
Ok(())
} else {
Err(Unspecified)
}
}