use native_ossl::digest::{DigestAlg, DigestCtx};
use std::ffi::CStr;
#[derive(Debug)]
pub struct Algorithm {
pub(crate) name: &'static CStr,
pub output_len: usize,
pub block_len: usize,
}
pub static SHA256: Algorithm = Algorithm {
name: c"SHA2-256",
output_len: 32,
block_len: 64,
};
pub static SHA384: Algorithm = Algorithm {
name: c"SHA2-384",
output_len: 48,
block_len: 128,
};
pub static SHA512: Algorithm = Algorithm {
name: c"SHA2-512",
output_len: 64,
block_len: 128,
};
pub static SHA1_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm {
name: c"SHA1",
output_len: 20,
block_len: 64,
};
pub struct Digest {
value: [u8; 64],
len: usize,
alg: &'static Algorithm,
}
impl Digest {
#[must_use]
pub fn algorithm(&self) -> &'static Algorithm {
self.alg
}
}
impl AsRef<[u8]> for Digest {
fn as_ref(&self) -> &[u8] {
&self.value[..self.len]
}
}
pub struct Context {
alg: &'static Algorithm,
ctx: DigestCtx,
}
impl Context {
#[must_use]
pub fn new(algorithm: &'static Algorithm) -> Self {
let alg_obj = DigestAlg::fetch(algorithm.name, None)
.unwrap_or_else(|e| panic!("OpenSSL digest algorithm unavailable: {e}"));
let ctx = alg_obj
.new_context()
.unwrap_or_else(|e| panic!("EVP_MD_CTX_new failed: {e}"));
Self {
alg: algorithm,
ctx,
}
}
pub fn update(&mut self, data: &[u8]) {
self.ctx
.update(data)
.unwrap_or_else(|e| panic!("EVP_DigestUpdate failed: {e}"));
}
#[must_use]
pub fn clone_state(&self) -> Self {
Self {
alg: self.alg,
ctx: self
.ctx
.fork()
.unwrap_or_else(|e| panic!("EVP_MD_CTX_copy_ex failed: {e}")),
}
}
#[must_use]
pub fn finish(mut self) -> Digest {
let mut out = [0u8; 64];
let n = self
.ctx
.finish(&mut out)
.unwrap_or_else(|e| panic!("EVP_DigestFinal_ex failed: {e}"));
Digest {
value: out,
len: n,
alg: self.alg,
}
}
#[must_use]
pub fn algorithm(&self) -> &'static Algorithm {
self.alg
}
}
impl Clone for Context {
fn clone(&self) -> Self {
self.clone_state()
}
}
#[must_use]
pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest {
let mut ctx = Context::new(algorithm);
ctx.update(data);
ctx.finish()
}