#![forbid(unsafe_code)]
use core::{
borrow::{Borrow, BorrowMut},
fmt::{self, Debug},
num::NonZeroU16,
ops::{Deref, DerefMut},
};
use generic_array::{ArrayLength, GenericArray, IntoArrayLength};
use subtle::{Choice, ConstantTimeEq};
use typenum::{
generic_const_mappings::Const,
type_operators::{IsGreaterOrEqual, IsLess},
Unsigned, U32, U65536,
};
use crate::AlgId;
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, AlgId)]
pub enum HashId {
#[alg_id(0x0001)]
Sha256,
#[alg_id(0x0002)]
Sha384,
#[alg_id(0x0003)]
Sha512_256,
#[alg_id(0x0004)]
Sha512,
#[alg_id(Other)]
Other(NonZeroU16),
}
pub trait Hash: Clone {
const ID: HashId;
type DigestSize: ArrayLength + IsGreaterOrEqual<U32> + IsLess<U65536> + 'static;
const DIGEST_SIZE: usize = Self::DigestSize::USIZE;
const BLOCK_SIZE: usize;
type Block: Borrow<[u8]> + BorrowMut<[u8]> + Default + Clone;
fn new() -> Self;
fn update(&mut self, data: &[u8]);
fn digest(self) -> Digest<Self::DigestSize>;
fn hash(data: &[u8]) -> Digest<Self::DigestSize>
where
Self: Sized,
{
let mut h = Self::new();
h.update(data);
h.digest()
}
}
#[derive(Clone, Default)]
#[repr(transparent)]
pub struct Digest<N: ArrayLength>(GenericArray<u8, N>);
impl<N: ArrayLength> Digest<N> {
#[inline]
pub const fn new(digest: GenericArray<u8, N>) -> Self {
Self(digest)
}
#[inline]
pub const fn from_array<const U: usize>(digest: [u8; U]) -> Self
where
Const<U>: IntoArrayLength<ArrayLength = N>,
{
Self::new(GenericArray::from_array(digest))
}
#[inline]
#[allow(clippy::len_without_is_empty)]
pub const fn len(&self) -> usize {
N::USIZE
}
#[inline]
pub const fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
}
#[inline]
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.0
}
#[inline]
pub fn into_array(self) -> GenericArray<u8, N> {
self.0
}
}
impl<N: ArrayLength> Copy for Digest<N> where N::ArrayType<u8>: Copy {}
impl<N: ArrayLength> Deref for Digest<N> {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<N: ArrayLength> DerefMut for Digest<N> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<N: ArrayLength> Debug for Digest<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Digest").field(&self.0).finish()
}
}
#[cfg(any(test, feature = "test_util"))]
impl<N: ArrayLength> Eq for Digest<N> {}
#[cfg(any(test, feature = "test_util"))]
impl<N: ArrayLength> PartialEq for Digest<N> {
fn eq(&self, other: &Self) -> bool {
bool::from(ConstantTimeEq::ct_eq(self, other))
}
}
impl<N: ArrayLength> ConstantTimeEq for Digest<N> {
#[inline]
fn ct_eq(&self, other: &Self) -> Choice {
self.as_bytes().ct_eq(other.as_bytes())
}
}
#[derive(Clone)]
pub struct Block<const N: usize>([u8; N]);
impl<const N: usize> Default for Block<N> {
#[inline]
fn default() -> Self {
Self([0u8; N])
}
}
impl<const N: usize> Borrow<[u8]> for Block<N> {
#[inline]
fn borrow(&self) -> &[u8] {
self.0.borrow()
}
}
impl<const N: usize> BorrowMut<[u8]> for Block<N> {
#[inline]
fn borrow_mut(&mut self) -> &mut [u8] {
self.0.borrow_mut()
}
}
pub fn tuple_hash<H, I>(s: I) -> Digest<H::DigestSize>
where
H: Hash,
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
let mut h = H::new();
for v in s {
encode_string(&mut h, v.as_ref())
}
right_encode(&mut h, H::DIGEST_SIZE as u64);
h.digest()
}
#[inline(always)]
fn encode_string<H: Hash>(h: &mut H, s: &[u8]) {
left_encode(h, s.len() as u64);
h.update(s);
}
#[inline(always)]
fn left_encode<H: Hash>(h: &mut H, v: u64) {
let mut b = [0u8; 9];
b[1..].copy_from_slice(&v.to_be_bytes());
let i = b[..8]
.iter()
.enumerate()
.position(|(pos, &n)| pos > 0 && n != 0)
.unwrap_or(8);
b[i.wrapping_sub(1)] = (9_usize.wrapping_sub(i)) as u8;
h.update(&b[i.wrapping_sub(1)..]);
}
#[inline(always)]
fn right_encode<H: Hash>(h: &mut H, v: u64) {
let mut b = [0u8; 9];
b[..8].copy_from_slice(&v.to_be_bytes());
let i = b[..7].iter().position(|&n| n != 0).unwrap_or(7);
b[8] = 8_usize.wrapping_sub(i) as u8;
h.update(&b[i.wrapping_sub(1)..]);
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! test_tuple_hash {
($name:ident, $hash:ty) => {
#[test]
fn $name() {
assert_eq!(
tuple_hash::<$hash, _>(["abc"].iter()),
tuple_hash::<$hash, _>(["abc"])
);
assert_eq!(tuple_hash::<$hash, _>([""]), tuple_hash::<$hash, _>([""]));
assert_ne!(
tuple_hash::<$hash, _>(["a", "b", "c"]),
tuple_hash::<$hash, _>(["abc"])
);
assert_ne!(
tuple_hash::<$hash, _>(["a", ""]),
tuple_hash::<$hash, _>(["a"])
);
}
};
}
macro_rules! tuple_hash_tests {
() => {
use super::*;
test_tuple_hash!(test_sha256, Sha256);
test_tuple_hash!(test_sha384, Sha384);
test_tuple_hash!(test_sha512, Sha512);
};
}
#[cfg(feature = "bearssl")]
mod bearssl {
use crate::bearssl::{Sha256, Sha384, Sha512};
tuple_hash_tests!();
}
mod rust {
use crate::rust::{Sha256, Sha384, Sha512};
tuple_hash_tests!();
}
}