use crate::error::{HsmError, HsmResult};
use crate::pkcs11_abi::constants::*;
use crate::pkcs11_abi::types::CK_MECHANISM_TYPE;
use sha2::{Digest, Sha256, Sha384, Sha512};
#[allow(dead_code)]
const HASHER_ZEROIZE_BUF_SIZE: usize = 256;
pub fn compute_digest(mechanism: CK_MECHANISM_TYPE, data: &[u8]) -> HsmResult<Vec<u8>> {
match mechanism {
CKM_SHA_1 => {
tracing::warn!(
"SHA-1 digest requested — SHA-1 is cryptographically broken for \
collision resistance. Migrate to SHA-256 or stronger."
);
use sha1::Sha1;
Ok(Sha1::digest(data).to_vec())
}
CKM_SHA256 => Ok(Sha256::digest(data).to_vec()),
CKM_SHA384 => Ok(Sha384::digest(data).to_vec()),
CKM_SHA512 => Ok(Sha512::digest(data).to_vec()),
CKM_SHA3_256 => {
use sha3::Sha3_256;
Ok(Sha3_256::digest(data).to_vec())
}
CKM_SHA3_384 => {
use sha3::Sha3_384;
Ok(Sha3_384::digest(data).to_vec())
}
CKM_SHA3_512 => {
use sha3::Sha3_512;
Ok(Sha3_512::digest(data).to_vec())
}
_ => Err(HsmError::MechanismInvalid),
}
}
pub fn digest_output_len(mechanism: CK_MECHANISM_TYPE) -> HsmResult<usize> {
match mechanism {
CKM_SHA_1 => Ok(20),
CKM_SHA256 | CKM_SHA3_256 => Ok(32),
CKM_SHA384 | CKM_SHA3_384 => Ok(48),
CKM_SHA512 | CKM_SHA3_512 => Ok(64),
_ => Err(HsmError::MechanismInvalid),
}
}
pub fn create_hasher(mechanism: CK_MECHANISM_TYPE) -> HsmResult<Box<dyn DigestAccumulator>> {
match mechanism {
CKM_SHA_1 => {
tracing::warn!(
"SHA-1 hasher requested — SHA-1 is cryptographically broken for \
collision resistance. Migrate to SHA-256 or stronger."
);
use sha1::Sha1;
Ok(Box::new(GenericHasher::<Sha1>::new()))
}
CKM_SHA256 => Ok(Box::new(GenericHasher::<Sha256>::new())),
CKM_SHA384 => Ok(Box::new(GenericHasher::<Sha384>::new())),
CKM_SHA512 => Ok(Box::new(GenericHasher::<Sha512>::new())),
CKM_SHA3_256 => {
use sha3::Sha3_256;
Ok(Box::new(GenericHasher::<Sha3_256>::new()))
}
CKM_SHA3_384 => {
use sha3::Sha3_384;
Ok(Box::new(GenericHasher::<Sha3_384>::new()))
}
CKM_SHA3_512 => {
use sha3::Sha3_512;
Ok(Box::new(GenericHasher::<Sha3_512>::new()))
}
_ => Err(HsmError::MechanismInvalid),
}
}
pub trait DigestAccumulator: Send + Sync {
fn update(&mut self, data: &[u8]);
fn finalize(self: Box<Self>) -> Vec<u8>;
fn output_len(&self) -> usize;
}
struct GenericHasher<D: Digest + Send + Sync> {
hasher: Option<D>,
}
impl<D: Digest + Send + Sync> GenericHasher<D> {
fn new() -> Self {
Self {
hasher: Some(D::new()),
}
}
}
impl<D: Digest + Send + Sync + 'static> DigestAccumulator for GenericHasher<D> {
fn update(&mut self, data: &[u8]) {
if let Some(ref mut h) = self.hasher {
h.update(data);
}
}
fn finalize(mut self: Box<Self>) -> Vec<u8> {
self.hasher
.take()
.map(|h| h.finalize().to_vec())
.unwrap_or_default()
}
fn output_len(&self) -> usize {
<D as Digest>::output_size()
}
}
impl<D: Digest + Send + Sync> Drop for GenericHasher<D> {
fn drop(&mut self) {
if let Some(hasher) = self.hasher.take() {
debug_assert!(
std::mem::size_of::<D>() <= 512,
"Hasher type {} exceeds 512 bytes — review for heap allocations before zeroizing",
std::any::type_name::<D>()
);
let mut hasher = std::mem::ManuallyDrop::new(hasher);
let ptr = &mut *hasher as *mut D as *mut u8;
let size = std::mem::size_of::<D>();
if size > 0 {
let slice = unsafe { std::slice::from_raw_parts_mut(ptr, size) };
zeroize::Zeroize::zeroize(slice);
}
}
}
}