use std::ffi::{c_uint, c_void, CStr};
use crate::bindings::*;
use crate::{cstr, trace_ossl, Error, ErrorKind, OsslContext, OsslParam};
#[derive(Debug)]
pub struct EvpMd {
ptr: *mut EVP_MD,
}
impl EvpMd {
pub fn new(ctx: &OsslContext, name: &CStr) -> Result<EvpMd, Error> {
let ptr = unsafe {
EVP_MD_fetch(ctx.ptr(), name.as_ptr(), std::ptr::null_mut())
};
if ptr.is_null() {
trace_ossl!("EVP_MD_fetch()");
return Err(Error::new(ErrorKind::NullPtr));
}
Ok(EvpMd { ptr })
}
pub unsafe fn as_ptr(&self) -> *const EVP_MD {
self.ptr
}
pub unsafe fn as_mut_ptr(&mut self) -> *mut EVP_MD {
self.ptr
}
}
impl Clone for EvpMd {
fn clone(&self) -> Self {
let ret = unsafe { EVP_MD_up_ref(self.ptr) };
if ret != 1 {
panic!("EVP_MD_up_ref failed");
}
EvpMd { ptr: self.ptr }
}
}
impl Drop for EvpMd {
fn drop(&mut self) {
unsafe {
EVP_MD_free(self.ptr);
}
}
}
unsafe impl Send for EvpMd {}
unsafe impl Sync for EvpMd {}
#[derive(Debug)]
pub struct EvpMdCtx {
ptr: *mut EVP_MD_CTX,
}
impl EvpMdCtx {
pub fn new() -> Result<EvpMdCtx, Error> {
let ptr = unsafe { EVP_MD_CTX_new() };
if ptr.is_null() {
trace_ossl!("EVP_MD_ctx_new()");
return Err(Error::new(ErrorKind::NullPtr));
}
Ok(EvpMdCtx { ptr })
}
pub unsafe fn as_ptr(&self) -> *const EVP_MD_CTX {
self.ptr
}
pub unsafe fn as_mut_ptr(&mut self) -> *mut EVP_MD_CTX {
self.ptr
}
pub fn try_clone(&self) -> Result<EvpMdCtx, Error> {
let mut new = Self::new()?;
let ret =
unsafe { EVP_MD_CTX_copy_ex(new.as_mut_ptr(), self.as_ptr()) };
if ret != 1 {
return Err(Error::new(ErrorKind::OsslError));
}
Ok(new)
}
#[cfg(ossl_v400)]
pub fn serialize(&self, state: Option<&mut [u8]>) -> Result<usize, Error> {
let mut outlen: usize = 0;
let ret = unsafe {
EVP_MD_CTX_serialize(
self.as_ptr() as *mut EVP_MD_CTX,
std::ptr::null_mut(),
&mut outlen,
)
};
if ret != 1 {
trace_ossl!("EVP_MD_CTX_serialize()");
return Err(Error::new(ErrorKind::OsslError));
}
if let Some(out) = state {
if outlen > out.len() {
return Err(Error::new(ErrorKind::BufferSize));
}
let ret = unsafe {
EVP_MD_CTX_serialize(
self.as_ptr() as *mut EVP_MD_CTX,
out.as_mut_ptr(),
&mut outlen,
)
};
if ret != 1 {
trace_ossl!("EVP_MD_CTX_serialize()");
return Err(Error::new(ErrorKind::OsslError));
}
}
Ok(outlen)
}
#[cfg(ossl_v400)]
pub fn deserialize(&mut self, state: &[u8]) -> Result<(), Error> {
let ret = unsafe {
EVP_MD_CTX_deserialize(
self.as_mut_ptr(),
state.as_ptr(),
state.len(),
)
};
if ret != 1 {
trace_ossl!("EVP_MD_CTX_deserialize()");
return Err(Error::new(ErrorKind::OsslError));
}
Ok(())
}
}
impl Drop for EvpMdCtx {
fn drop(&mut self) {
unsafe {
EVP_MD_CTX_free(self.ptr);
}
}
}
unsafe impl Send for EvpMdCtx {}
unsafe impl Sync for EvpMdCtx {}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DigestAlg {
Sha1,
Sha2_224,
Sha2_256,
Sha2_384,
Sha2_512,
Sha2_512_224,
Sha2_512_256,
Sha3_224,
Sha3_256,
Sha3_384,
Sha3_512,
#[cfg(feature = "rfc9580")]
Md5,
}
pub(crate) fn digest_to_string(digest: DigestAlg) -> &'static CStr {
match digest {
DigestAlg::Sha1 => cstr!(OSSL_DIGEST_NAME_SHA1),
DigestAlg::Sha2_224 => cstr!(OSSL_DIGEST_NAME_SHA2_224),
DigestAlg::Sha2_256 => cstr!(OSSL_DIGEST_NAME_SHA2_256),
DigestAlg::Sha2_384 => cstr!(OSSL_DIGEST_NAME_SHA2_384),
DigestAlg::Sha2_512 => cstr!(OSSL_DIGEST_NAME_SHA2_512),
DigestAlg::Sha2_512_224 => cstr!(OSSL_DIGEST_NAME_SHA2_512_224),
DigestAlg::Sha2_512_256 => cstr!(OSSL_DIGEST_NAME_SHA2_512_256),
DigestAlg::Sha3_224 => cstr!(OSSL_DIGEST_NAME_SHA3_224),
DigestAlg::Sha3_256 => cstr!(OSSL_DIGEST_NAME_SHA3_256),
DigestAlg::Sha3_384 => cstr!(OSSL_DIGEST_NAME_SHA3_384),
DigestAlg::Sha3_512 => cstr!(OSSL_DIGEST_NAME_SHA3_512),
#[cfg(feature = "rfc9580")]
DigestAlg::Md5 => cstr!(OSSL_DIGEST_NAME_MD5),
}
}
pub(crate) fn string_to_digest(digest: &CStr) -> Result<DigestAlg, Error> {
match digest.to_bytes() {
b"SHA1" => Ok(DigestAlg::Sha1),
b"SHA2-224" => Ok(DigestAlg::Sha2_224),
b"SHA2-256" => Ok(DigestAlg::Sha2_256),
b"SHA2-384" => Ok(DigestAlg::Sha2_384),
b"SHA2-512" => Ok(DigestAlg::Sha2_512),
b"SHA2-512/224" => Ok(DigestAlg::Sha2_512_224),
b"SHA2-512/256" => Ok(DigestAlg::Sha2_512_256),
b"SHA3-224" => Ok(DigestAlg::Sha3_224),
b"SHA3-256" => Ok(DigestAlg::Sha3_256),
b"SHA3-384" => Ok(DigestAlg::Sha3_384),
b"SHA3-512" => Ok(DigestAlg::Sha3_512),
#[cfg(feature = "rfc9580")]
b"MD5" => Ok(DigestAlg::Md5),
_ => Err(Error::new(ErrorKind::WrapperError)),
}
}
#[derive(Debug)]
pub struct OsslDigest {
ctx: EvpMdCtx,
md: EvpMd,
size: usize,
}
impl OsslDigest {
pub fn new(
ctx: &OsslContext,
digest: DigestAlg,
params: Option<&OsslParam>,
) -> Result<OsslDigest, Error> {
let md = EvpMd::new(ctx, digest_to_string(digest))?;
let size = usize::try_from(unsafe { EVP_MD_get_size(md.as_ptr()) })?;
let mut dctx = OsslDigest {
ctx: EvpMdCtx::new()?,
md: md,
size: size,
};
dctx.reset(params)?;
Ok(dctx)
}
pub fn reset(&mut self, params: Option<&OsslParam>) -> Result<(), Error> {
let ret = unsafe {
EVP_DigestInit_ex2(
self.ctx.as_mut_ptr(),
self.md.as_ptr(),
match params {
Some(p) => p.as_ptr(),
None => std::ptr::null(),
},
)
};
if ret != 1 {
trace_ossl!("EVP_DigestInit_ex2()");
return Err(Error::new(ErrorKind::OsslError));
}
Ok(())
}
pub fn update(&mut self, data: &[u8]) -> Result<(), Error> {
let ret = unsafe {
EVP_DigestUpdate(
self.ctx.as_mut_ptr(),
data.as_ptr() as *const c_void,
data.len(),
)
};
if ret != 1 {
trace_ossl!("EVP_DigestUpdate()");
return Err(Error::new(ErrorKind::OsslError));
}
Ok(())
}
pub fn finalize(&mut self, digest: &mut [u8]) -> Result<usize, Error> {
if digest.len() < self.size {
return Err(Error::new(ErrorKind::BufferSize));
}
let mut retlen = c_uint::try_from(self.size)?;
let ret = unsafe {
EVP_DigestFinal_ex(
self.ctx.as_mut_ptr(),
digest.as_mut_ptr(),
&mut retlen,
)
};
if ret != 1 {
trace_ossl!("EVP_DigestFinal_ex()");
return Err(Error::new(ErrorKind::OsslError));
}
Ok(usize::try_from(retlen)?)
}
pub fn size(&self) -> usize {
self.size
}
pub fn try_clone(&self) -> Result<Self, Error> {
Ok(OsslDigest {
ctx: self.ctx.try_clone()?,
md: self.md.clone(),
size: self.size,
})
}
#[cfg(ossl_v400)]
pub fn get_state_size(&self) -> Result<usize, Error> {
self.ctx.serialize(None)
}
#[cfg(not(ossl_v400))]
pub fn get_state_size(&self) -> Result<usize, Error> {
Err(Error::new(ErrorKind::WrapperError))
}
#[cfg(ossl_v400)]
pub fn get_state(&self, state: &mut [u8]) -> Result<usize, Error> {
self.ctx.serialize(Some(state))
}
#[cfg(not(ossl_v400))]
pub fn get_state(&self, _state: &mut [u8]) -> Result<usize, Error> {
Err(Error::new(ErrorKind::WrapperError))
}
#[cfg(ossl_v400)]
pub fn set_state(&mut self, state: &[u8]) -> Result<(), Error> {
self.ctx.deserialize(state)
}
#[cfg(not(ossl_v400))]
pub fn set_state(&mut self, _state: &[u8]) -> Result<(), Error> {
Err(Error::new(ErrorKind::WrapperError))
}
}
unsafe impl Send for OsslDigest {}
unsafe impl Sync for OsslDigest {}