crypt-sha512 1.0.0

no_std SHA512-crypt ($6$) password hashing, ported from Ulrich Drepper's reference implementation. Pluggable crypto backend (aws-lc, BoringSSL, OpenSSL, or RustCrypto) selected by feature.
Documentation
//! aws-lc-sys backend.

use core::mem::MaybeUninit;

pub(crate) struct Sha512Context {
    ctx: aws_lc_sys::SHA512_CTX,
}

impl Sha512Context {
    pub(crate) fn new() -> Self {
        let mut ctx = MaybeUninit::uninit();
        // SAFETY: SHA512_Init initializes every field of SHA512_CTX.
        unsafe {
            aws_lc_sys::SHA512_Init(ctx.as_mut_ptr());
            Self {
                ctx: ctx.assume_init(),
            }
        }
    }

    pub(crate) fn update(&mut self, data: &[u8]) {
        // SAFETY: data pointer/length describe a valid readable region.
        unsafe {
            aws_lc_sys::SHA512_Update(
                &mut self.ctx,
                data.as_ptr().cast::<core::ffi::c_void>(),
                data.len(),
            );
        }
    }

    pub(crate) fn finish(mut self) -> [u8; 64] {
        let mut result = [0u8; 64];
        // SAFETY: result is a 64-byte writable buffer matching SHA512_DIGEST_LENGTH.
        unsafe {
            aws_lc_sys::SHA512_Final(result.as_mut_ptr(), &mut self.ctx);
        }
        result
    }
}

impl Drop for Sha512Context {
    fn drop(&mut self) {
        // SAFETY: SHA512_CTX is plain old data; zeroing every byte is sound.
        unsafe {
            aws_lc_sys::OPENSSL_cleanse(
                (&mut self.ctx as *mut aws_lc_sys::SHA512_CTX).cast::<core::ffi::c_void>(),
                core::mem::size_of::<aws_lc_sys::SHA512_CTX>(),
            );
        }
    }
}

pub(crate) fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
    if a.len() != b.len() {
        return false;
    }
    // SAFETY: pointers/length describe valid readable regions of equal size.
    unsafe {
        aws_lc_sys::CRYPTO_memcmp(
            a.as_ptr().cast::<core::ffi::c_void>(),
            b.as_ptr().cast::<core::ffi::c_void>(),
            a.len(),
        ) == 0
    }
}

pub(crate) fn random_bytes(buf: &mut [u8]) {
    // SAFETY: buf is a writable region of the given length. RAND_bytes
    // either fills it or aborts the process per aws-lc semantics.
    unsafe {
        aws_lc_sys::RAND_bytes(buf.as_mut_ptr(), buf.len());
    }
}

pub(crate) fn secure_zero_bytes(data: &mut [u8]) {
    if !data.is_empty() {
        // SAFETY: data points to len writable bytes.
        unsafe {
            aws_lc_sys::OPENSSL_cleanse(data.as_mut_ptr().cast::<core::ffi::c_void>(), data.len());
        }
    }
}