#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use hacl_sys::*;
#[derive(Debug)]
pub enum Error {
InvalidStateFinished,
ModeUnsupportedForStreaming,
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub enum Algorithm {
Sha1 = Spec_Hash_Definitions_SHA1 as isize,
Sha224 = Spec_Hash_Definitions_SHA2_224 as isize,
Sha256 = Spec_Hash_Definitions_SHA2_256 as isize,
Sha384 = Spec_Hash_Definitions_SHA2_384 as isize,
Sha512 = Spec_Hash_Definitions_SHA2_512 as isize,
Blake2s = Spec_Hash_Definitions_Blake2S as isize,
Blake2b = Spec_Hash_Definitions_Blake2B as isize,
Sha3_256 = Spec_Hash_Definitions_SHA3_256 as isize,
Sha3_224 = 9,
Sha3_384 = 10,
Sha3_512 = 11,
}
#[allow(non_upper_case_globals)]
impl From<u32> for Algorithm {
fn from(v: u32) -> Algorithm {
match v {
Spec_Hash_Definitions_SHA1 => Algorithm::Sha1,
Spec_Hash_Definitions_SHA2_224 => Algorithm::Sha224,
Spec_Hash_Definitions_SHA2_256 => Algorithm::Sha256,
Spec_Hash_Definitions_SHA2_384 => Algorithm::Sha384,
Spec_Hash_Definitions_SHA2_512 => Algorithm::Sha512,
Spec_Hash_Definitions_Blake2S => Algorithm::Blake2s,
Spec_Hash_Definitions_Blake2B => Algorithm::Blake2b,
Spec_Hash_Definitions_SHA3_256 => Algorithm::Sha3_256,
9 => Algorithm::Sha3_224,
10 => Algorithm::Sha3_384,
11 => Algorithm::Sha3_512,
_ => panic!("Unknown Digest mode {}", v),
}
}
}
impl From<Algorithm> for Spec_Hash_Definitions_hash_alg {
fn from(v: Algorithm) -> Spec_Hash_Definitions_hash_alg {
match v {
Algorithm::Sha1 => Spec_Hash_Definitions_SHA1 as Spec_Hash_Definitions_hash_alg,
Algorithm::Sha224 => Spec_Hash_Definitions_SHA2_224 as Spec_Hash_Definitions_hash_alg,
Algorithm::Sha256 => Spec_Hash_Definitions_SHA2_256 as Spec_Hash_Definitions_hash_alg,
Algorithm::Sha384 => Spec_Hash_Definitions_SHA2_384 as Spec_Hash_Definitions_hash_alg,
Algorithm::Sha512 => Spec_Hash_Definitions_SHA2_512 as Spec_Hash_Definitions_hash_alg,
Algorithm::Blake2s => Spec_Hash_Definitions_Blake2S as Spec_Hash_Definitions_hash_alg,
Algorithm::Blake2b => Spec_Hash_Definitions_Blake2B as Spec_Hash_Definitions_hash_alg,
Algorithm::Sha3_256 => Spec_Hash_Definitions_SHA3_256 as Spec_Hash_Definitions_hash_alg,
Algorithm::Sha3_224 => 9,
Algorithm::Sha3_384 => 10,
Algorithm::Sha3_512 => 11,
}
}
}
pub const fn digest_size(mode: Algorithm) -> usize {
match mode {
Algorithm::Sha1 => 20,
Algorithm::Sha224 => 28,
Algorithm::Sha256 => 32,
Algorithm::Sha384 => 48,
Algorithm::Sha512 => 64,
Algorithm::Blake2s => 32,
Algorithm::Blake2b => 64,
Algorithm::Sha3_224 => 28,
Algorithm::Sha3_256 => 32,
Algorithm::Sha3_384 => 48,
Algorithm::Sha3_512 => 64,
}
}
impl Algorithm {
pub fn size(self) -> usize {
digest_size(self)
}
}
const fn does_not_support_streaming(alg: Algorithm) -> bool {
matches!(
alg,
Algorithm::Sha3_224 | Algorithm::Sha3_384 | Algorithm::Sha3_512
)
}
pub struct Digest {
mode: Algorithm,
finished: bool,
c_state: *mut EverCrypt_Hash_Incremental_hash_state,
}
impl Digest {
pub fn new(alg: Algorithm) -> Result<Self, Error> {
if does_not_support_streaming(alg) {
return Err(Error::ModeUnsupportedForStreaming);
}
unsafe {
EverCrypt_AutoConfig2_init();
}
let c_state: *mut EverCrypt_Hash_Incremental_hash_state =
unsafe { EverCrypt_Hash_Incremental_create_in(alg.into()) };
Ok(Self {
mode: alg,
finished: false,
c_state,
})
}
pub fn update(&mut self, data: &[u8]) -> Result<(), Error> {
if self.finished {
return Err(Error::InvalidStateFinished);
}
unsafe {
EverCrypt_Hash_Incremental_update(self.c_state, data.as_ptr() as _, data.len() as u32);
}
Ok(())
}
pub fn finish(&mut self) -> Result<Vec<u8>, Error> {
if self.finished {
return Err(Error::InvalidStateFinished);
}
let mut out = vec![0u8; digest_size(self.mode)];
unsafe {
EverCrypt_Hash_Incremental_finish(self.c_state, out.as_mut_ptr());
}
self.finished = true;
Ok(out)
}
}
impl Drop for Digest {
fn drop(&mut self) {
unsafe {
EverCrypt_Hash_Incremental_free(self.c_state);
}
}
}
macro_rules! define_plain_digest {
($name:ident, $version:expr, $l:literal) => {
pub fn $name(data: &[u8]) -> [u8; $l] {
let mut out = [0u8; $l];
unsafe {
EverCrypt_AutoConfig2_init();
}
match $version {
Algorithm::Sha3_224 => unsafe {
Hacl_SHA3_sha3_224(data.len() as u32, data.as_ptr() as _, out.as_mut_ptr())
},
Algorithm::Sha3_256 => unsafe {
Hacl_SHA3_sha3_256(data.len() as u32, data.as_ptr() as _, out.as_mut_ptr())
},
Algorithm::Sha3_384 => unsafe {
Hacl_SHA3_sha3_384(data.len() as u32, data.as_ptr() as _, out.as_mut_ptr())
},
Algorithm::Sha3_512 => unsafe {
Hacl_SHA3_sha3_512(data.len() as u32, data.as_ptr() as _, out.as_mut_ptr())
},
_ => unsafe {
EverCrypt_Hash_Incremental_hash(
$version.into(),
out.as_mut_ptr(),
data.as_ptr() as _,
data.len() as u32,
);
},
}
out
}
};
}
define_plain_digest!(sha1, Algorithm::Sha1, 20);
define_plain_digest!(sha224, Algorithm::Sha224, 28);
define_plain_digest!(sha256, Algorithm::Sha256, 32);
define_plain_digest!(sha384, Algorithm::Sha384, 48);
define_plain_digest!(sha512, Algorithm::Sha512, 64);
define_plain_digest!(sha3_224, Algorithm::Sha3_224, 28);
define_plain_digest!(sha3_256, Algorithm::Sha3_256, 32);
define_plain_digest!(sha3_384, Algorithm::Sha3_384, 48);
define_plain_digest!(sha3_512, Algorithm::Sha3_512, 64);
define_plain_digest!(blake2s, Algorithm::Blake2s, 32);
define_plain_digest!(blake2b, Algorithm::Blake2b, 64);
pub fn hash(alg: Algorithm, data: &[u8]) -> Vec<u8> {
match alg {
Algorithm::Sha1 => sha1(data).to_vec(),
Algorithm::Sha224 => sha224(data).to_vec(),
Algorithm::Sha256 => sha256(data).to_vec(),
Algorithm::Sha384 => sha384(data).to_vec(),
Algorithm::Sha512 => sha512(data).to_vec(),
Algorithm::Sha3_224 => sha3_224(data).to_vec(),
Algorithm::Sha3_256 => sha3_256(data).to_vec(),
Algorithm::Sha3_384 => sha3_384(data).to_vec(),
Algorithm::Sha3_512 => sha3_512(data).to_vec(),
Algorithm::Blake2s => blake2s(data).to_vec(),
Algorithm::Blake2b => blake2b(data).to_vec(),
}
}
pub fn shake128<const BYTES: usize>(data: &[u8]) -> [u8; BYTES] {
let mut out = [0u8; BYTES];
unsafe {
Hacl_SHA3_shake128_hacl(
data.len() as u32,
data.as_ptr() as _,
BYTES as u32,
out.as_mut_ptr(),
);
}
out
}
pub fn shake256<const BYTES: usize>(data: &[u8]) -> [u8; BYTES] {
let mut out = [0u8; BYTES];
unsafe {
Hacl_SHA3_shake256_hacl(
data.len() as u32,
data.as_ptr() as _,
BYTES as u32,
out.as_mut_ptr(),
);
}
out
}