use crate::hacl::{
blake2,
sha2::{
self,
streaming::{Sha224, Sha256, Sha384, Sha512},
},
sha3,
};
use libcrux_platform::{simd128_support, simd256_support};
#[derive(Debug)]
pub enum Error {
InvalidStateFinished,
ModeUnsupportedForStreaming,
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u32)]
pub enum Algorithm {
Sha1 = 1,
Sha224 = 2,
Sha256 = 3,
Sha384 = 4,
Sha512 = 5,
Blake2s = 6,
Blake2b = 7,
Sha3_224 = 8,
Sha3_256 = 9,
Sha3_384 = 10,
Sha3_512 = 11,
}
impl From<u32> for Algorithm {
fn from(v: u32) -> Algorithm {
match v {
1 => Algorithm::Sha1,
2 => Algorithm::Sha224,
3 => Algorithm::Sha256,
4 => Algorithm::Sha384,
5 => Algorithm::Sha512,
6 => Algorithm::Blake2s,
7 => Algorithm::Blake2b,
8 => Algorithm::Sha3_224,
9 => Algorithm::Sha3_256,
10 => Algorithm::Sha3_384,
11 => Algorithm::Sha3_512,
_ => panic!("Unknown Digest mode {}", v),
}
}
}
impl From<Algorithm> for u32 {
fn from(v: Algorithm) -> u32 {
match v {
Algorithm::Sha1 => 1,
Algorithm::Sha224 => 2,
Algorithm::Sha256 => 3,
Algorithm::Sha384 => 4,
Algorithm::Sha512 => 5,
Algorithm::Blake2s => 6,
Algorithm::Blake2b => 7,
Algorithm::Sha3_224 => 8,
Algorithm::Sha3_256 => 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)
}
}
pub type Sha2_224Digest = [u8; digest_size(Algorithm::Sha224)];
pub type Sha2_256Digest = [u8; digest_size(Algorithm::Sha256)];
pub type Sha2_384Digest = [u8; digest_size(Algorithm::Sha384)];
pub type Sha2_512Digest = [u8; digest_size(Algorithm::Sha512)];
pub type Sha3_224Digest = [u8; digest_size(Algorithm::Sha3_224)];
pub type Sha3_256Digest = [u8; digest_size(Algorithm::Sha3_256)];
pub type Sha3_384Digest = [u8; digest_size(Algorithm::Sha3_384)];
pub type Sha3_512Digest = [u8; digest_size(Algorithm::Sha3_512)];
macro_rules! sha3_impl {
($fun_name:ident, $output:ty, $jasmin_fun:expr, $hacl_fun:expr) => {
#[cfg(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
pub fn $fun_name(payload: &[u8]) -> $output {
$jasmin_fun(payload)
}
#[cfg(not(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos"))))]
pub fn $fun_name(payload: &[u8]) -> $output {
$hacl_fun(payload)
}
};
}
sha3_impl!(
sha3_224,
Sha3_224Digest,
crate::jasmin::sha3::sha3_224,
sha3::sha224
);
sha3_impl!(
sha3_256,
Sha3_256Digest,
crate::jasmin::sha3::sha3_256,
sha3::sha256
);
sha3_impl!(
sha3_384,
Sha3_384Digest,
crate::jasmin::sha3::sha3_384,
sha3::sha384
);
sha3_impl!(
sha3_512,
Sha3_512Digest,
crate::jasmin::sha3::sha3_512,
sha3::sha512
);
pub fn hash(alg: Algorithm, payload: &[u8]) -> Vec<u8> {
match alg {
Algorithm::Sha1 => todo!(),
Algorithm::Sha224 => sha2::sha224(payload).into(),
Algorithm::Sha256 => sha2::sha256(payload).into(),
Algorithm::Sha384 => sha2::sha384(payload).into(),
Algorithm::Sha512 => sha2::sha512(payload).into(),
Algorithm::Blake2s => blake2s(payload, &[]),
Algorithm::Blake2b => blake2b(payload, &[]),
Algorithm::Sha3_224 => sha3_224(payload).into(),
Algorithm::Sha3_256 => sha3_256(payload).into(),
Algorithm::Sha3_384 => sha3_384(payload).into(),
Algorithm::Sha3_512 => sha3_512(payload).into(),
}
}
#[cfg(simd128)]
fn blake2s_128<const LEN: usize>(payload: &[u8], key: &[u8]) -> [u8; LEN] {
blake2::simd128::blake2s(payload, key)
}
#[cfg(not(simd128))]
fn blake2s_128<const LEN: usize>(payload: &[u8], key: &[u8]) -> [u8; LEN] {
blake2::blake2s::<LEN>(payload, key)
}
fn blake2s(payload: &[u8], key: &[u8]) -> Vec<u8> {
const DIGEST_LEN: usize = digest_size(Algorithm::Blake2s);
if simd128_support() {
blake2s_128::<DIGEST_LEN>(payload, key)
} else {
blake2::blake2s::<DIGEST_LEN>(payload, key)
}
.into()
}
#[cfg(simd256)]
fn blake2b_256<const LEN: usize>(payload: &[u8], key: &[u8]) -> [u8; LEN] {
blake2::simd256::blake2b(payload, key)
}
#[cfg(not(simd256))]
fn blake2b_256<const LEN: usize>(payload: &[u8], key: &[u8]) -> [u8; LEN] {
blake2::blake2b::<LEN>(payload, key)
}
fn blake2b(payload: &[u8], key: &[u8]) -> Vec<u8> {
const DIGEST_LEN: usize = digest_size(Algorithm::Blake2b);
if simd256_support() {
blake2b_256::<DIGEST_LEN>(payload, key)
} else {
blake2::blake2b::<DIGEST_LEN>(payload, key)
}
.into()
}
pub fn sha2_224(payload: &[u8]) -> Sha2_224Digest {
sha2::sha224(payload)
}
pub fn sha2_256(payload: &[u8]) -> Sha2_256Digest {
sha2::sha256(payload)
}
pub fn sha2_384(payload: &[u8]) -> Sha2_384Digest {
sha2::sha384(payload)
}
pub fn sha2_512(payload: &[u8]) -> Sha2_512Digest {
sha2::sha512(payload)
}
macro_rules! impl_streaming {
($name:ident, $state:ty, $result:ty) => {
pub struct $name {
state: $state,
}
impl $name {
pub fn new() -> Self {
Self {
state: <$state>::new(),
}
}
pub fn update(&mut self, payload: &[u8]) {
self.state.update(payload);
}
pub fn finish(&mut self) -> $result {
self.state.finish()
}
}
impl Default for $name {
fn default() -> Self {
Self::new()
}
}
};
}
impl_streaming!(Sha2_224, Sha224, Sha2_224Digest);
impl_streaming!(Sha2_256, Sha256, Sha2_256Digest);
impl_streaming!(Sha2_384, Sha384, Sha2_384Digest);
impl_streaming!(Sha2_512, Sha512, Sha2_512Digest);
pub fn shake128<const LEN: usize>(data: &[u8]) -> [u8; LEN] {
sha3::shake128(data)
}
pub fn shake256<const LEN: usize>(data: &[u8]) -> [u8; LEN] {
sha3::shake256(data)
}