diff --git a/Cargo.lock b/Cargo.lock index 5a9cbc10..93b22438 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,9 +72,9 @@ dependencies = [ [[package]] name = "blobby" -version = "0.4.0" +version = "0.4.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89af0b093cc13baa4e51e64e65ec2422f7e73aea0e612e5ad3872986671622f1" +checksum = "e4db6eec520c1f3d3b060c5b67527c3d1d4122d9f0ff47edf0149a7ffa34cebb" [[package]] name = "block-buffer" @@ -88,9 +88,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "const-oid" @@ -166,15 +166,15 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-literal" -version = "1.1.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" +checksum = "bcaaec4551594c969335c98c903c1397853d4198408ea609190f420500f6be71" [[package]] name = "hybrid-array" -version = "0.4.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0" +checksum = "6fe39a812f039072707ce38020acbab2f769087952eddd9e2b890f37654b2349" dependencies = [ "typenum", ] @@ -218,9 +218,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "md-5" @@ -261,18 +261,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -380,9 +380,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.108" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -409,15 +409,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.20" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "whirlpool" @@ -430,18 +430,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", diff --git a/blake2/CHANGELOG.md b/blake2/CHANGELOG.md index bd478a3f..d3a61634 100644 --- a/blake2/CHANGELOG.md +++ b/blake2/CHANGELOG.md @@ -17,11 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - `std` crate feature ([#678]) -- `Blake2bVar` and `Blake2sVar` types ([#744]) [#652]: https://github.com/RustCrypto/hashes/pull/652 [#678]: https://github.com/RustCrypto/hashes/pull/678 -[#744]: https://github.com/RustCrypto/hashes/pull/744 ## 0.10.6 (2022-12-16) ### Added diff --git a/blake2/README.md b/blake2/README.md index 8e4c3e9e..88010be1 100644 --- a/blake2/README.md +++ b/blake2/README.md @@ -45,8 +45,22 @@ Also, see the [examples section] in the RustCrypto/hashes readme. ### Variable output size -This implementation supports output sizes variable at compile time: +This implementation supports run and compile time variable sizes. +Output size set at run time: +```rust +use blake2::Blake2bVar; +use blake2::digest::Update; +use hex_literal::hex; + +let mut hasher = Blake2bVar::new(10).unwrap(); +hasher.update(b"my_input"); +let mut buf = [0u8; 10]; +hasher.finalize_variable(&mut buf).unwrap(); +assert_eq!(buf, hex!("2cc55c84e416924e6400")); +``` + +Output size set at compile time: ```rust use blake2::{Blake2b, Digest, digest::consts::U10}; use hex_literal::hex; diff --git a/blake2/src/lib.rs b/blake2/src/lib.rs index 54b62160..1234f4a4 100644 --- a/blake2/src/lib.rs +++ b/blake2/src/lib.rs @@ -79,6 +79,12 @@ where } } +buffer_rt_variable!( + /// BLAKE2b which allows to choose output size at runtime. + pub struct Blake2bVar(Blake2bVarCore); + exclude: SerializableState; +); + /// BLAKE2b-128 hasher state. pub type Blake2b128 = Blake2b; /// BLAKE2b-256 hasher state. @@ -127,6 +133,12 @@ where } } +buffer_rt_variable!( + /// BLAKE2s which allows to choose output size at runtime. + pub struct Blake2sVar(Blake2sVarCore); + exclude: SerializableState; +); + /// BLAKE2s-128 hasher state. pub type Blake2s128 = Blake2s; /// BLAKE2s-256 hasher state. diff --git a/blake2/src/macros.rs b/blake2/src/macros.rs index a5d0af50..120bc6f3 100644 --- a/blake2/src/macros.rs +++ b/blake2/src/macros.rs @@ -472,3 +472,195 @@ macro_rules! blake2_mac_impl { } }; } + +/// 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: digest::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 digest::crypto_common::AlgorithmName for $name { + #[inline] + fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + <$core_ty as digest::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 digest::Reset for $name { + #[inline] + fn reset(&mut self) { + let size = self.output_size.into(); + self.core = <$core_ty as digest::block_api::VariableOutputCore>::new(size).unwrap(); + self.buffer.reset(); + } + } + + impl digest::block_api::BlockSizeUser for $name { + type BlockSize = <$core_ty as digest::crypto_common::BlockSizeUser>::BlockSize; + } + + impl digest::HashMarker for $name {} + + impl digest::Update for $name { + #[inline] + fn update(&mut self, data: &[u8]) { + let Self { core, buffer, .. } = self; + buffer.digest_blocks(data, |blocks| { + digest::block_api::UpdateCore::update_blocks(core, blocks); + }); + } + } + + impl $name { + /// Maximum size of output hash in bytes. + pub const MAX_OUTPUT_SIZE: usize = < + <$core_ty as digest::block_api::OutputSizeUser>::OutputSize + as digest::typenum::Unsigned + >::USIZE; + + /// Create new hasher instance with the given output size in bytes. + /// + /// It will return `Err(InvalidOutputSize)` in case if hasher can not return + /// hash of the specified output size. + #[inline] + pub fn new(output_size: usize) -> Result { + let output_size = u8::try_from(output_size).map_err(|_| digest::InvalidOutputSize)?; + let buffer = Default::default(); + let core = <$core_ty as digest::block_api::VariableOutputCore>::new(output_size.into())?; + Ok(Self { + core, + buffer, + output_size, + }) + } + + /// Get output size in bytes of the hasher instance provided to the `new` method + #[inline] + pub fn output_size(&self) -> usize { + self.output_size.into() + } + + /// Write result into the output buffer. + /// + /// Returns `Err(InvalidOutputSize)` if `out` size is not equal to + /// `self.output_size()`. + #[inline] + pub fn finalize_variable(mut self, out: &mut [u8]) -> Result<(), digest::InvalidBufferSize> { + self.finalize_dirty(out) + } + + /// Write result into the output buffer and reset the hasher state. + /// + /// Returns `Err(InvalidOutputSize)` if `out` size is not equal to + /// `self.output_size()`. + #[inline] + pub fn finalize_variable_reset( + &mut self, + out: &mut [u8], + ) -> Result<(), digest::InvalidBufferSize> { + self.finalize_dirty(out)?; + digest::Reset::reset(self); + Ok(()) + } + + #[inline] + fn finalize_dirty(&mut self, out: &mut [u8]) -> Result<(), digest::InvalidBufferSize> { + let Self { + core, + buffer, + output_size, + } = self; + let size_u8 = u8::try_from(out.len()).map_err(|_| digest::InvalidBufferSize)?; + + let max_size = Self::MAX_OUTPUT_SIZE; + if out.len() > max_size || size_u8 != *output_size { + return Err(digest::InvalidBufferSize); + } + let mut full_res = Default::default(); + digest::block_api::VariableOutputCore::finalize_variable_core(core, buffer, &mut full_res); + let n = out.len(); + let m = full_res.len() - n; + use digest::block_api::TruncSide::{Left, Right}; + let side = <$core_ty as digest::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 Drop for $name { + #[inline] + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + { + use digest::zeroize::Zeroize; + self.buffer.zeroize(); + self.output_size.zeroize(); + } + } + } + + #[cfg(feature = "zeroize")] + impl digest::zeroize::ZeroizeOnDrop for $name {} + }; + + ( + $(#[$attr:meta])* + $vis:vis struct $name:ident($core_ty:ty); + ) => { + buffer_rt_variable!( + $(#[$attr])* + $vis struct $name($core_ty); + exclude: SerializableState; + ); + + impl digest::crypto_common::hazmat::SerializableState for $name { + type SerializedStateSize = digest::typenum::Add1::SerializedStateSize, + <$core_ty as digest::block_api::BlockSizeUser>::BlockSize, + >>; + + #[inline] + fn serialize(&self) -> digest::crypto_common::hazmat::SerializedState { + todo!() + } + + #[inline] + fn deserialize( + serialized_state: &digest::crypto_common::hazmat::SerializedState, + ) -> Result { + todo!() + } + } + }; +} diff --git a/blake2/tests/mod.rs b/blake2/tests/mod.rs index e3988bdc..6d9a9fc4 100644 --- a/blake2/tests/mod.rs +++ b/blake2/tests/mod.rs @@ -5,3 +5,7 @@ use digest::dev::fixed_test; use digest::new_test; new_test!(blake2b_kat, blake2::Blake2b512, fixed_test); + +// TODO(tarcieri): port tests over from the `digest` crate +// new_test!(blake2b_variable_kat, blake2::Blake2bVar, variable_test); +// new_test!(blake2s_variable_kat, blake2::Blake2sVar, variable_test);