use wolfhsm_sys::wolfhsm_cmac;
use crate::client::Client;
use crate::error::Error;
use crate::key::{with_key, KeyId};
pub struct CmacKey {
pub(crate) id: KeyId,
}
impl CmacKey {
pub(crate) fn cache(client: &mut Client, key_bytes: &[u8]) -> Result<Self, Error> {
if !matches!(key_bytes.len(), 16 | 24 | 32) {
return Err(Error::BadArgs {
msg: "key must be 16, 24, or 32 bytes",
});
}
let id = client.key_cache(key_bytes, b"cmac")?;
Ok(CmacKey { id })
}
pub fn compute(&self, client: &mut Client, data: &[u8]) -> Result<[u8; 16], Error> {
let in_len = u32::try_from(data.len()).map_err(|_| Error::BadArgs {
msg: "data exceeds u32::MAX bytes",
})?;
let mut out = [0u8; 16];
let mut out_len: u32 = 16;
let rc = unsafe {
wolfhsm_cmac(
client.ctx_ptr(),
self.id.0,
data.as_ptr(),
in_len,
out.as_mut_ptr(),
&mut out_len,
)
};
Error::check(rc, "wolfhsm_cmac")?;
if out_len != 16 {
return Err(Error::ProtocolError {
msg: "wolfhsm_cmac: unexpected output length",
});
}
Ok(out)
}
}
impl Drop for CmacKey {
fn drop(&mut self) {
if self.id != KeyId::ERASED {
log::warn!(
"wolfhsm: CmacKey (id={}) dropped without eviction — \
HSM cache slot leaked. Use with_cmac_key().",
self.id.0
);
}
}
}
impl Client {
pub fn with_cmac_key<F, R>(&mut self, key_bytes: &[u8], f: F) -> Result<R, Error>
where
F: FnOnce(&CmacKey, &mut Client) -> Result<R, Error>,
{
let key = CmacKey::cache(self, key_bytes)?;
with_key!(key, self, f)
}
}