use sha2::{Digest, Sha256};
pub const DIGEST_LENGTH: usize = 32;
pub struct HasherContext {
hasher: Sha256,
}
#[no_mangle]
pub extern "C" fn hasher_init() -> *mut HasherContext {
Box::into_raw(Box::new(HasherContext {
hasher: Sha256::new(),
}))
}
#[no_mangle]
pub unsafe extern "C" fn hasher_update(ctx: *mut HasherContext, data: *const u8, len: usize) {
if ctx.is_null() || data.is_null() {
return;
}
let context = unsafe { &mut *ctx };
let slice = unsafe { std::slice::from_raw_parts(data, len) };
context.hasher.update(slice);
}
#[no_mangle]
pub unsafe extern "C" fn hasher_final(ctx: *mut HasherContext, out: *mut u8) {
if ctx.is_null() || out.is_null() {
return;
}
let context = unsafe { Box::from_raw(ctx) };
let digest = context.hasher.finalize();
unsafe {
std::ptr::copy_nonoverlapping(digest.as_ptr(), out, digest.len());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hashes_known_vector() {
let ctx = hasher_init();
let data = b"hello";
let mut out = [0u8; DIGEST_LENGTH];
unsafe {
hasher_update(ctx, data.as_ptr(), data.len());
hasher_final(ctx, out.as_mut_ptr());
}
let expected =
hex::decode("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824")
.unwrap();
assert_eq!(out.to_vec(), expected);
}
#[test]
fn hashes_across_multiple_updates() {
let ctx = hasher_init();
let mut out = [0u8; DIGEST_LENGTH];
unsafe {
hasher_update(ctx, b"hel".as_ptr(), 3);
hasher_update(ctx, b"lo".as_ptr(), 2);
hasher_final(ctx, out.as_mut_ptr());
}
let expected =
hex::decode("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824")
.unwrap();
assert_eq!(out.to_vec(), expected);
}
}