use std::{ffi::c_void, ptr::null_mut};
use secapi_sys as ffi;
use crate::{convert_result, key::Key, DigestAlgorithm, ErrorStatus, FfiParameters};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MacAlgorithm {
CMac,
HMac,
}
impl From<MacAlgorithm> for ffi::sa_mac_algorithm {
fn from(value: MacAlgorithm) -> Self {
match value {
MacAlgorithm::CMac => ffi::sa_mac_algorithm::SA_MAC_ALGORITHM_CMAC,
MacAlgorithm::HMac => ffi::sa_mac_algorithm::SA_MAC_ALGORITHM_HMAC,
}
}
}
impl From<ffi::sa_mac_algorithm> for MacAlgorithm {
fn from(value: ffi::sa_mac_algorithm) -> Self {
match value {
ffi::sa_mac_algorithm::SA_MAC_ALGORITHM_CMAC => Self::CMac,
ffi::sa_mac_algorithm::SA_MAC_ALGORITHM_HMAC => Self::HMac,
_ => panic!("invalid sa_mac_algorithm: {value:?}"),
}
}
}
pub enum MacInitParameters {
CMac,
HMac { digest_algorithm: DigestAlgorithm },
}
impl From<&MacInitParameters> for ffi::sa_mac_algorithm {
fn from(value: &MacInitParameters) -> Self {
match value {
MacInitParameters::CMac => Self::SA_MAC_ALGORITHM_CMAC,
MacInitParameters::HMac { .. } => Self::SA_MAC_ALGORITHM_HMAC,
}
}
}
impl MacInitParameters {
fn into_ffi_parameters(self) -> MacInitFfiParameters {
match self {
Self::CMac => MacInitFfiParameters::CMac,
Self::HMac { digest_algorithm } => MacInitFfiParameters::HMac {
params: ffi::sa_mac_parameters_hmac {
digest_algorithm: digest_algorithm.into(),
},
},
}
}
}
enum MacInitFfiParameters {
CMac,
HMac { params: ffi::sa_mac_parameters_hmac },
}
impl FfiParameters for MacInitFfiParameters {
fn ffi_ptr(&mut self) -> *mut c_void {
match self {
Self::CMac => null_mut(),
Self::HMac { params, .. } => params as *mut _ as *mut c_void,
}
}
}
pub struct MacContext<'a> {
pub(crate) context_handle: ffi::sa_crypto_mac_context,
_key: &'a Key,
}
impl<'a> MacContext<'a> {
pub fn init(mac_params: MacInitParameters, key: &'a Key) -> Result<Self, ErrorStatus> {
let mut context_handle: ffi::sa_crypto_mac_context = ffi::INVALID_HANDLE;
let mac_algorithm = (&mac_params).into();
let mut ffi_params = mac_params.into_ffi_parameters();
convert_result(unsafe {
ffi::sa_crypto_mac_init(
&mut context_handle as *mut _,
mac_algorithm,
key.key_handle,
ffi_params.ffi_ptr(),
)
})?;
Ok(MacContext {
context_handle,
_key: key,
})
}
pub fn process_bytes(&mut self, bytes: &[u8]) -> Result<(), ErrorStatus> {
let bytes_ptr = bytes.as_ptr();
let bytes_len = bytes.len();
convert_result(unsafe {
ffi::sa_crypto_mac_process(self.context_handle, bytes_ptr as *const _, bytes_len)
})?;
Ok(())
}
pub fn process_key(&mut self, key: &Key) -> Result<(), ErrorStatus> {
convert_result(unsafe {
ffi::sa_crypto_mac_process_key(self.context_handle, key.key_handle)
})?;
Ok(())
}
pub fn compute(&self) -> Result<Vec<u8>, ErrorStatus> {
let mut out_length = 0;
convert_result(unsafe {
ffi::sa_crypto_mac_compute(null_mut(), &mut out_length as *mut _, self.context_handle)
})?;
let mut mac_bytes = vec![0u8; out_length];
convert_result(unsafe {
ffi::sa_crypto_mac_compute(
mac_bytes.as_mut_ptr() as *mut _,
&mut out_length as *mut _,
self.context_handle,
)
})?;
Ok(mac_bytes)
}
}
impl Drop for MacContext<'_> {
fn drop(&mut self) {
let Self { context_handle, .. } = self;
let _ = unsafe { ffi::sa_crypto_mac_release(*context_handle) };
}
}
pub fn fill_random_bytes(bytes: &mut [u8]) -> Result<(), ErrorStatus> {
convert_result(unsafe {
ffi::sa_crypto_random(bytes.as_mut_ptr() as *mut c_void, bytes.len())
})?;
Ok(())
}
pub fn random_bytes<const N: usize>() -> Result<[u8; N], ErrorStatus> {
let mut bytes = [0u8; N];
convert_result(unsafe {
ffi::sa_crypto_random(bytes.as_mut_ptr() as *mut c_void, bytes.len())
})?;
Ok(bytes)
}
pub fn random_bytes_vec(len: usize) -> Result<Vec<u8>, ErrorStatus> {
let mut bytes = vec![0u8; len];
convert_result(unsafe {
ffi::sa_crypto_random(bytes.as_mut_ptr() as *mut c_void, bytes.len())
})?;
Ok(bytes)
}
#[cfg(test)]
mod test {
use crate::{crypto, ErrorStatus};
#[test]
fn test_fill_random_bytes() -> Result<(), ErrorStatus> {
let mut bytes = [0u8; 10];
crypto::fill_random_bytes(&mut bytes)?;
Ok(())
}
#[test]
fn test_fill_random_bytes_zero_len() -> Result<(), ErrorStatus> {
let mut bytes = [0u8; 0];
let random_result = crypto::fill_random_bytes(&mut bytes);
assert_eq!(random_result, Err(ErrorStatus::NullParameter));
Ok(())
}
#[test]
fn test_random_bytes() -> Result<(), ErrorStatus> {
let _ = crypto::random_bytes::<128>();
Ok(())
}
#[test]
fn test_random_bytes_zero_len() -> Result<(), ErrorStatus> {
let random_result = crypto::random_bytes::<0>();
assert_eq!(random_result, Err(ErrorStatus::NullParameter));
Ok(())
}
}