use core::ffi::{c_int, c_void};
use core::mem::MaybeUninit;
extern "C" {
fn OPENSSL_cleanse(ptr: *mut c_void, len: usize);
}
pub(crate) struct Sha512Context {
ctx: openssl_sys::SHA512_CTX,
}
impl Sha512Context {
pub(crate) fn new() -> Self {
let mut ctx = MaybeUninit::uninit();
unsafe {
openssl_sys::SHA512_Init(ctx.as_mut_ptr());
Self {
ctx: ctx.assume_init(),
}
}
}
pub(crate) fn update(&mut self, data: &[u8]) {
unsafe {
openssl_sys::SHA512_Update(&mut self.ctx, data.as_ptr().cast::<c_void>(), data.len());
}
}
pub(crate) fn finish(mut self) -> [u8; 64] {
let mut result = [0u8; 64];
unsafe {
openssl_sys::SHA512_Final(result.as_mut_ptr(), &mut self.ctx);
}
result
}
}
impl Drop for Sha512Context {
fn drop(&mut self) {
unsafe {
OPENSSL_cleanse(
(&mut self.ctx as *mut openssl_sys::SHA512_CTX).cast::<c_void>(),
core::mem::size_of::<openssl_sys::SHA512_CTX>(),
);
}
}
}
pub(crate) fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
unsafe {
openssl_sys::CRYPTO_memcmp(
a.as_ptr().cast::<c_void>(),
b.as_ptr().cast::<c_void>(),
a.len(),
) == 0
}
}
pub(crate) fn random_bytes(buf: &mut [u8]) {
let len: c_int = buf
.len()
.try_into()
.expect("crypt-sha512: RNG request larger than c_int::MAX");
let rc = unsafe { openssl_sys::RAND_bytes(buf.as_mut_ptr(), len) };
assert_eq!(rc, 1, "crypt-sha512: OpenSSL RAND_bytes failed");
}
pub(crate) fn secure_zero_bytes(data: &mut [u8]) {
if !data.is_empty() {
unsafe {
OPENSSL_cleanse(data.as_mut_ptr().cast::<c_void>(), data.len());
}
}
}