diff --git a/digest/README.md b/digest/README.md index 5038829a..e17da08b 100644 --- a/digest/README.md +++ b/digest/README.md @@ -64,25 +64,6 @@ let hash = Sha256::digest(b"my message"); println!("Result: {:x}", hash); ``` -### Hashing `Read`-able objects - -If you want to hash data from [`Read`][3] trait (e.g. from file) you can rely on -implementation of [`Write`][4] trait (requires enabled-by-default `std` feature): - -```rust -use sha2::{Sha256, Digest}; -use std::{fs, io}; - -let mut file = fs::File::open(&path)?; -let mut hasher = Sha256::new(); -let n = io::copy(&mut file, &mut hasher)?; -let hash = hasher.finalize(); - -println!("Path: {}", path); -println!("Bytes processed: {}", n); -println!("Hash value: {:x}", hash); -``` - ### Generic code You can write generic code over `Digest` (or other traits from `digest` crate) diff --git a/digest/src/core_api.rs b/digest/src/core_api.rs index 6e8b8def..d7fe308c 100644 --- a/digest/src/core_api.rs +++ b/digest/src/core_api.rs @@ -5,6 +5,7 @@ //! higher-level traits. use crate::InvalidOutputSize; +pub use block_buffer::{Eager, Lazy}; pub use crypto_common::{AlgorithmName, Block, BlockSizeUser, OutputSizeUser, Reset}; use block_buffer::{BlockBuffer, BufferKind}; diff --git a/digest/src/core_api/ct_variable.rs b/digest/src/core_api/ct_variable.rs index d8ad3e8c..1aba4d90 100644 --- a/digest/src/core_api/ct_variable.rs +++ b/digest/src/core_api/ct_variable.rs @@ -16,6 +16,7 @@ use crypto_common::{ hazmat::{DeserializeStateError, SerializableState, SerializedState, SubSerializedStateSize}, typenum::{IsLess, IsLessOrEqual, Le, LeEq, NonZero, Sum, U1, U256}, }; + /// Wrapper around [`VariableOutputCore`] which selects output size /// at compile time. #[derive(Clone)] diff --git a/digest/src/core_api/rt_variable.rs b/digest/src/core_api/rt_variable.rs index 6b69f09d..c880ccaa 100644 --- a/digest/src/core_api/rt_variable.rs +++ b/digest/src/core_api/rt_variable.rs @@ -18,8 +18,7 @@ use crypto_common::{ #[cfg(feature = "zeroize")] use zeroize::ZeroizeOnDrop; -/// Wrapper around [`VariableOutputCore`] which selects output size -/// at run time. +/// Wrapper around [`VariableOutputCore`] which selects output size at run time. #[derive(Clone)] pub struct RtVariableCoreWrapper { core: T, @@ -64,11 +63,19 @@ impl BlockSizeUser for RtVariableCoreWrapper { type BlockSize = T::BlockSize; } -impl Reset for RtVariableCoreWrapper { +// TODO: remove because the hasher may be customized? +impl Reset for RtVariableCoreWrapper { #[inline] fn reset(&mut self) { self.buffer.reset(); - self.core.reset(); + self.core = T::new(self.output_size as usize).unwrap(); + } +} + +impl AlgorithmName for RtVariableCoreWrapper { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + T::write_alg_name(f)?; + f.write_str("Var") } } diff --git a/digest/src/core_api/wrapper.rs b/digest/src/core_api/wrapper.rs index 24287177..5c91034c 100644 --- a/digest/src/core_api/wrapper.rs +++ b/digest/src/core_api/wrapper.rs @@ -149,6 +149,13 @@ impl ExtendableOutputReset for CoreWrapper { } } +impl AlgorithmName for CoreWrapper { + #[inline] + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + T::write_alg_name(f) + } +} + impl Drop for CoreWrapper { #[inline] fn drop(&mut self) { @@ -214,20 +221,12 @@ where } } -/// A proxy trait to a core type implemented by [`CoreWrapper`] -// TODO: replace with an inherent associated type on stabilization: -// https://github.com/rust-lang/rust/issues/8995 -pub trait CoreProxy: sealed::Sealed { +/// A proxy trait to a core type. +pub trait CoreProxy { /// Type wrapped by [`CoreWrapper`]. type Core; } -mod sealed { - pub trait Sealed {} -} - -impl sealed::Sealed for CoreWrapper {} - impl CoreProxy for CoreWrapper { type Core = T; } diff --git a/digest/src/lib.rs b/digest/src/lib.rs index de2a784b..9ec84531 100644 --- a/digest/src/lib.rs +++ b/digest/src/lib.rs @@ -55,6 +55,7 @@ pub mod core_api; mod digest; #[cfg(feature = "mac")] mod mac; +mod newtype; mod xof_fixed; #[cfg(feature = "core-api")] diff --git a/digest/src/newtype.rs b/digest/src/newtype.rs new file mode 100644 index 00000000..f03fec8d --- /dev/null +++ b/digest/src/newtype.rs @@ -0,0 +1,4 @@ +mod fixed; +mod variable_ct; +mod variable_rt; +mod xof; diff --git a/digest/src/newtype/fixed.rs b/digest/src/newtype/fixed.rs new file mode 100644 index 00000000..5bef611f --- /dev/null +++ b/digest/src/newtype/fixed.rs @@ -0,0 +1,171 @@ +/// Creates a newtype wrapper around another type and +/// delegates implementation of `digest` traits to it. +#[macro_export] +macro_rules! newtype_fixed_hash { + ( + $(#[$attr:meta])* + $v:vis struct $name:ident$(<$gp:ident: $bound:ident>)?($core_ty:ty); + ) => { + $(#[$attr])* + $v struct $name$(<$gp: $bound>)? { + core: $core_ty, + buffer: $crate::core_api::Buffer<$core_ty>, + } + + impl$(<$gp: $bound>)? core::fmt::Debug for $name$(<$gp>)? { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(concat!(stringify!($name), " { ... }")) + } + } + + impl$(<$gp: $bound>)? $crate::crypto_common::AlgorithmName for $name$(<$gp>)? { + #[inline] + fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + <$core_ty as $crate::crypto_common::AlgorithmName>::write_alg_name(f) + } + } + + impl$(<$gp: $bound>)? Clone for $name$(<$gp>)? { + #[inline] + fn clone(&self) -> Self { + Self { + core: Clone::clone(&self.core), + buffer: Clone::clone(&self.buffer), + } + } + } + + impl$(<$gp: $bound>)? Default for $name$(<$gp>)? { + #[inline] + fn default() -> Self { + Self { + core: Default::default(), + buffer: Default::default(), + } + } + } + + impl$(<$gp: $bound>)? $crate::Reset for $name$(<$gp>)? { + #[inline] + fn reset(&mut self) { + $crate::Reset::reset(&mut self.core); + self.buffer.reset(); + } + } + + impl$(<$gp: $bound>)? $crate::core_api::BlockSizeUser for $name$(<$gp>)? { + type BlockSize = <$core_ty as $crate::crypto_common::BlockSizeUser>::BlockSize; + } + + impl$(<$gp: $bound>)? $crate::OutputSizeUser for $name$(<$gp>)? { + type OutputSize = <$core_ty as $crate::core_api::OutputSizeUser>::OutputSize; + } + + impl$(<$gp: $bound>)? $crate::HashMarker for $name$(<$gp>)? {} + + // Verify that `$core_ty` implements `HashMarker` + const _: () = { + fn check$(<$gp: $bound>)?(v: &$core_ty) { + v as &dyn $crate::HashMarker; + } + }; + + impl$(<$gp: $bound>)? $crate::core_api::CoreProxy for $name$(<$gp>)? { + type Core = $core_ty; + } + + impl$(<$gp: $bound>)? $crate::Update for $name$(<$gp>)? { + #[inline] + fn update(&mut self, data: &[u8]) { + let Self { core, buffer } = self; + buffer.digest_blocks(data, |blocks| { + $crate::core_api::UpdateCore::update_blocks(core, blocks) + }); + } + } + + impl$(<$gp: $bound>)? $crate::FixedOutput for $name$(<$gp>)? { + #[inline] + fn finalize_into(mut self, out: &mut $crate::Output) { + let Self { core, buffer } = &mut self; + $crate::core_api::FixedOutputCore::finalize_fixed_core(core, buffer, out); + } + } + + impl$(<$gp: $bound>)? $crate::FixedOutputReset for $name$(<$gp>)? { + #[inline] + fn finalize_into_reset(&mut self, out: &mut $crate::Output) { + let Self { core, buffer } = self; + $crate::core_api::FixedOutputCore::finalize_fixed_core(core, buffer, out); + $crate::Reset::reset(self); + } + } + + impl$(<$gp: $bound>)? $crate::crypto_common::hazmat::SerializableState for $name$(<$gp>)? { + type SerializedStateSize = $crate::typenum::Sum< + <$core_ty as $crate::crypto_common::hazmat::SerializableState>::SerializedStateSize, + $crate::typenum::Add1< + <$core_ty as $crate::core_api::BlockSizeUser>::BlockSize + > + >; + + #[inline] + fn serialize(&self) -> $crate::crypto_common::hazmat::SerializedState { + use $crate::{ + array::Array, + consts::U1, + block_buffer::BlockBuffer, + crypto_common::hazmat::SerializableState, + }; + + let serialized_core = self.core.serialize(); + let pos = u8::try_from(self.buffer.get_pos()).unwrap(); + let serialized_pos: Array = Array([pos]); + let serialized_data = self.buffer.clone().pad_with_zeros(); + + serialized_core + .concat(serialized_pos) + .concat(serialized_data) + } + + #[inline] + fn deserialize( + serialized_state: &$crate::crypto_common::hazmat::SerializedState, + ) -> Result { + use $crate::{ + block_buffer::BlockBuffer, + consts::U1, + crypto_common::hazmat::{SerializableState, DeserializeStateError}, + }; + + let (serialized_core, remaining_buffer) = serialized_state + .split_ref::<<$core_ty as SerializableState>::SerializedStateSize>(); + let (serialized_pos, serialized_data) = remaining_buffer.split_ref::(); + + Ok(Self { + core: <$core_ty as SerializableState>::deserialize(serialized_core)?, + buffer: BlockBuffer::try_new(&serialized_data[..serialized_pos[0].into()]) + .map_err(|_| DeserializeStateError)?, + }) + } + } + }; + + ( + $(#[$attr:meta])* + $v:vis struct $name:ident$(<$gp:ident: $bound:ident>)?($core_ty:ty); + oid: $oid:literal + ) => { + $crate::newtype_fixed_hash!( + $(#[$attr])* + $v struct $name$(<$gp: $bound>)?($core_ty); + ); + + #[cfg(feature = "oid")] + impl$(<$gp: $bound>)? $crate::const_oid::AssociatedOid for $name$(<$gp>)? { + const OID: $crate::const_oid::ObjectIdentifier = + $crate::const_oid::ObjectIdentifier::new_unwrap($oid); + } + }; +} diff --git a/digest/src/newtype/variable_ct.rs b/digest/src/newtype/variable_ct.rs new file mode 100644 index 00000000..86216f45 --- /dev/null +++ b/digest/src/newtype/variable_ct.rs @@ -0,0 +1,224 @@ +/// Creates a newtype wrapper around another type and +/// delegates implementation of `digest` traits to it. +#[macro_export] +macro_rules! newtype_ct_variable_hash { + ( + $(#[$ct_attr:meta])* + $vis:vis struct $name:ident<$out_size:ident>($core_ty:ty); + // Ideally, we would use `$core_ty::OutputSize`, but unfortunately the compiler + // does not accept such code. The likely reason is this issue: + // https://github.com/rust-lang/rust/issues/79629 + max_size: $max_size:ty; + ) => { + $(#[$ct_attr])* + $vis struct $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + core: $crate::core_api::CtVariableCoreWrapper<$core_ty, $out_size>, + buffer: $crate::core_api::Buffer<$core_ty>, + } + + impl<$out_size> core::fmt::Debug for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(concat!(stringify!($name), " { ... }")) + } + } + + impl<$out_size> $crate::crypto_common::AlgorithmName for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + #[inline] + fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + <$core_ty as $crate::crypto_common::AlgorithmName>::write_alg_name(f) + } + } + + impl<$out_size> Clone for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + #[inline] + fn clone(&self) -> Self { + Self { + core: Clone::clone(&self.core), + buffer: Clone::clone(&self.buffer), + } + } + } + + impl<$out_size> Default for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + #[inline] + fn default() -> Self { + Self { + core: Default::default(), + buffer: Default::default(), + } + } + } + + impl<$out_size> $crate::Reset for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + #[inline] + fn reset(&mut self) { + $crate::Reset::reset(&mut self.core); + self.buffer.reset(); + } + } + + impl<$out_size> $crate::core_api::BlockSizeUser for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + type BlockSize = <$core_ty as $crate::crypto_common::BlockSizeUser>::BlockSize; + } + + impl<$out_size> $crate::OutputSizeUser for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + type OutputSize = $out_size; + } + + impl<$out_size> $crate::HashMarker for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + {} + + // Verify that `$wrapped_ty` implements `HashMarker` + const _: () = { + fn check<$out_size>(v: &$core_ty) + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + v as &dyn $crate::HashMarker; + } + }; + + impl<$out_size> $crate::core_api::CoreProxy for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + type Core = $crate::core_api::CtVariableCoreWrapper<$core_ty, $out_size>; + } + + impl<$out_size> $crate::Update for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + #[inline] + fn update(&mut self, data: &[u8]) { + let Self { core, buffer } = self; + buffer.digest_blocks(data, |blocks| { + $crate::core_api::UpdateCore::update_blocks(core, blocks) + }); + } + } + + impl<$out_size> $crate::FixedOutput for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + #[inline] + fn finalize_into(mut self, out: &mut $crate::Output) { + let Self { core, buffer } = &mut self; + $crate::core_api::FixedOutputCore::finalize_fixed_core(core, buffer, out); + } + } + + impl<$out_size> $crate::FixedOutputReset for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + #[inline] + fn finalize_into_reset(&mut self, out: &mut $crate::Output) { + let Self { core, buffer } = self; + $crate::core_api::FixedOutputCore::finalize_fixed_core(core, buffer, out); + $crate::Reset::reset(self); + } + } + + impl<$out_size> $crate::crypto_common::hazmat::SerializableState for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size>, + $crate::typenum::LeEq<$out_size, $max_size>: $crate::typenum::NonZero, + { + type SerializedStateSize = $crate::typenum::Add1<$crate::typenum::Sum< + < + $crate::core_api::CtVariableCoreWrapper<$core_ty, $out_size> + as $crate::crypto_common::hazmat::SerializableState + >::SerializedStateSize, + <$core_ty as $crate::core_api::BlockSizeUser>::BlockSize, + >>; + + #[inline] + fn serialize(&self) -> $crate::crypto_common::hazmat::SerializedState { + use $crate::{ + array::Array, + consts::U1, + block_buffer::BlockBuffer, + crypto_common::hazmat::SerializableState, + }; + + let serialized_core = self.core.serialize(); + let pos = u8::try_from(self.buffer.get_pos()).unwrap(); + let serialized_pos: Array = Array([pos]); + let serialized_data = self.buffer.clone().pad_with_zeros(); + + serialized_core + .concat(serialized_pos) + .concat(serialized_data) + } + + #[inline] + fn deserialize( + serialized_state: &$crate::crypto_common::hazmat::SerializedState, + ) -> Result { + use $crate::{ + block_buffer::BlockBuffer, + consts::U1, + core_api::CtVariableCoreWrapper, + crypto_common::hazmat::{SerializableState, DeserializeStateError}, + }; + + let (serialized_core, remaining_buffer) = serialized_state + .split_ref::<< + CtVariableCoreWrapper<$core_ty, $out_size> + as SerializableState + >::SerializedStateSize>(); + let (serialized_pos, serialized_data) = remaining_buffer.split_ref::(); + + let core = SerializableState::deserialize(serialized_core)?; + let pos = usize::from(serialized_pos[0]); + let buffer = BlockBuffer::try_new(&serialized_data[..pos]) + .map_err(|_| DeserializeStateError)?; + + Ok(Self { core, buffer }) + } + } + }; +} diff --git a/digest/src/newtype/variable_rt.rs b/digest/src/newtype/variable_rt.rs new file mode 100644 index 00000000..34542b26 --- /dev/null +++ b/digest/src/newtype/variable_rt.rs @@ -0,0 +1,175 @@ +/// Wrap +#[macro_export] +macro_rules! newtype_rt_variable_hash { + ( + $(#[$rt_attr:meta])* + $rt_vis:vis struct $name:ident($core_ty:ty); + ) => { + $(#[$rt_attr])* + $rt_vis struct $name { + core: $core_ty, + buffer: $crate::core_api::Buffer<$core_ty>, + output_size: u8, + } + + impl core::fmt::Debug for $name { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(concat!(stringify!($name), " { ... }")) + } + } + + impl $crate::crypto_common::AlgorithmName for $name { + #[inline] + fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + <$core_ty as $crate::crypto_common::AlgorithmName>::write_alg_name(f) + } + } + + impl Clone for $name { + #[inline] + fn clone(&self) -> Self { + Self { + core: Clone::clone(&self.core), + buffer: Clone::clone(&self.buffer), + output_size: self.output_size, + } + } + } + + impl $crate::Reset for $name { + #[inline] + fn reset(&mut self) { + let size = self.output_size.into(); + self.core = <$core_ty as $crate::core_api::VariableOutputCore>::new(size).unwrap(); + self.buffer.reset(); + } + } + + impl $crate::core_api::BlockSizeUser for $name { + type BlockSize = <$core_ty as $crate::crypto_common::BlockSizeUser>::BlockSize; + } + + impl $crate::HashMarker for $name {} + + // Verify that `$wrapped_ty` implements `HashMarker` + const _: () = { + fn check(v: &$core_ty) { + v as &dyn $crate::HashMarker; + } + }; + + impl $crate::Update for $name { + #[inline] + fn update(&mut self, data: &[u8]) { + let Self { core, buffer, .. } = self; + buffer.digest_blocks(data, |blocks| { + $crate::core_api::UpdateCore::update_blocks(core, blocks); + }); + } + } + + impl $name { + #[inline] + fn finalize_dirty(&mut self, out: &mut [u8]) -> Result<(), $crate::InvalidBufferSize> { + let Self { + core, + buffer, + output_size, + } = self; + let size_u8 = u8::try_from(out.len()).map_err(|_| $crate::InvalidBufferSize)?; + + let max_size = ::MAX_OUTPUT_SIZE; + if out.len() > max_size || size_u8 != *output_size { + return Err($crate::InvalidBufferSize); + } + let mut full_res = Default::default(); + $crate::core_api::VariableOutputCore::finalize_variable_core(core, buffer, &mut full_res); + let n = out.len(); + let m = full_res.len() - n; + use $crate::core_api::TruncSide::{Left, Right}; + let side = <$core_ty as $crate::core_api::VariableOutputCore>::TRUNC_SIDE; + match side { + Left => out.copy_from_slice(&full_res[..n]), + Right => out.copy_from_slice(&full_res[m..]), + } + Ok(()) + } + } + + impl $crate::VariableOutput for $name { + const MAX_OUTPUT_SIZE: usize = < + <$core_ty as $crate::core_api::OutputSizeUser>::OutputSize + as $crate::typenum::Unsigned + >::USIZE; + + #[inline] + fn new(output_size: usize) -> Result { + let output_size = u8::try_from(output_size).map_err(|_| $crate::InvalidOutputSize)?; + let buffer = Default::default(); + let core = <$core_ty as $crate::core_api::VariableOutputCore>::new(output_size.into())?; + Ok(Self { + core, + buffer, + output_size, + }) + } + + #[inline] + fn output_size(&self) -> usize { + self.output_size.into() + } + + #[inline] + fn finalize_variable(mut self, out: &mut [u8]) -> Result<(), $crate::InvalidBufferSize> { + self.finalize_dirty(out) + } + } + + impl $crate::VariableOutputReset for $name { + #[inline] + fn finalize_variable_reset( + &mut self, + out: &mut [u8], + ) -> Result<(), $crate::InvalidBufferSize> { + self.finalize_dirty(out)?; + $crate::Reset::reset(self); + Ok(()) + } + } + + impl Drop for $name { + #[inline] + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + { + use $crate::zeroize::Zeroize; + self.buffer.zeroize(); + self.output_size.zeroize(); + } + } + } + + #[cfg(feature = "zeroize")] + impl $crate::zeroize::ZeroizeOnDrop for $name {} + + impl $crate::crypto_common::hazmat::SerializableState for $name { + type SerializedStateSize = $crate::typenum::Add1<$crate::typenum::Sum< + <$core_ty as $crate::crypto_common::hazmat::SerializableState>::SerializedStateSize, + <$core_ty as $crate::core_api::BlockSizeUser>::BlockSize, + >>; + + #[inline] + fn serialize(&self) -> $crate::crypto_common::hazmat::SerializedState { + todo!() + } + + #[inline] + fn deserialize( + serialized_state: &$crate::crypto_common::hazmat::SerializedState, + ) -> Result { + todo!() + } + } + }; +} diff --git a/digest/src/newtype/xof.rs b/digest/src/newtype/xof.rs new file mode 100644 index 00000000..427a3371 --- /dev/null +++ b/digest/src/newtype/xof.rs @@ -0,0 +1,201 @@ +/// Creates a newtype wrapper around another type and +/// delegates implementation of `digest` traits to it. +#[macro_export] +macro_rules! newtype_xof_hash { + ( + $(#[$attr:meta])* + $hasher_vis:vis struct $hasher_name:ident($hasher_core:ty); + $(#[$reader_attr:meta])* + $reader_vis:vis struct $reader_name:ident($reader_core:ty); + $(oid: $oid:literal)? + ) => { + $(#[$attr])* + $hasher_vis struct $hasher_name { + core: $hasher_core, + buffer: $crate::core_api::Buffer<$hasher_core>, + } + + impl core::fmt::Debug for $hasher_name { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(concat!(stringify!($hasher_name), " { ... }")) + } + } + + impl $crate::crypto_common::AlgorithmName for $hasher_name { + #[inline] + fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + <$hasher_core as $crate::crypto_common::AlgorithmName>::write_alg_name(f) + } + } + + impl Clone for $hasher_name { + #[inline] + fn clone(&self) -> Self { + Self { + core: Clone::clone(&self.core), + buffer: Clone::clone(&self.buffer), + } + } + } + + impl Default for $hasher_name { + #[inline] + fn default() -> Self { + Self { + core: Default::default(), + buffer: Default::default(), + } + } + } + + impl $crate::Reset for $hasher_name { + #[inline] + fn reset(&mut self) { + $crate::Reset::reset(&mut self.core); + self.buffer.reset(); + } + } + + impl $crate::core_api::BlockSizeUser for $hasher_name { + type BlockSize = <$hasher_core as $crate::crypto_common::BlockSizeUser>::BlockSize; + } + + impl $crate::HashMarker for $hasher_name {} + + // Verify that `$hasher_core` implements `HashMarker` + const _: () = { + fn check(v: &$hasher_core) { + v as &dyn $crate::HashMarker; + } + }; + + impl $crate::core_api::CoreProxy for $hasher_name { + type Core = $hasher_core; + } + + impl $crate::Update for $hasher_name { + #[inline] + fn update(&mut self, data: &[u8]) { + let Self { core, buffer } = self; + buffer.digest_blocks(data, |blocks| { + $crate::core_api::UpdateCore::update_blocks(core, blocks) + }); + } + } + + impl $crate::ExtendableOutput for $hasher_name { + type Reader = $reader_name; + + #[inline] + fn finalize_xof(mut self) -> Self::Reader { + let Self { core, buffer } = &mut self; + let core = <$hasher_core as $crate::core_api::ExtendableOutputCore>::finalize_xof_core(core, buffer); + let buffer = Default::default(); + Self::Reader { core, buffer } + } + } + + impl $crate::ExtendableOutputReset for $hasher_name { + #[inline] + fn finalize_xof_reset(&mut self) -> Self::Reader { + let Self { core, buffer } = self; + let core = <$hasher_core as $crate::core_api::ExtendableOutputCore>::finalize_xof_core(core, buffer); + $crate::Reset::reset(self); + let buffer = Default::default(); + Self::Reader { core, buffer } + } + } + + + impl $crate::crypto_common::hazmat::SerializableState for $hasher_name { + type SerializedStateSize = $crate::typenum::Sum< + <$hasher_core as $crate::crypto_common::hazmat::SerializableState>::SerializedStateSize, + $crate::typenum::Add1< + <$hasher_core as $crate::core_api::BlockSizeUser>::BlockSize + > + >; + + #[inline] + fn serialize(&self) -> $crate::crypto_common::hazmat::SerializedState { + use $crate::{ + array::Array, + consts::U1, + block_buffer::BlockBuffer, + crypto_common::hazmat::SerializableState, + }; + + let serialized_core = self.core.serialize(); + let pos = u8::try_from(self.buffer.get_pos()).unwrap(); + let serialized_pos: Array = Array([pos]); + let serialized_data = self.buffer.clone().pad_with_zeros(); + + serialized_core + .concat(serialized_pos) + .concat(serialized_data) + } + + #[inline] + fn deserialize( + serialized_state: &$crate::crypto_common::hazmat::SerializedState, + ) -> Result { + use $crate::{ + block_buffer::BlockBuffer, + consts::U1, + crypto_common::hazmat::{SerializableState, DeserializeStateError}, + }; + + let (serialized_core, remaining_buffer) = serialized_state + .split_ref::<<$hasher_core as SerializableState>::SerializedStateSize>(); + let (serialized_pos, serialized_data) = remaining_buffer.split_ref::(); + + Ok(Self { + core: <$hasher_core as SerializableState>::deserialize(serialized_core)?, + buffer: BlockBuffer::try_new(&serialized_data[..serialized_pos[0].into()]) + .map_err(|_| DeserializeStateError)?, + }) + } + } + + $( + #[cfg(feature = "oid")] + impl $crate::const_oid::AssociatedOid for $hasher_name { + const OID: $crate::const_oid::ObjectIdentifier = + $crate::const_oid::ObjectIdentifier::new_unwrap($oid); + } + )? + + $(#[$reader_attr])* + $reader_vis struct $reader_name { + core: $reader_core, + buffer: $crate::block_buffer::ReadBuffer<<$reader_core as $crate::core_api::BlockSizeUser>::BlockSize>, + } + + impl core::fmt::Debug for $reader_name { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(concat!(stringify!($reader_name), " { ... }")) + } + } + + impl Clone for $reader_name { + #[inline] + fn clone(&self) -> Self { + Self { + core: Clone::clone(&self.core), + buffer: Clone::clone(&self.buffer), + } + } + } + + impl $crate::XofReader for $reader_name { + #[inline] + fn read(&mut self, buf: &mut [u8]) { + let Self { core, buffer } = self; + buffer.read(buf, |block| { + *block = $crate::core_api::XofReaderCore::read_block(core); + }); + } + } + }; +} diff --git a/digest/tests/dummy_fixed.rs b/digest/tests/dummy_fixed.rs new file mode 100644 index 00000000..169bb50a --- /dev/null +++ b/digest/tests/dummy_fixed.rs @@ -0,0 +1,83 @@ +#![cfg(feature = "core-api")] + +mod block_api { + use core::fmt; + use digest::{ + HashMarker, Output, OutputSizeUser, Reset, + consts::U8, + core_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore, + UpdateCore, + }, + crypto_common::hazmat::{DeserializeStateError, SerializableState, SerializedState}, + }; + + /// Core of primitive XOR hasher for testing purposes + #[derive(Clone, Default, Debug)] + pub struct FixedHashCore { + state: u64, + } + + impl AlgorithmName for FixedHashCore { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("FixedHash") + } + } + + impl BlockSizeUser for FixedHashCore { + type BlockSize = U8; + } + + impl BufferKindUser for FixedHashCore { + type BufferKind = block_buffer::Eager; + } + + impl Reset for FixedHashCore { + fn reset(&mut self) { + self.state = 0; + } + } + + impl UpdateCore for FixedHashCore { + fn update_blocks(&mut self, blocks: &[Block]) { + for block in blocks { + self.state ^= u64::from_le_bytes(block.0) + } + } + } + + impl HashMarker for FixedHashCore {} + + impl OutputSizeUser for FixedHashCore { + type OutputSize = U8; + } + + impl FixedOutputCore for FixedHashCore { + fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { + let block = buffer.pad_with_zeros(); + self.state ^= u64::from_le_bytes(block.0); + out.copy_from_slice(&self.state.to_le_bytes()); + } + } + + impl SerializableState for FixedHashCore { + type SerializedStateSize = U8; + + fn serialize(&self) -> SerializedState { + self.state.to_le_bytes().into() + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + Ok(Self { + state: u64::from_le_bytes(serialized_state.0), + }) + } + } +} + +digest::newtype_fixed_hash!( + /// Primitive XOR hasher for testing purposes + pub struct FixedHash(block_api::FixedHashCore); +);