use std::{convert::TryInto, ffi::CString};
use crate::{KcapiError, KcapiResult, BITS_PER_BYTE, INIT_AIO};
const SHA1_BITSIZE: usize = 160;
const SHA224_BITSIZE: usize = 224;
const SHA256_BITSIZE: usize = 256;
const SHA384_BITSIZE: usize = 384;
const SHA512_BITSIZE: usize = 512;
const SM3_BITSIZE: usize = 256;
const SHA3_224_BITSIZE: usize = 224;
const SHA3_256_BITSIZE: usize = 256;
const SHA3_384_BITSIZE: usize = 384;
const SHA3_512_BITSIZE: usize = 512;
pub const SHA1_DIGESTSIZE: usize = SHA1_BITSIZE / BITS_PER_BYTE;
pub const SHA224_DIGESTSIZE: usize = SHA224_BITSIZE / BITS_PER_BYTE;
pub const SHA256_DIGESTSIZE: usize = SHA256_BITSIZE / BITS_PER_BYTE;
pub const SHA384_DIGESTSIZE: usize = SHA384_BITSIZE / BITS_PER_BYTE;
pub const SHA512_DIGESTSIZE: usize = SHA512_BITSIZE / BITS_PER_BYTE;
pub const SM3_DIGESTSIZE: usize = SM3_BITSIZE / BITS_PER_BYTE;
pub const SHA3_224_DIGESTSIZE: usize = SHA3_224_BITSIZE / BITS_PER_BYTE;
pub const SHA3_256_DIGESTSIZE: usize = SHA3_256_BITSIZE / BITS_PER_BYTE;
pub const SHA3_384_DIGESTSIZE: usize = SHA3_384_BITSIZE / BITS_PER_BYTE;
pub const SHA3_512_DIGESTSIZE: usize = SHA3_512_BITSIZE / BITS_PER_BYTE;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KcapiHash {
handle: *mut kcapi_sys::kcapi_handle,
key: Vec<u8>,
pub algorithm: String,
pub blocksize: usize,
pub digestsize: usize,
}
unsafe impl Send for KcapiHash {}
impl KcapiHash {
pub fn new(algorithm: &str) -> KcapiResult<Self> {
let mut handle = Box::into_raw(Box::new(crate::kcapi_handle { _unused: [0u8; 0] }))
as *mut kcapi_sys::kcapi_handle;
let digestsize: usize;
let blocksize: usize;
let alg = CString::new(algorithm).expect("Failed to create CString");
unsafe {
let ret = kcapi_sys::kcapi_md_init(&mut handle as *mut _, alg.as_ptr(), !INIT_AIO);
if ret < 0 {
return Err(KcapiError {
code: ret,
message: format!(
"Failed to initialize hash handle for algorithm '{}'",
algorithm
),
});
}
digestsize = kcapi_sys::kcapi_md_digestsize(handle)
.try_into()
.expect("Failed to convert u32 into usize");
if digestsize == 0 {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!(
"Failed to obtained digest size for algorithm '{}",
algorithm
),
});
}
blocksize = kcapi_sys::kcapi_md_blocksize(handle)
.try_into()
.expect("Failed to convert u32 into usize");
if blocksize == 0 {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!("Failed to obtain block size for algorithm '{}'", algorithm),
});
}
}
let key: Vec<u8> = Vec::new();
Ok(KcapiHash {
handle,
key,
algorithm: algorithm.to_string(),
blocksize,
digestsize,
})
}
pub fn update(&self, buffer: Vec<u8>) -> KcapiResult<()> {
unsafe {
let ret = kcapi_sys::kcapi_md_update(
self.handle,
buffer.as_ptr(),
buffer.len() as kcapi_sys::size_t,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!(
"Failed to update message digest for algorithm '{}'",
self.algorithm
),
});
}
}
Ok(())
}
pub fn finalize(&self) -> KcapiResult<Vec<u8>> {
let mut digest = vec![0u8; self.digestsize];
unsafe {
let ret = kcapi_sys::kcapi_md_final(
self.handle,
digest.as_mut_ptr(),
digest.len() as kcapi_sys::size_t,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!(
"Failed to finalize digest for algorithm '{}'",
self.algorithm,
),
});
}
}
Ok(digest)
}
pub fn setkey(&mut self, key: Vec<u8>) -> KcapiResult<()> {
unsafe {
let ret = kcapi_sys::kcapi_md_setkey(self.handle, key.as_ptr(), key.len() as u32);
if ret < 0 {
return Err(KcapiError {
code: ret,
message: format!("Failed to set key for algorithm '{}'", self.algorithm),
});
}
self.key = key;
}
Ok(())
}
pub fn digest(&self, input: Vec<u8>) -> KcapiResult<Vec<u8>> {
if self.digestsize == 0 {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!(
"Failed to obtain valid blocksize for algorithm '{}'",
self.algorithm
),
});
}
let mut digest = vec![0u8; self.digestsize];
unsafe {
let ret = kcapi_sys::kcapi_md_digest(
self.handle,
input.as_ptr(),
input.len() as kcapi_sys::size_t,
digest.as_mut_ptr(),
self.digestsize as kcapi_sys::size_t,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!("Failed to obtain digest for algorithm '{}'", self.algorithm),
});
}
}
Ok(digest)
}
}
impl Drop for KcapiHash {
fn drop(&mut self) {
unsafe {
kcapi_sys::kcapi_md_destroy(self.handle);
}
}
}
pub fn digest(alg: &str, input: Vec<u8>) -> KcapiResult<Vec<u8>> {
let hash = crate::md::KcapiHash::new(alg)?;
hash.update(input)?;
let output = hash.finalize()?;
Ok(output)
}
pub fn keyed_digest(alg: &str, key: Vec<u8>, input: Vec<u8>) -> KcapiResult<Vec<u8>> {
let mut hmac = crate::md::KcapiHash::new(alg)?;
hmac.setkey(key)?;
hmac.update(input)?;
let output = hmac.finalize()?;
Ok(output)
}
pub fn sha1(input: Vec<u8>) -> KcapiResult<[u8; SHA1_DIGESTSIZE]> {
let mut digest = [0u8; SHA1_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_sha1(
input.as_ptr(),
input.len() as kcapi_sys::size_t,
digest.as_mut_ptr(),
SHA1_DIGESTSIZE as kcapi_sys::size_t,
);
}
if ret != SHA1_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message digest".to_string(),
});
}
Ok(digest)
}
pub fn sha224(input: Vec<u8>) -> KcapiResult<[u8; SHA224_DIGESTSIZE]> {
let mut digest = [0u8; SHA224_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_sha224(
input.as_ptr(),
input.len() as kcapi_sys::size_t,
digest.as_mut_ptr(),
SHA224_DIGESTSIZE as kcapi_sys::size_t,
);
}
if ret != SHA224_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message digest".to_string(),
});
}
Ok(digest)
}
pub fn sha256(input: Vec<u8>) -> KcapiResult<[u8; SHA256_DIGESTSIZE]> {
let mut digest = [0u8; SHA256_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_sha256(
input.as_ptr(),
input.len() as kcapi_sys::size_t,
digest.as_mut_ptr(),
SHA256_DIGESTSIZE as kcapi_sys::size_t,
);
}
if ret != SHA256_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message digest".to_string(),
});
}
Ok(digest)
}
pub fn sha384(input: Vec<u8>) -> KcapiResult<[u8; SHA384_DIGESTSIZE]> {
let mut digest = [0u8; SHA384_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_sha384(
input.as_ptr(),
input.len() as kcapi_sys::size_t,
digest.as_mut_ptr(),
SHA384_DIGESTSIZE as kcapi_sys::size_t,
);
}
if ret != SHA384_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message digest".to_string(),
});
}
Ok(digest)
}
pub fn sha512(input: Vec<u8>) -> KcapiResult<[u8; SHA512_DIGESTSIZE]> {
let mut digest = [0u8; SHA512_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_sha512(
input.as_ptr(),
input.len() as kcapi_sys::size_t,
digest.as_mut_ptr(),
SHA512_DIGESTSIZE as kcapi_sys::size_t,
);
}
if ret != SHA512_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message digest".to_string(),
});
}
Ok(digest)
}
pub fn hmac_sha1(input: Vec<u8>, key: Vec<u8>) -> KcapiResult<[u8; SHA1_DIGESTSIZE]> {
let mut hmac = [0u8; SHA1_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_hmac_sha1(
key.as_ptr(),
key.len() as u32,
input.as_ptr(),
input.len() as kcapi_sys::size_t,
hmac.as_mut_ptr(),
hmac.len() as kcapi_sys::size_t,
);
}
if ret != SHA1_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message hmac".to_string(),
});
}
Ok(hmac)
}
pub fn hmac_sha224(input: Vec<u8>, key: Vec<u8>) -> KcapiResult<[u8; SHA224_DIGESTSIZE]> {
let mut hmac = [0u8; SHA224_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_hmac_sha224(
key.as_ptr(),
key.len() as u32,
input.as_ptr(),
input.len() as kcapi_sys::size_t,
hmac.as_mut_ptr(),
hmac.len() as kcapi_sys::size_t,
);
}
if ret != SHA224_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message hmac".to_string(),
});
}
Ok(hmac)
}
pub fn hmac_sha256(input: Vec<u8>, key: Vec<u8>) -> KcapiResult<[u8; SHA256_DIGESTSIZE]> {
let mut hmac = [0u8; SHA256_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_hmac_sha256(
key.as_ptr(),
key.len() as u32,
input.as_ptr(),
input.len() as kcapi_sys::size_t,
hmac.as_mut_ptr(),
hmac.len() as kcapi_sys::size_t,
);
}
if ret != SHA256_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message hmac".to_string(),
});
}
Ok(hmac)
}
pub fn hmac_sha384(input: Vec<u8>, key: Vec<u8>) -> KcapiResult<[u8; SHA384_DIGESTSIZE]> {
let mut hmac = [0u8; SHA384_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_hmac_sha384(
key.as_ptr(),
key.len() as u32,
input.as_ptr(),
input.len() as kcapi_sys::size_t,
hmac.as_mut_ptr(),
hmac.len() as kcapi_sys::size_t,
);
}
if ret != SHA384_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message hmac".to_string(),
});
}
Ok(hmac)
}
pub fn hmac_sha512(input: Vec<u8>, key: Vec<u8>) -> KcapiResult<[u8; SHA512_DIGESTSIZE]> {
let mut hmac = [0u8; SHA512_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_hmac_sha512(
key.as_ptr(),
key.len() as u32,
input.as_ptr(),
input.len() as kcapi_sys::size_t,
hmac.as_mut_ptr(),
hmac.len() as kcapi_sys::size_t,
);
}
if ret != SHA512_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message hmac".to_string(),
});
}
Ok(hmac)
}
pub fn sm3(input: Vec<u8>) -> KcapiResult<[u8; SM3_DIGESTSIZE]> {
let mut digest = [0u8; SM3_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_sm3(
input.as_ptr(),
input.len() as kcapi_sys::size_t,
digest.as_mut_ptr(),
SM3_DIGESTSIZE as kcapi_sys::size_t,
);
}
if ret != SM3_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message digest".to_string(),
});
}
Ok(digest)
}
pub fn hmac_sm3(input: Vec<u8>, key: Vec<u8>) -> KcapiResult<[u8; SM3_DIGESTSIZE]> {
let mut hmac = [0u8; SM3_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_hmac_sm3(
key.as_ptr(),
key.len() as u32,
input.as_ptr(),
input.len() as kcapi_sys::size_t,
hmac.as_mut_ptr(),
hmac.len() as kcapi_sys::size_t,
);
}
if ret != SM3_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message hmac".to_string(),
});
}
Ok(hmac)
}
pub fn sha3_224(input: Vec<u8>) -> KcapiResult<[u8; SHA3_224_DIGESTSIZE]> {
let mut digest = [0u8; SHA3_224_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_sha3_224(
input.as_ptr(),
input.len() as kcapi_sys::size_t,
digest.as_mut_ptr(),
SHA3_224_DIGESTSIZE as kcapi_sys::size_t,
);
}
if ret != SHA3_224_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message digest".to_string(),
});
}
Ok(digest)
}
pub fn sha3_256(input: Vec<u8>) -> KcapiResult<[u8; SHA3_256_DIGESTSIZE]> {
let mut digest = [0u8; SHA3_256_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_sha3_256(
input.as_ptr(),
input.len() as kcapi_sys::size_t,
digest.as_mut_ptr(),
SHA3_256_DIGESTSIZE as kcapi_sys::size_t,
);
}
if ret != SHA3_256_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message digest".to_string(),
});
}
Ok(digest)
}
pub fn sha3_384(input: Vec<u8>) -> KcapiResult<[u8; SHA3_384_DIGESTSIZE]> {
let mut digest = [0u8; SHA3_384_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_sha3_384(
input.as_ptr(),
input.len() as kcapi_sys::size_t,
digest.as_mut_ptr(),
SHA3_384_DIGESTSIZE as kcapi_sys::size_t,
);
}
if ret != SHA3_384_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message digest".to_string(),
});
}
Ok(digest)
}
pub fn sha3_512(input: Vec<u8>) -> KcapiResult<[u8; SHA3_512_DIGESTSIZE]> {
let mut digest = [0u8; SHA3_512_DIGESTSIZE];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_md_sha3_512(
input.as_ptr(),
input.len() as kcapi_sys::size_t,
digest.as_mut_ptr(),
SHA3_512_DIGESTSIZE as kcapi_sys::size_t,
);
}
if ret != SHA3_512_DIGESTSIZE as kcapi_sys::ssize_t {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed to generate message digest".to_string(),
});
}
Ok(digest)
}