Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ members = [
"fstar-helpers/core-models",
"test-utils",
"crates/primitives/aead",
"crates/primitives/digest",
]

[workspace.package]
Expand Down
131 changes: 118 additions & 13 deletions blake2/src/impl_digest_trait.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,66 @@
use crate::impl_hacl::*;
use libcrux_traits::digest::{arrayref, slice, DigestIncrementalBase, Hasher, UpdateError};
use libcrux_traits::digest::{
arrayref,
consts::HashConsts,
slice,
typed_owned::{impl_digest_incremental_typed_owned, impl_hash_typed_owned},
typed_refs, DigestIncrementalBase, Hasher, InitializeError, UpdateError,
};

macro_rules! impl_digest_traits {
($out_size:ident, $type:ty, $blake2:ty, $hasher:ty) => {
macro_rules! impl_runtime_digest_traits {
($type:ty, $builder:ty, $max:expr) => {
impl slice::Hash for $type {
fn hash(digest: &mut [u8], payload: &[u8]) -> Result<usize, slice::HashError> {
let digest_len: u8 = digest
.len()
.try_into()
.map_err(|_| slice::HashError::InvalidDigestLength)?;

let mut hasher = <$builder>::new_unkeyed()
.build_var_digest_len(digest_len)
.map_err(|_| slice::HashError::InvalidDigestLength)?;

hasher.update(payload).map_err(|e| match e {
Error::InvalidChunkLength | Error::MaximumLengthExceeded => {
slice::HashError::InvalidPayloadLength
}
_ => slice::HashError::Unknown,
})?;

hasher
.finalize(digest.as_mut())
.map_err(|_| slice::HashError::InvalidDigestLength)
}
}
impl typed_refs::Hash for $type {
fn digest_len_is_valid(&self, len: usize) -> bool {
(1..=$max).contains(&len)
}
fn hash<'a>(
&self,
mut digest: typed_refs::DigestMut<'a, Self>,
payload: &[u8],
) -> Result<(), typed_refs::HashError> {
<$type as slice::Hash>::hash(digest.as_mut(), payload)?;
Ok(())
}
}
};
}

macro_rules! impl_const_digest_traits {
($out_size:ident, $type:ty, $blake2:ty, $hasher:ty, $builder:ty) => {
impl<const $out_size: usize> DigestIncrementalBase for $type {
type IncrementalState = $blake2;

fn new() -> Result<Self::IncrementalState, InitializeError> {
<$builder>::new_unkeyed()
.build_const_digest_len()
.map_err(|e| match e {
Error::InvalidDigestLength => InitializeError::InvalidDigestLength,
_ => InitializeError::Unknown,
})
}
fn update(state: &mut Self::IncrementalState, chunk: &[u8]) -> Result<(), UpdateError> {
// maps all known errors returned by this function
state.update(chunk).map_err(|e| match e {
Expand Down Expand Up @@ -39,39 +94,89 @@ macro_rules! impl_digest_traits {
}
}

impl<const $out_size: usize> arrayref::Hash<$out_size> for $type {
fn hash(
digest: &mut [u8; $out_size],
payload: &[u8],
) -> Result<(), arrayref::HashError> {
// Initialize a new incremental hasher
let mut hasher = <$hasher>::new().map_err(|e| match e {
InitializeError::InvalidDigestLength => {
arrayref::HashError::InvalidDigestLength
}
InitializeError::Unknown => arrayref::HashError::Unknown,
})?;
// Update the hasher with the payload
hasher.update(payload).map_err(|e| match e {
UpdateError::InvalidPayloadLength => arrayref::HashError::InvalidPayloadLength,
UpdateError::MaximumLengthExceeded => arrayref::HashError::InvalidPayloadLength,
UpdateError::Unknown => arrayref::HashError::Unknown,
})?;
// Finalize and write to digest
hasher.finish(digest);

Ok(())
}
}

impl<const $out_size: usize> From<$blake2> for $hasher {
fn from(state: $blake2) -> Self {
Self { state }
}
}

impl<const $out_size: usize> HashConsts for $type {
const DIGEST_SIZE: usize = $out_size;
}
impl_hash_typed_owned!($type, $out_size, generic);
impl_digest_incremental_typed_owned!($type, $out_size, generic);
};
}

#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct ConstDigestLen<const OUT_SIZE: usize>;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct RuntimeDigestLen;

/// A struct that implements [`libcrux_traits::digest`] traits.
///
/// [`Blake2bHasher`] is a convenience hasher for this struct.
pub struct Blake2bHash<const OUT_SIZE: usize>;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Blake2bHash<T> {
_marker: core::marker::PhantomData<T>,
}

impl_digest_traits!(
impl_const_digest_traits!(
OUT_SIZE,
Blake2bHash<OUT_SIZE>,
Blake2bHash<ConstDigestLen<OUT_SIZE>>,
Blake2b<ConstKeyLenConstDigestLen<0, OUT_SIZE>>,
Blake2bHasher<OUT_SIZE>
Blake2bHasher<OUT_SIZE>,
Blake2bBuilder<'_, &_>
);

impl_runtime_digest_traits!(Blake2bHash<RuntimeDigestLen>, Blake2bBuilder<'_, &_>, 64);

/// A hasher for [`Blake2bHash`].
pub type Blake2bHasher<const OUT_SIZE: usize> = Hasher<OUT_SIZE, Blake2bHash<OUT_SIZE>>;
pub type Blake2bHasher<const OUT_SIZE: usize> =
Hasher<OUT_SIZE, Blake2bHash<ConstDigestLen<OUT_SIZE>>>;

/// A struct that implements [`libcrux_traits::digest`] traits.
///
/// [`Blake2sHasher`] is a convenience hasher for this struct.
pub struct Blake2sHash<const OUT_SIZE: usize>;
impl_digest_traits!(
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Blake2sHash<T> {
_marker: core::marker::PhantomData<T>,
}
impl_const_digest_traits!(
OUT_SIZE,
Blake2sHash<OUT_SIZE>,
Blake2sHash<ConstDigestLen<OUT_SIZE>>,
Blake2s<ConstKeyLenConstDigestLen<0, OUT_SIZE>>,
Blake2sHasher<OUT_SIZE>
Blake2sHasher<OUT_SIZE>,
Blake2sBuilder<'_, &_>
);

impl_runtime_digest_traits!(Blake2sHash<RuntimeDigestLen>, Blake2sBuilder<'_, &_>, 32);

/// A hasher for [`Blake2sHash`].
pub type Blake2sHasher<const OUT_SIZE: usize> = Hasher<OUT_SIZE, Blake2sHash<OUT_SIZE>>;
pub type Blake2sHasher<const OUT_SIZE: usize> =
Hasher<OUT_SIZE, Blake2sHash<ConstDigestLen<OUT_SIZE>>>;
4 changes: 3 additions & 1 deletion blake2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ mod impl_hacl;

mod impl_digest_trait;

pub use impl_digest_trait::{Blake2bHash, Blake2bHasher, Blake2sHash, Blake2sHasher};
pub use impl_digest_trait::{
Blake2bHash, Blake2bHasher, Blake2sHash, Blake2sHasher, ConstDigestLen, RuntimeDigestLen,
};
pub use impl_hacl::{Blake2b, Blake2bBuilder, Blake2s, Blake2sBuilder, Error};
32 changes: 23 additions & 9 deletions blake2/tests/blake2.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use libcrux_blake2::{Blake2bBuilder, Blake2bHasher, Blake2sBuilder, Blake2sHasher};
use libcrux_blake2::{
Blake2bBuilder, Blake2bHash, Blake2bHasher, Blake2sBuilder, Blake2sHash, Blake2sHasher,
RuntimeDigestLen,
};

#[test]
fn test_blake2b() {
Expand Down Expand Up @@ -227,14 +230,24 @@ fn test_digest_traits_2s() {

// test unkeyed, with const key and digest len
let expected_hash = b"\xf2\x01\x46\xc0\x54\xf9\xdd\x6b\x67\x64\xb6\xc0\x93\x57\xf7\xcd\x75\x51\xdf\xbc\xba\x54\x59\x72\xa4\xc8\x16\x6d\xf8\xaf\xde\x60";
let mut hasher: Blake2sHasher<_> = Blake2sBuilder::new_unkeyed()
.build_const_digest_len()
.unwrap()
.into();
let mut hasher = Blake2sHasher::new().unwrap();
hasher.update(b"this is a test").unwrap();
hasher.finish(&mut got_hash);

assert_eq!(&got_hash, expected_hash);
// compare to result from oneshot hasher
assert_eq!(
&Blake2sHasher::<32>::hash_to_owned(b"this is a test").unwrap(),
expected_hash
);

// compare to result from varlen hasher
use libcrux_traits::digest::typed_refs::*;
let mut digest = [0; 32];
let algo = Blake2sHash::<RuntimeDigestLen>::default();
let digest_mut = DigestMut::new_for_algo(algo, &mut digest).unwrap();
algo.hash(digest_mut, b"this is a test").unwrap();
Comment on lines +247 to +249
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a lot of code for just calling a hash function.
I'd want to call something like Blake2sHash::hash(digest_mut, b"this is a test"). It's not clear why the extra two lines are necessary.

assert_eq!(&digest, expected_hash);

let mut too_short = vec![0; 31];
let err = hasher.finish_slice(&mut too_short).unwrap_err();
Expand All @@ -256,14 +269,15 @@ fn test_digest_traits_2b() {

// test unkeyed, with const key and digest len
let expected_hash = b"\xe9\xed\x14\x1d\xf1\xce\xbf\xc8\x9e\x46\x6c\xe0\x89\xee\xdd\x4f\x12\x5a\xa7\x57\x15\x01\xa0\xaf\x87\x1f\xab\x60\x59\x71\x17\xb7";
let mut hasher: Blake2bHasher<_> = Blake2bBuilder::new_unkeyed()
.build_const_digest_len()
.unwrap()
.into();
let mut hasher = Blake2bHasher::new().unwrap();
hasher.update(b"this is a test").unwrap();
hasher.finish(&mut got_hash);

assert_eq!(&got_hash, expected_hash);
assert_eq!(
&Blake2bHasher::<32>::hash_to_owned(b"this is a test").unwrap(),
expected_hash
);

let mut too_short = vec![0; 31];
let err = hasher.finish_slice(&mut too_short).unwrap_err();
Expand Down
23 changes: 23 additions & 0 deletions crates/primitives/digest/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
description = "Formally verified digest library"
name = "libcrux-digest"
readme = "Readme.md"
version = "0.0.3"

authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true

[dependencies]
libcrux-blake2 = { version = "0.0.3", path = "../../../blake2", optional = true }
libcrux-sha2 = { version = "0.0.3", path = "../../../sha2", optional = true }
libcrux-sha3 = { version = "0.0.3", path = "../../../libcrux-sha3", optional = true }
libcrux-traits = { version = "0.0.3", path = "../../../traits", optional = true }

[features]
blake2 = ["dep:libcrux-blake2", "dep:libcrux-traits"]
default = ["blake2", "sha2", "sha3"]
sha2 = ["dep:libcrux-sha2", "dep:libcrux-traits"]
sha3 = ["dep:libcrux-sha3", "dep:libcrux-traits"]
31 changes: 31 additions & 0 deletions crates/primitives/digest/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
mod multiplexed;

pub use multiplexed::*;

#[cfg(feature = "blake2")]
pub mod blake2 {

pub use libcrux_blake2::{
Blake2bHash as Blake2b, Blake2bHasher, Blake2sHash as Blake2s, Blake2sHasher,
ConstDigestLen, RuntimeDigestLen,
};
}

#[cfg(feature = "sha2")]
pub mod sha2 {

pub use libcrux_sha2::{
Sha224Hash as Sha2_224, Sha224Hasher as Sha2_224Hasher, Sha256Hash as Sha2_256,
Sha256Hasher as Sha2_256Hasher, Sha384Hash as Sha2_384, Sha384Hasher as Sha2_384Hasher,
Sha512Hash as Sha2_512, Sha512Hasher as Sha2_512Hasher,
};
}

#[cfg(feature = "sha3")]
pub mod sha3 {

pub use libcrux_sha3::{
Sha3_224, Sha3_224Hasher, Sha3_256, Sha3_256Hasher, Sha3_384, Sha3_384Hasher, Sha3_512,
Sha3_512Hasher,
};
}
Loading
Loading