diff --git a/digest/CHANGELOG.md b/digest/CHANGELOG.md index 431816c5e..a4fe6b64a 100644 --- a/digest/CHANGELOG.md +++ b/digest/CHANGELOG.md @@ -7,21 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.11.0 (UNRELEASED) ### Added -- `CustomizedInit` trait ([#1334]). +- `CustomizedInit` trait ([#1334]) +- `buffer_fixed`, `buffer_ct_variable`, `buffer_rt_variable`, and `buffer_xof` macros ([#1799]) +- `CollisionResistance` trait ([#1820]) ### Changed - `crypto-common` dependency bumped to v0.2 ([#1173]) - Edition changed to 2024 and MSRV bumped to 1.85 ([#1759]) +- `CtVariableCoreWrapper` renamed to `CtOutWrapper` ([#1799]) +- Removed the OID type parameter from `CtOutWrapper` ([#1799]) ### Removed - `Mac::new`, `Mac::new_from_slice`, and `Mac::generate_key` methods ([#1173]) +- `CoreWrapper`, `RtVariableCoreWrapper`, and `XofReaderCoreWrapper` types ([#1799]) - `HashReader` and `HashWriter` are moved to the `digest-io` crate ([#1809]) - `io::Write/Read` implementations in favor of the `digest_io::IoWrapper` type ([#1809]) [#1173]: https://github.com/RustCrypto/traits/pull/1173 [#1334]: https://github.com/RustCrypto/traits/pull/1334 [#1759]: https://github.com/RustCrypto/traits/pull/1759 +[#1799]: https://github.com/RustCrypto/traits/pull/1799 [#1809]: https://github.com/RustCrypto/traits/pull/1809 +[#1820]: https://github.com/RustCrypto/traits/pull/1820 ## 0.10.7 (2023-05-19) ### Changed diff --git a/digest/Cargo.toml b/digest/Cargo.toml index db79a487d..895d1efe9 100644 --- a/digest/Cargo.toml +++ b/digest/Cargo.toml @@ -23,8 +23,8 @@ const-oid = { version = "0.10", optional = true } zeroize = { version = "1.7", optional = true, default-features = false } [features] -default = ["core-api"] -core-api = ["block-buffer"] # Enable Core API traits +default = ["block-api"] +block-api = ["block-buffer"] # Enable block API traits mac = ["subtle"] # Enable MAC traits rand_core = ["crypto-common/rand_core"] # Enable random key generation methods os_rng = ["crypto-common/rand_core", "rand_core"] diff --git a/digest/README.md b/digest/README.md index 5038829ad..e17da08b0 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/block_api.rs similarity index 94% rename from digest/src/core_api.rs rename to digest/src/block_api.rs index 6e8b8def7..bdc70ab6c 100644 --- a/digest/src/core_api.rs +++ b/digest/src/block_api.rs @@ -5,20 +5,14 @@ //! 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}; use crypto_common::Output; mod ct_variable; -mod rt_variable; -mod wrapper; -mod xof_reader; - -pub use ct_variable::CtVariableCoreWrapper; -pub use rt_variable::RtVariableCoreWrapper; -pub use wrapper::{CoreProxy, CoreWrapper}; -pub use xof_reader::XofReaderCoreWrapper; +pub use ct_variable::CtOutWrapper; /// Buffer type used by type which implements [`BufferKindUser`]. pub type Buffer = @@ -102,3 +96,9 @@ pub enum TruncSide { /// Truncate right side, i.e. `&out[m..]`. Right, } + +/// A proxy trait to a core type. +pub trait CoreProxy { + /// Wrapped block-level type. + type Core; +} diff --git a/digest/src/core_api/ct_variable.rs b/digest/src/block_api/ct_variable.rs similarity index 51% rename from digest/src/core_api/ct_variable.rs rename to digest/src/block_api/ct_variable.rs index 93ff2f96a..775dd3c34 100644 --- a/digest/src/core_api/ct_variable.rs +++ b/digest/src/block_api/ct_variable.rs @@ -5,8 +5,6 @@ use super::{ #[cfg(feature = "mac")] use crate::MacMarker; use crate::{CollisionResistance, CustomizedInit, HashMarker, VarOutputCustomized}; -#[cfg(feature = "oid")] -use const_oid::{AssociatedOid, ObjectIdentifier}; use core::{ fmt, marker::PhantomData, @@ -16,67 +14,55 @@ use crypto_common::{ Block, BlockSizeUser, OutputSizeUser, array::{Array, ArraySize}, hazmat::{DeserializeStateError, SerializableState, SerializedState, SubSerializedStateSize}, - typenum::{IsLess, IsLessOrEqual, Le, LeEq, NonZero, Sum, U1, U256}, + typenum::{IsLess, IsLessOrEqual, Le, NonZero, Sum, True, U1, U256}, }; -/// Dummy type used with [`CtVariableCoreWrapper`] in cases when -/// resulting hash does not have a known OID. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub struct NoOid; - -/// Wrapper around [`VariableOutputCore`] which selects output size -/// at compile time. +/// Wrapper around [`VariableOutputCore`] which selects output size at compile time. #[derive(Clone)] -pub struct CtVariableCoreWrapper +pub struct CtOutWrapper where T: VariableOutputCore, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { inner: T, - _out: PhantomData<(OutSize, O)>, + _out: PhantomData, } -impl HashMarker for CtVariableCoreWrapper +impl HashMarker for CtOutWrapper where T: VariableOutputCore + HashMarker, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { } #[cfg(feature = "mac")] -impl MacMarker for CtVariableCoreWrapper +impl MacMarker for CtOutWrapper where T: VariableOutputCore + MacMarker, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { } -impl CollisionResistance for CtVariableCoreWrapper +impl CollisionResistance for CtOutWrapper where T: VariableOutputCore + CollisionResistance, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { type CollisionResistance = T::CollisionResistance; } -impl BlockSizeUser for CtVariableCoreWrapper +impl BlockSizeUser for CtOutWrapper where T: VariableOutputCore, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { type BlockSize = T::BlockSize; } -impl UpdateCore for CtVariableCoreWrapper +impl UpdateCore for CtOutWrapper where T: VariableOutputCore, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { #[inline] fn update_blocks(&mut self, blocks: &[Block]) { @@ -84,29 +70,26 @@ where } } -impl OutputSizeUser for CtVariableCoreWrapper +impl OutputSizeUser for CtOutWrapper where T: VariableOutputCore, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { type OutputSize = OutSize; } -impl BufferKindUser for CtVariableCoreWrapper +impl BufferKindUser for CtOutWrapper where T: VariableOutputCore, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { type BufferKind = T::BufferKind; } -impl FixedOutputCore for CtVariableCoreWrapper +impl FixedOutputCore for CtOutWrapper where T: VariableOutputCore, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { #[inline] fn finalize_fixed_core( @@ -125,11 +108,10 @@ where } } -impl Default for CtVariableCoreWrapper +impl Default for CtOutWrapper where T: VariableOutputCore, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { #[inline] fn default() -> Self { @@ -140,11 +122,10 @@ where } } -impl CustomizedInit for CtVariableCoreWrapper +impl CustomizedInit for CtOutWrapper where T: VariableOutputCore + VarOutputCustomized, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { #[inline] fn new_customized(customization: &[u8]) -> Self { @@ -155,11 +136,10 @@ where } } -impl Reset for CtVariableCoreWrapper +impl Reset for CtOutWrapper where T: VariableOutputCore, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { #[inline] fn reset(&mut self) { @@ -167,11 +147,10 @@ where } } -impl AlgorithmName for CtVariableCoreWrapper +impl AlgorithmName for CtOutWrapper where T: VariableOutputCore + AlgorithmName, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { T::write_alg_name(f)?; @@ -180,61 +159,31 @@ where } } -#[cfg(feature = "oid")] -impl AssociatedOid for CtVariableCoreWrapper -where - T: VariableOutputCore, - O: AssociatedOid, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, -{ - const OID: ObjectIdentifier = O::OID; -} - #[cfg(feature = "zeroize")] -impl zeroize::ZeroizeOnDrop for CtVariableCoreWrapper +impl zeroize::ZeroizeOnDrop for CtOutWrapper where T: VariableOutputCore + zeroize::ZeroizeOnDrop, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { } -impl fmt::Debug for CtVariableCoreWrapper +impl fmt::Debug for CtOutWrapper where T: VariableOutputCore + AlgorithmName, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Self::write_alg_name(f) } } -/// Implement dummy type with hidden docs which is used to "carry" hasher -/// OID for [`CtVariableCoreWrapper`]. -#[macro_export] -macro_rules! impl_oid_carrier { - ($name:ident, $oid:literal) => { - #[doc(hidden)] - #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] - pub struct $name; - - #[cfg(feature = "oid")] - impl AssociatedOid for $name { - const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap($oid); - } - }; -} - type CtVariableCoreWrapperSerializedStateSize = Sum<::SerializedStateSize, U1>; -impl SerializableState for CtVariableCoreWrapper +impl SerializableState for CtOutWrapper where T: VariableOutputCore + SerializableState, - OutSize: ArraySize + IsLessOrEqual, - LeEq: NonZero, + OutSize: ArraySize + IsLessOrEqual, T::BlockSize: IsLess, Le: NonZero, T::SerializedStateSize: Add, diff --git a/digest/src/buffer_macros.rs b/digest/src/buffer_macros.rs new file mode 100644 index 000000000..f03fec8d8 --- /dev/null +++ b/digest/src/buffer_macros.rs @@ -0,0 +1,4 @@ +mod fixed; +mod variable_ct; +mod variable_rt; +mod xof; diff --git a/digest/src/buffer_macros/fixed.rs b/digest/src/buffer_macros/fixed.rs new file mode 100644 index 000000000..7d27d8906 --- /dev/null +++ b/digest/src/buffer_macros/fixed.rs @@ -0,0 +1,473 @@ +/// Creates a buffered wrapper around block-level "core" type which implements fixed output size traits. +#[macro_export] +macro_rules! buffer_fixed { + ( + $(#[$attr:meta])* + $v:vis struct $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + impl: $($trait_name:ident)*; + ) => { + $(#[$attr])* + $v struct $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? { + core: $core_ty, + buffer: $crate::block_api::Buffer<$core_ty>, + } + + $crate::buffer_fixed!( + impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); + $($trait_name)*; + ); + }; + + ( + $(#[$attr:meta])* + $v:vis struct $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + oid: $oid:literal; + impl: $($trait_name:ident)*; + ) => { + $crate::buffer_fixed!( + $(#[$attr])* + $v struct $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); + impl: $($trait_name)*; + ); + + #[cfg(feature = "oid")] + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::const_oid::AssociatedOid for $name$(< $( $lt ),+ >)? { + const OID: $crate::const_oid::ObjectIdentifier = + $crate::const_oid::ObjectIdentifier::new_unwrap($oid); + } + }; + + // Terminates `impl_inner` sequences. + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + ; + ) => {}; + + // Implements the set of traits common for fixed output hashes: + // `Default`, `Clone`, `HashMarker`, `Reset`, `FixedOutputReset`, `SerializableState` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + FixedHashTraits $($trait_name:ident)*; + ) => { + $crate::buffer_fixed!( + impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); + BaseFixedTraits AlgorithmName Default Clone HashMarker + Reset FixedOutputReset SerializableState $($trait_name)*; + ); + }; + + // Implements the set of traits common for MAC functions: + // `Debug`, `BlockSizeUser`, `OutputSizeUser`, `CoreProxy`, `Update`, `FixedOutput`, + // `Clone`, `MacMarker`. + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + MacTraits $($trait_name:ident)*; + ) => { + $crate::buffer_fixed!( + impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); + BaseFixedTraits Clone MacMarker $($trait_name)*; + ); + }; + + // Implements the set of traits common for resettable MAC functions: + // `Debug`, `BlockSizeUser`, `OutputSizeUser`, `CoreProxy`, `Update`, `FixedOutput`, + // `Clone`, `MacMarker`, `Reset`, `FixedOutputReset`. + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + ResetMacTraits $($trait_name:ident)*; + ) => { + $crate::buffer_fixed!( + impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); + MacTraits Reset FixedOutputReset $($trait_name)*; + ); + }; + + // Implements basic fixed traits: + // `Debug`, `BlockSizeUser`, `OutputSizeUser`, `CoreProxy`, `Update`, and `FixedOutput`. + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + BaseFixedTraits $($trait_name:ident)*; + ) => { + $crate::buffer_fixed!( + impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); + Debug BlockSizeUser OutputSizeUser CoreProxy Update FixedOutput $($trait_name)*; + ); + }; + + // Implements `Debug` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + Debug $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? core::fmt::Debug for $name$(< $( $lt ),+ >)? { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(concat!(stringify!($name), " { ... }")) + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `AlgorithmName` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + AlgorithmName $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::crypto_common::AlgorithmName for $name$(< $( $lt ),+ >)? { + #[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) + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `BlockSizeUser` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + BlockSizeUser $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::block_api::BlockSizeUser for $name$(< $( $lt ),+ >)? { + type BlockSize = <$core_ty as $crate::crypto_common::BlockSizeUser>::BlockSize; + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `OutputSizeUser` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + OutputSizeUser $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::OutputSizeUser for $name$(< $( $lt ),+ >)? { + type OutputSize = <$core_ty as $crate::block_api::OutputSizeUser>::OutputSize; + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `CoreProxy` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + CoreProxy $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::block_api::CoreProxy for $name$(< $( $lt ),+ >)? { + type Core = $core_ty; + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `Update` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + Update $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::Update for $name$(< $( $lt ),+ >)? { + #[inline] + fn update(&mut self, data: &[u8]) { + let Self { core, buffer } = self; + buffer.digest_blocks(data, |blocks| { + $crate::block_api::UpdateCore::update_blocks(core, blocks) + }); + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `FixedOutput` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + FixedOutput $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::FixedOutput for $name$(< $( $lt ),+ >)? { + #[inline] + fn finalize_into(mut self, out: &mut $crate::Output) { + let Self { core, buffer } = &mut self; + $crate::block_api::FixedOutputCore::finalize_fixed_core(core, buffer, out); + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `Default` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + Default $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? Default for $name$(< $( $lt ),+ >)? { + #[inline] + fn default() -> Self { + Self { + core: Default::default(), + buffer: Default::default(), + } + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `CustomizedInit` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + CustomizedInit $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::CustomizedInit for $name$(< $( $lt ),+ >)? { + #[inline] + fn new_customized(customization: &[u8]) -> Self { + Self { + core: $crate::CustomizedInit::new_customized(customization), + buffer: Default::default(), + } + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `Clone` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + Clone $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? Clone for $name$(< $( $lt ),+ >)? { + #[inline] + fn clone(&self) -> Self { + Self { + core: Clone::clone(&self.core), + buffer: Clone::clone(&self.buffer), + } + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `HashMarker` and asserts that `$core_ty` implements it + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + HashMarker $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::HashMarker for $name$(< $( $lt ),+ >)? {} + + // Verify that `$core_ty` implements `HashMarker` + const _: () = { + fn check$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?(v: &$core_ty) { + v as &dyn $crate::HashMarker; + } + }; + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `MacMarker` and asserts that `$core_ty` implements it + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + MacMarker $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::MacMarker for $name$(< $( $lt ),+ >)? {} + + // Verify that `$core_ty` implements `MacMarker` + const _: () = { + fn check$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?(v: &$core_ty) { + v as &dyn $crate::MacMarker; + } + }; + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `InnerUser` and `InnerInit` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + InnerInit $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::crypto_common::InnerUser for $name$(< $( $lt ),+ >)? { + type Inner = <$core_ty as $crate::crypto_common::InnerUser>::Inner; + } + + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::crypto_common::InnerInit for $name$(< $( $lt ),+ >)? { + #[inline] + fn inner_init(inner: Self::Inner) -> Self { + Self { + core: <$core_ty as $crate::crypto_common::InnerInit>::inner_init(inner), + buffer: Default::default(), + } + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `KeySizeUser` and `KeyInit` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + KeyInit $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::crypto_common::KeySizeUser for $name$(< $( $lt ),+ >)? { + type KeySize = <$core_ty as $crate::crypto_common::KeySizeUser>::KeySize; + } + + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::KeyInit for $name$(< $( $lt ),+ >)? { + #[inline] + fn new(key: &$crate::Key) -> Self { + Self { + core: <$core_ty as $crate::KeyInit>::new(key), + buffer: Default::default(), + } + } + + #[inline] + fn new_from_slice(key: &[u8]) -> Result { + <$core_ty as $crate::KeyInit>::new_from_slice(key).map(|core| + Self { core, buffer: Default::default() } + ) + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `Reset` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + Reset $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::Reset for $name$(< $( $lt ),+ >)? { + #[inline] + fn reset(&mut self) { + $crate::Reset::reset(&mut self.core); + self.buffer.reset(); + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `FixedOutputReset` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + FixedOutputReset $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::FixedOutputReset for $name$(< $( $lt ),+ >)? { + #[inline] + fn finalize_into_reset(&mut self, out: &mut $crate::Output) { + let Self { core, buffer } = self; + $crate::block_api::FixedOutputCore::finalize_fixed_core(core, buffer, out); + $crate::Reset::reset(self); + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + }; + + // Implements `SerializableState` + ( + impl_inner: $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + ($core_ty:ty); + SerializableState $($trait_name:ident)*; + ) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::crypto_common::hazmat::SerializableState for $name$(< $( $lt ),+ >)? { + type SerializedStateSize = $crate::typenum::Sum< + <$core_ty as $crate::crypto_common::hazmat::SerializableState>::SerializedStateSize, + $crate::typenum::Add1< + <$core_ty as $crate::block_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)?, + }) + } + } + + $crate::buffer_fixed!(impl_inner: $name$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?($core_ty); $($trait_name)*;); + } +} diff --git a/digest/src/buffer_macros/variable_ct.rs b/digest/src/buffer_macros/variable_ct.rs new file mode 100644 index 000000000..1399ec084 --- /dev/null +++ b/digest/src/buffer_macros/variable_ct.rs @@ -0,0 +1,226 @@ +/// Creates a buffered wrapper around block-level "core" type which implements variable output size traits +/// with output size selected at compile time. +#[macro_export] +macro_rules! buffer_ct_variable { + ( + $(#[$attr:meta])* + $vis:vis struct $name:ident<$out_size:ident>($core_ty:ty); + exclude: SerializableState; + // 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; + ) => { + $(#[$attr])* + $vis struct $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size, Output = $crate::typenum::True>, + { + core: $crate::block_api::CtOutWrapper<$core_ty, $out_size>, + buffer: $crate::block_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, Output = $crate::typenum::True>, + { + #[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, Output = $crate::typenum::True>, + { + #[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, Output = $crate::typenum::True>, + { + #[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, Output = $crate::typenum::True>, + { + #[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, Output = $crate::typenum::True>, + { + #[inline] + fn reset(&mut self) { + $crate::Reset::reset(&mut self.core); + self.buffer.reset(); + } + } + + impl<$out_size> $crate::block_api::BlockSizeUser for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size, Output = $crate::typenum::True>, + { + 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, Output = $crate::typenum::True>, + { + type OutputSize = $out_size; + } + + impl<$out_size> $crate::HashMarker for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size, Output = $crate::typenum::True>, + {} + + // 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::block_api::CoreProxy for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size, Output = $crate::typenum::True>, + { + type Core = $crate::block_api::CtOutWrapper<$core_ty, $out_size>; + } + + impl<$out_size> $crate::Update for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size, Output = $crate::typenum::True>, + { + #[inline] + fn update(&mut self, data: &[u8]) { + let Self { core, buffer } = self; + buffer.digest_blocks(data, |blocks| { + $crate::block_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, Output = $crate::typenum::True>, + { + #[inline] + fn finalize_into(mut self, out: &mut $crate::Output) { + let Self { core, buffer } = &mut self; + $crate::block_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, Output = $crate::typenum::True>, + { + #[inline] + fn finalize_into_reset(&mut self, out: &mut $crate::Output) { + let Self { core, buffer } = self; + $crate::block_api::FixedOutputCore::finalize_fixed_core(core, buffer, out); + $crate::Reset::reset(self); + } + } + }; + ( + $(#[$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; + ) => { + $crate::buffer_ct_variable!( + $(#[$attr])* + $vis struct $name<$out_size>($core_ty); + exclude: SerializableState; + max_size: $max_size; + ); + + impl<$out_size> $crate::crypto_common::hazmat::SerializableState for $name<$out_size> + where + $out_size: $crate::array::ArraySize + $crate::typenum::IsLessOrEqual<$max_size, Output = $crate::typenum::True>, + { + type SerializedStateSize = $crate::typenum::Add1<$crate::typenum::Sum< + < + $crate::block_api::CtOutWrapper<$core_ty, $out_size> + as $crate::crypto_common::hazmat::SerializableState + >::SerializedStateSize, + <$core_ty as $crate::block_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, + block_api::CtOutWrapper, + crypto_common::hazmat::{SerializableState, DeserializeStateError}, + }; + + let (serialized_core, remaining_buffer) = serialized_state + .split_ref::<< + CtOutWrapper<$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/buffer_macros/variable_rt.rs b/digest/src/buffer_macros/variable_rt.rs new file mode 100644 index 000000000..e39ff5adb --- /dev/null +++ b/digest/src/buffer_macros/variable_rt.rs @@ -0,0 +1,188 @@ +/// Creates a buffered wrapper around block-level "core" type which implements variable output size traits +/// with output size selected at run time. +#[macro_export] +macro_rules! buffer_rt_variable { + ( + $(#[$attr:meta])* + $vis:vis struct $name:ident($core_ty:ty); + exclude: SerializableState; + ) => { + $(#[$attr])* + $vis struct $name { + core: $core_ty, + buffer: $crate::block_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::block_api::VariableOutputCore>::new(size).unwrap(); + self.buffer.reset(); + } + } + + impl $crate::block_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::block_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::block_api::VariableOutputCore::finalize_variable_core(core, buffer, &mut full_res); + let n = out.len(); + let m = full_res.len() - n; + use $crate::block_api::TruncSide::{Left, Right}; + let side = <$core_ty as $crate::block_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::block_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::block_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 {} + }; + + ( + $(#[$attr:meta])* + $vis:vis struct $name:ident($core_ty:ty); + ) => { + $crate::buffer_rt_variable!( + $(#[$attr])* + $vis struct $name($core_ty); + exclude: SerializableState; + ); + + 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::block_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/buffer_macros/xof.rs b/digest/src/buffer_macros/xof.rs new file mode 100644 index 000000000..dd635332c --- /dev/null +++ b/digest/src/buffer_macros/xof.rs @@ -0,0 +1,328 @@ +/// Creates a buffered wrapper around block-level "core" type which implements extendable output size traits. +#[macro_export] +macro_rules! buffer_xof { + ( + $(#[$hasher_attr:meta])* + $hasher_vis:vis struct $hasher_name:ident($hasher_core:ty); + $(oid: $oid:literal;)? + impl: $($hasher_trait_name:ident)*; + + $(#[$reader_attr:meta])* + $reader_vis:vis struct $reader_name:ident($reader_core:ty); + impl: $($reader_trait_name:ident)*; + ) => { + $(#[$hasher_attr])* + $hasher_vis struct $hasher_name { + core: $hasher_core, + buffer: $crate::block_api::Buffer<$hasher_core>, + } + + 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::block_api::ExtendableOutputCore>::finalize_xof_core(core, buffer); + let buffer = Default::default(); + Self::Reader { core, buffer } + } + } + + $( + #[cfg(feature = "oid")] + impl $crate::const_oid::AssociatedOid for $hasher_name { + const OID: $crate::const_oid::ObjectIdentifier = + $crate::const_oid::ObjectIdentifier::new_unwrap($oid); + } + )? + + $crate::buffer_xof!( + impl_inner: $hasher_name($hasher_core); + $($hasher_trait_name)*; + ); + + $(#[$reader_attr])* + $reader_vis struct $reader_name { + core: $reader_core, + buffer: $crate::block_buffer::ReadBuffer<<$reader_core as $crate::block_api::BlockSizeUser>::BlockSize>, + } + + 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::block_api::XofReaderCore::read_block(core); + }); + } + } + + $crate::buffer_xof!( + impl_inner: $reader_name($reader_core); + $($reader_trait_name)*; + ); + }; + + // Terminates `impl_inner` sequences. + ( + impl_inner: $name:ident($core_ty:ty); ; + ) => {}; + + // Implements the set of traits common for XOF hashers + ( + impl_inner: $name:ident($core_ty:ty); + XofHasherTraits $($trait_name:ident)*; + ) => { + $crate::buffer_xof!( + impl_inner: $name($core_ty); + Debug AlgorithmName Clone Default BlockSizeUser CoreProxy HashMarker Update SerializableState Reset ExtendableOutputReset $($trait_name)* ; + ); + }; + + // Implements the set of traits common for XOF readers + ( + impl_inner: $name:ident($core_ty:ty); + XofReaderTraits $($trait_name:ident)*; + ) => { + $crate::buffer_xof!( + impl_inner: $name($core_ty); + Debug Clone BlockSizeUser CoreProxy + $($trait_name)*;); + }; + + // Implements `Debug` + ( + impl_inner: $name:ident($core_ty:ty); + Debug $($trait_name:ident)*; + ) => { + impl core::fmt::Debug for $name { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(concat!(stringify!($name), " { ... }")) + } + } + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; + + // Implements `AlgorithmName` + ( + impl_inner: $name:ident($core_ty:ty); + AlgorithmName $($trait_name:ident)*; + ) => { + 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) + } + } + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; + + // Implements `Default` + ( + impl_inner: $name:ident($core_ty:ty); + Default $($trait_name:ident)*; + ) => { + impl Default for $name { + #[inline] + fn default() -> Self { + Self { + core: Default::default(), + buffer: Default::default(), + } + } + } + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; + + // Implements `CustomizedInit` + ( + impl_inner: $name:ident($core_ty:ty); + CustomizedInit $($trait_name:ident)*; + ) => { + impl $crate::CustomizedInit for $name { + #[inline] + fn new_customized(customization: &[u8]) -> Self { + Self { + core: $crate::CustomizedInit::new_customized(customization), + buffer: Default::default(), + } + } + } + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; + + // Implements `Clone` + ( + impl_inner: $name:ident($core_ty:ty); + Clone $($trait_name:ident)*; + ) => { + impl Clone for $name { + #[inline] + fn clone(&self) -> Self { + Self { + core: Clone::clone(&self.core), + buffer: Clone::clone(&self.buffer), + } + } + } + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; + + // Implements `BlockSizeUser` + ( + impl_inner: $name:ident($core_ty:ty); + BlockSizeUser $($trait_name:ident)*; + ) => { + impl $crate::block_api::BlockSizeUser for $name { + type BlockSize = <$core_ty as $crate::crypto_common::BlockSizeUser>::BlockSize; + } + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; + + // Implements `CoreProxy` + ( + impl_inner: $name:ident($core_ty:ty); + CoreProxy $($trait_name:ident)*; + ) => { + impl $crate::block_api::CoreProxy for $name { + type Core = $core_ty; + } + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; + + // Implements `HashMarker` + ( + impl_inner: $name:ident($core_ty:ty); + HashMarker $($trait_name:ident)*; + ) => { + impl $crate::HashMarker for $name {} + + // Verify that `$core_ty` implements `HashMarker` + const _: () = { + fn check(v: &$core_ty) { + v as &dyn $crate::HashMarker; + } + }; + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; + + // Implements `Update` + ( + impl_inner: $name:ident($core_ty:ty); + Update $($trait_name:ident)*; + ) => { + impl $crate::Update for $name { + #[inline] + fn update(&mut self, data: &[u8]) { + let Self { core, buffer } = self; + buffer.digest_blocks(data, |blocks| { + $crate::block_api::UpdateCore::update_blocks(core, blocks) + }); + } + } + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; + + // Implements `Reset` + ( + impl_inner: $name:ident($core_ty:ty); + Reset $($trait_name:ident)*; + ) => { + impl $crate::Reset for $name { + #[inline] + fn reset(&mut self) { + $crate::Reset::reset(&mut self.core); + self.buffer.reset(); + } + } + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; + + // Implements `ExtendableOutputReset` + ( + impl_inner: $name:ident($core_ty:ty); + ExtendableOutputReset $($trait_name:ident)*; + ) => { + impl $crate::ExtendableOutputReset for $name { + #[inline] + fn finalize_xof_reset(&mut self) -> Self::Reader { + let Self { core, buffer } = self; + let core = <$core_ty as $crate::block_api::ExtendableOutputCore>::finalize_xof_core(core, buffer); + $crate::Reset::reset(self); + let buffer = Default::default(); + Self::Reader { core, buffer } + } + } + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; + + // Implements `SerializableState` + ( + impl_inner: $name:ident($core_ty:ty); + SerializableState $($trait_name:ident)*; + ) => { + impl $crate::crypto_common::hazmat::SerializableState for $name { + type SerializedStateSize = $crate::typenum::Sum< + <$core_ty as $crate::crypto_common::hazmat::SerializableState>::SerializedStateSize, + $crate::typenum::Add1< + <$core_ty as $crate::block_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)?, + }) + } + } + + $crate::buffer_xof!(impl_inner: $name($core_ty); $($trait_name)*;); + }; +} diff --git a/digest/src/core_api/rt_variable.rs b/digest/src/core_api/rt_variable.rs deleted file mode 100644 index 6b69f09d9..000000000 --- a/digest/src/core_api/rt_variable.rs +++ /dev/null @@ -1,190 +0,0 @@ -use super::{AlgorithmName, BlockSizeUser, TruncSide, VariableOutputCore}; -#[cfg(feature = "mac")] -use crate::MacMarker; -use crate::{CollisionResistance, HashMarker, InvalidBufferSize}; -use crate::{InvalidOutputSize, Reset, Update, VariableOutput, VariableOutputReset}; -use block_buffer::BlockBuffer; -use core::{ - convert::TryInto, - fmt, - ops::{Add, Sub}, -}; -use crypto_common::{ - AddBlockSize, SubBlockSize, - array::{Array, ArraySize}, - hazmat::{DeserializeStateError, SerializableState, SerializedState, SubSerializedStateSize}, - typenum::{Diff, IsLess, Le, NonZero, Sum, U1, U256, Unsigned}, -}; -#[cfg(feature = "zeroize")] -use zeroize::ZeroizeOnDrop; - -/// Wrapper around [`VariableOutputCore`] which selects output size -/// at run time. -#[derive(Clone)] -pub struct RtVariableCoreWrapper { - core: T, - buffer: BlockBuffer, - output_size: u8, -} - -impl RtVariableCoreWrapper { - #[inline] - fn finalize_dirty(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferSize> { - let Self { - core, - buffer, - output_size, - } = self; - let size_u8 = u8::try_from(out.len()).map_err(|_| InvalidBufferSize)?; - if out.len() > Self::MAX_OUTPUT_SIZE || size_u8 != *output_size { - return Err(InvalidBufferSize); - } - let mut full_res = Default::default(); - core.finalize_variable_core(buffer, &mut full_res); - let n = out.len(); - let m = full_res.len() - n; - match T::TRUNC_SIDE { - TruncSide::Left => out.copy_from_slice(&full_res[..n]), - TruncSide::Right => out.copy_from_slice(&full_res[m..]), - } - Ok(()) - } -} - -impl HashMarker for RtVariableCoreWrapper {} - -#[cfg(feature = "mac")] -impl MacMarker for RtVariableCoreWrapper {} - -impl CollisionResistance for RtVariableCoreWrapper { - type CollisionResistance = T::CollisionResistance; -} - -impl BlockSizeUser for RtVariableCoreWrapper { - type BlockSize = T::BlockSize; -} - -impl Reset for RtVariableCoreWrapper { - #[inline] - fn reset(&mut self) { - self.buffer.reset(); - self.core.reset(); - } -} - -impl Update for RtVariableCoreWrapper { - #[inline] - fn update(&mut self, input: &[u8]) { - let Self { core, buffer, .. } = self; - buffer.digest_blocks(input, |blocks| core.update_blocks(blocks)); - } -} - -impl VariableOutput for RtVariableCoreWrapper { - const MAX_OUTPUT_SIZE: usize = T::OutputSize::USIZE; - - #[inline] - fn new(output_size: usize) -> Result { - let output_size = u8::try_from(output_size).map_err(|_| InvalidOutputSize)?; - let buffer = Default::default(); - T::new(output_size.into()).map(|core| 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<(), InvalidBufferSize> { - self.finalize_dirty(out) - } -} - -impl VariableOutputReset for RtVariableCoreWrapper { - #[inline] - fn finalize_variable_reset(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferSize> { - self.finalize_dirty(out)?; - self.core.reset(); - self.buffer.reset(); - Ok(()) - } -} - -impl Drop for RtVariableCoreWrapper { - #[inline] - fn drop(&mut self) { - #[cfg(feature = "zeroize")] - { - use zeroize::Zeroize; - self.buffer.zeroize(); - self.output_size.zeroize(); - } - } -} - -#[cfg(feature = "zeroize")] -impl ZeroizeOnDrop for RtVariableCoreWrapper {} - -impl fmt::Debug for RtVariableCoreWrapper { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - T::write_alg_name(f)?; - f.write_str(" { .. }") - } -} - -type RtVariableCoreWrapperSerializedStateSize = - Sum::SerializedStateSize, U1>, T>, U1>; - -impl SerializableState for RtVariableCoreWrapper -where - T: VariableOutputCore + SerializableState, - T::BlockSize: IsLess, - Le: NonZero, - T::SerializedStateSize: Add, - Sum: Add + ArraySize, - AddBlockSize, T>: Add + ArraySize, - RtVariableCoreWrapperSerializedStateSize: Sub + ArraySize, - SubSerializedStateSize, T>: Sub + ArraySize, - Diff, T>, U1>: - Sub + ArraySize, - SubBlockSize< - Diff, T>, U1>, - T, - >: ArraySize, -{ - type SerializedStateSize = RtVariableCoreWrapperSerializedStateSize; - - fn serialize(&self) -> SerializedState { - let serialized_core = self.core.serialize(); - let serialized_pos = Array([self.buffer.get_pos().try_into().unwrap()]); - let serialized_data = self.buffer.clone().pad_with_zeros(); - let serialized_output_size = Array([self.output_size]); - - serialized_core - .concat(serialized_pos) - .concat(serialized_data) - .concat(serialized_output_size) - } - - fn deserialize( - serialized_state: &SerializedState, - ) -> Result { - let (serialized_core, remaining_buffer) = - serialized_state.split_ref::(); - let (serialized_pos, remaining_buffer) = remaining_buffer.split_ref::(); - let (serialized_data, serialized_output_size) = - remaining_buffer.split_ref::(); - - Ok(Self { - core: T::deserialize(serialized_core)?, - buffer: BlockBuffer::try_new(&serialized_data[..serialized_pos[0].into()]) - .map_err(|_| DeserializeStateError)?, - output_size: serialized_output_size[0], - }) - } -} diff --git a/digest/src/core_api/wrapper.rs b/digest/src/core_api/wrapper.rs deleted file mode 100644 index c7368d8a2..000000000 --- a/digest/src/core_api/wrapper.rs +++ /dev/null @@ -1,243 +0,0 @@ -use super::{ - AlgorithmName, BufferKindUser, ExtendableOutputCore, FixedOutputCore, OutputSizeUser, Reset, - UpdateCore, XofReaderCoreWrapper, -}; -use crate::{ - CollisionResistance, CustomizedInit, ExtendableOutput, ExtendableOutputReset, FixedOutput, - FixedOutputReset, HashMarker, Update, -}; -use block_buffer::BlockBuffer; -use core::{ - convert::TryInto, - fmt, - ops::{Add, Sub}, -}; -use crypto_common::{ - BlockSizeUser, InvalidLength, Key, KeyInit, KeySizeUser, Output, - array::{Array, ArraySize}, - hazmat::{DeserializeStateError, SerializableState, SerializedState, SubSerializedStateSize}, - typenum::{Diff, IsLess, Le, NonZero, Sum, U1, U256}, -}; - -#[cfg(feature = "mac")] -use crate::MacMarker; -#[cfg(feature = "oid")] -use const_oid::{AssociatedOid, ObjectIdentifier}; - -/// Wrapper around [`BufferKindUser`]. -/// -/// It handles data buffering and implements the slice-based traits. -#[derive(Clone, Default)] -pub struct CoreWrapper { - core: T, - buffer: BlockBuffer, -} - -impl HashMarker for CoreWrapper {} - -#[cfg(feature = "mac")] -impl MacMarker for CoreWrapper {} - -impl CollisionResistance for CoreWrapper { - type CollisionResistance = T::CollisionResistance; -} - -// this blanket impl is needed for HMAC -impl BlockSizeUser for CoreWrapper { - type BlockSize = T::BlockSize; -} - -impl CoreWrapper { - /// Create new wrapper from `core`. - #[inline] - pub fn from_core(core: T) -> Self { - let buffer = Default::default(); - Self { core, buffer } - } -} - -impl KeySizeUser for CoreWrapper { - type KeySize = T::KeySize; -} - -impl KeyInit for CoreWrapper { - #[inline] - fn new(key: &Key) -> Self { - Self { - core: T::new(key), - buffer: Default::default(), - } - } - - #[inline] - fn new_from_slice(key: &[u8]) -> Result { - Ok(Self { - core: T::new_from_slice(key)?, - buffer: Default::default(), - }) - } -} - -impl fmt::Debug for CoreWrapper { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - T::write_alg_name(f)?; - f.write_str(" { .. }") - } -} - -impl Reset for CoreWrapper { - #[inline] - fn reset(&mut self) { - self.core.reset(); - self.buffer.reset(); - } -} - -impl Update for CoreWrapper { - #[inline] - fn update(&mut self, input: &[u8]) { - let Self { core, buffer } = self; - buffer.digest_blocks(input, |blocks| core.update_blocks(blocks)); - } -} - -impl OutputSizeUser for CoreWrapper { - type OutputSize = T::OutputSize; -} - -impl FixedOutput for CoreWrapper { - #[inline] - fn finalize_into(mut self, out: &mut Output) { - let Self { core, buffer } = &mut self; - core.finalize_fixed_core(buffer, out); - } -} - -impl FixedOutputReset for CoreWrapper { - #[inline] - fn finalize_into_reset(&mut self, out: &mut Output) { - let Self { core, buffer } = self; - core.finalize_fixed_core(buffer, out); - core.reset(); - buffer.reset(); - } -} - -impl ExtendableOutput for CoreWrapper { - type Reader = XofReaderCoreWrapper; - - #[inline] - fn finalize_xof(mut self) -> Self::Reader { - Self::Reader { - core: self.core.finalize_xof_core(&mut self.buffer), - buffer: Default::default(), - } - } -} - -impl ExtendableOutputReset for CoreWrapper { - #[inline] - fn finalize_xof_reset(&mut self) -> Self::Reader { - let Self { core, buffer } = self; - let reader_core = core.finalize_xof_core(buffer); - core.reset(); - buffer.reset(); - let buffer = Default::default(); - Self::Reader { - core: reader_core, - buffer, - } - } -} - -impl Drop for CoreWrapper { - #[inline] - fn drop(&mut self) { - #[cfg(feature = "zeroize")] - { - use zeroize::Zeroize; - self.buffer.zeroize(); - } - } -} - -#[cfg(feature = "zeroize")] -impl zeroize::ZeroizeOnDrop for CoreWrapper {} - -impl CustomizedInit for CoreWrapper -where - T: BufferKindUser + CustomizedInit, -{ - #[inline] - fn new_customized(customization: &[u8]) -> Self { - Self::from_core(T::new_customized(customization)) - } -} - -#[cfg(feature = "oid")] -impl AssociatedOid for CoreWrapper -where - T: BufferKindUser + AssociatedOid, -{ - const OID: ObjectIdentifier = T::OID; -} - -type CoreWrapperSerializedStateSize = - Sum::SerializedStateSize, U1>, ::BlockSize>; - -impl SerializableState for CoreWrapper -where - T: BufferKindUser + SerializableState, - T::BlockSize: IsLess, - Le: NonZero, - T::SerializedStateSize: Add, - Sum: Add + ArraySize, - CoreWrapperSerializedStateSize: Sub + ArraySize, - SubSerializedStateSize, T>: Sub + ArraySize, - Diff, T>, U1>: ArraySize, -{ - type SerializedStateSize = CoreWrapperSerializedStateSize; - - fn serialize(&self) -> SerializedState { - let serialized_core = self.core.serialize(); - let serialized_pos = Array([self.buffer.get_pos().try_into().unwrap()]); - let serialized_data = self.buffer.clone().pad_with_zeros(); - - serialized_core - .concat(serialized_pos) - .concat(serialized_data) - } - - fn deserialize( - serialized_state: &SerializedState, - ) -> Result { - let (serialized_core, remaining_buffer) = - serialized_state.split_ref::(); - let (serialized_pos, serialized_data) = remaining_buffer.split_ref::(); - - Ok(Self { - core: T::deserialize(serialized_core)?, - buffer: BlockBuffer::try_new(&serialized_data[..serialized_pos[0].into()]) - .map_err(|_| DeserializeStateError)?, - }) - } -} - -/// 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 { - /// 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/core_api/xof_reader.rs b/digest/src/core_api/xof_reader.rs deleted file mode 100644 index 0bb63933c..000000000 --- a/digest/src/core_api/xof_reader.rs +++ /dev/null @@ -1,51 +0,0 @@ -use super::{AlgorithmName, XofReaderCore}; -use crate::XofReader; -use block_buffer::ReadBuffer; -use core::fmt; - -/// Wrapper around [`XofReaderCore`] implementations. -/// -/// It handles data buffering and implements the mid-level traits. -#[derive(Clone, Default)] -pub struct XofReaderCoreWrapper -where - T: XofReaderCore, -{ - pub(super) core: T, - pub(super) buffer: ReadBuffer, -} - -impl fmt::Debug for XofReaderCoreWrapper -where - T: XofReaderCore + AlgorithmName, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - T::write_alg_name(f)?; - f.write_str(" { .. }") - } -} - -impl XofReader for XofReaderCoreWrapper -where - T: XofReaderCore, -{ - #[inline] - fn read(&mut self, buffer: &mut [u8]) { - let Self { core, buffer: buf } = self; - buf.read(buffer, |block| *block = core.read_block()); - } -} - -impl Drop for XofReaderCoreWrapper { - #[inline] - fn drop(&mut self) { - #[cfg(feature = "zeroize")] - { - use zeroize::Zeroize; - self.buffer.zeroize(); - } - } -} - -#[cfg(feature = "zeroize")] -impl zeroize::ZeroizeOnDrop for XofReaderCoreWrapper {} diff --git a/digest/src/lib.rs b/digest/src/lib.rs index e8cb7b141..91ab8fb83 100644 --- a/digest/src/lib.rs +++ b/digest/src/lib.rs @@ -12,7 +12,7 @@ //! traits atomically describe available functionality of an algorithm. //! - **Marker traits**: [`HashMarker`], [`MacMarker`]. Used to distinguish //! different algorithm classes. -//! - **Low-level traits** defined in the [`core_api`] module. These traits +//! - **Low-level traits** defined in the [`block_api`] module. These traits //! operate at a block-level and do not contain any built-in buffering. //! They are intended to be implemented by low-level algorithm providers only. //! Usually they should not be used in application-level code. @@ -50,14 +50,15 @@ use alloc::boxed::Box; #[cfg(feature = "dev")] pub mod dev; -#[cfg(feature = "core-api")] -pub mod core_api; +#[cfg(feature = "block-api")] +pub mod block_api; +mod buffer_macros; mod digest; #[cfg(feature = "mac")] mod mac; mod xof_fixed; -#[cfg(feature = "core-api")] +#[cfg(feature = "block-api")] pub use block_buffer; #[cfg(feature = "oid")] pub use const_oid; diff --git a/digest/tests/dummy_fixed.rs b/digest/tests/dummy_fixed.rs new file mode 100644 index 000000000..cbec6aced --- /dev/null +++ b/digest/tests/dummy_fixed.rs @@ -0,0 +1,101 @@ +#![cfg(feature = "block-api")] + +mod block_api { + use core::fmt; + use digest::{ + HashMarker, Output, OutputSizeUser, Reset, + block_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore, + UpdateCore, + }, + consts::U8, + 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::buffer_fixed!( + /// Primitive XOR hasher for testing purposes + pub struct FixedHash(block_api::FixedHashCore); + impl: BaseFixedTraits Default Clone HashMarker Reset FixedOutputReset; +); +digest::buffer_fixed!( + /// Primitive XOR hasher for testing purposes + pub struct FixedHashWithSer(block_api::FixedHashCore); + impl: FixedHashTraits; +); +digest::buffer_fixed!( + /// Primitive XOR hasher for testing purposes + pub struct FixedHashWithOid(block_api::FixedHashCore); + oid: "0.1.2.3.4.5"; + impl: BaseFixedTraits Default Clone HashMarker Reset FixedOutputReset; +); +digest::buffer_fixed!( + /// Primitive XOR hasher for testing purposes + pub struct FixedHashWithOidSer(block_api::FixedHashCore); + oid: "0.1.2.3.4.5"; + impl: FixedHashTraits; +);