diff --git a/Cargo.lock b/Cargo.lock index 14c302bdb..baa72179d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ name = "aead" version = "0.3.2" dependencies = [ - "blobby 0.3.0", + "blobby", "generic-array 0.14.4", "heapless", ] @@ -57,12 +57,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "blobby" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "517e75eae2ab6547f6a32ca5eefce861ecb5ec4c57a4c015e82503f71a7c63a9" - [[package]] name = "blobby" version = "0.3.0" @@ -78,6 +72,15 @@ dependencies = [ "generic-array 0.14.4", ] +[[package]] +name = "block-buffer" +version = "0.10.0-pre" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5ae77a4961d75e3e4505bbe155705bda23ff5948209c8face08f75c2cd52a0" +dependencies = [ + "generic-array 0.14.4", +] + [[package]] name = "byteorder" version = "1.3.4" @@ -94,7 +97,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "cipher" version = "0.3.0-pre.4" dependencies = [ - "blobby 0.3.0", + "blobby", "generic-array 0.14.4", ] @@ -117,7 +120,7 @@ dependencies = [ "aead", "cipher", "crypto-mac", - "digest 0.9.0", + "digest 0.10.0-pre", "elliptic-curve", "signature", "universal-hash", @@ -127,7 +130,7 @@ dependencies = [ name = "crypto-mac" version = "0.11.0-pre" dependencies = [ - "blobby 0.3.0", + "blobby", "cipher", "generic-array 0.14.4", "subtle", @@ -145,17 +148,18 @@ dependencies = [ [[package]] name = "digest" version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "blobby 0.2.0", "generic-array 0.14.4", ] [[package]] name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +version = "0.10.0-pre" dependencies = [ + "blobby", + "block-buffer 0.10.0-pre", "generic-array 0.14.4", ] @@ -164,7 +168,7 @@ name = "elliptic-curve" version = "0.9.0-pre" dependencies = [ "bitvec", - "digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.9.0", "ff", "generic-array 0.14.4", "group", @@ -343,10 +347,10 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpuid-bool", - "digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.9.0", "opaque-debug", ] @@ -354,7 +358,7 @@ dependencies = [ name = "signature" version = "1.3.0" dependencies = [ - "digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.9.0", "hex-literal 0.2.1", "rand_core", "sha2", diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 26c5c9332..efa129c3c 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -14,7 +14,7 @@ edition = "2018" [dependencies] aead = { version = "0.3", optional = true, path = "../aead" } cipher = { version = "=0.3.0-pre.4", optional = true, path = "../cipher" } -digest = { version = "0.9", optional = true, path = "../digest" } +digest = { version = "0.10.0-pre", optional = true, path = "../digest" } elliptic-curve = { version = "=0.9.0-pre", optional = true, path = "../elliptic-curve" } mac = { version = "=0.11.0-pre", package = "crypto-mac", optional = true, path = "../crypto-mac" } signature = { version = "1.3.0", optional = true, default-features = false, path = "../signature" } diff --git a/digest/CHANGELOG.md b/digest/CHANGELOG.md index 03f0ad387..0b8547c6f 100644 --- a/digest/CHANGELOG.md +++ b/digest/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.0 (2021-01-18) +### Breaking changes +- Dirty traits are removed and instead block-level traits are introduced. +Variable output traits are removed as well in favor of fixed output tratis, +implementors of variable output hashes are expected to be generic over +output size. ([#380]) + +[#380]: https://github.com/RustCrypto/traits/pull/380 + ## 0.9.0 (2020-06-09) ### Added - `ExtendableOutputDirty` and `VariableOutputDirty` traits ([#183]) diff --git a/digest/Cargo.toml b/digest/Cargo.toml index 5c0652e08..e41667c40 100644 --- a/digest/Cargo.toml +++ b/digest/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "digest" description = "Traits for cryptographic hash functions" -version = "0.9.0" +version = "0.10.0-pre" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -13,12 +13,14 @@ categories = ["cryptography", "no-std"] [dependencies] generic-array = "0.14" -blobby = { version = "0.2", optional = true } +blobby = { version = "0.3", optional = true } +block-buffer = { version = "0.10.0-pre", optional = true } [features] alloc = [] std = ["alloc"] dev = ["blobby"] +core-api = ["block-buffer"] [package.metadata.docs.rs] all-features = true diff --git a/digest/src/core_api.rs b/digest/src/core_api.rs new file mode 100644 index 000000000..8201ced45 --- /dev/null +++ b/digest/src/core_api.rs @@ -0,0 +1,164 @@ +use crate::{ExtendableOutput, FixedOutput, Reset, Update, XofReader}; +use block_buffer::BlockBuffer; +use generic_array::{ArrayLength, GenericArray}; + +/// Trait for updating hasher state with input data divided into blocks. +pub trait UpdateCore { + /// Block size in bytes. + type BlockSize: ArrayLength; + + /// Update the hasher state using the provided data. + fn update_blocks(&mut self, blocks: &[GenericArray]); +} + +/// Trait for fixed-output digest implementations to use to retrieve the +/// hash output. +/// +/// Usage of this trait in user code is discouraged. Instead use core algorithm +/// wrapped by [`crate::CoreWrapper`], which implements the [`FixedOutput`] +/// trait. +pub trait FixedOutputCore: crate::UpdateCore { + /// Digest output size in bytes. + type OutputSize: ArrayLength; + + /// Retrieve result into provided buffer using remaining data stored + /// in the block buffer and leave hasher in a dirty state. + /// + /// This method is expected to only be called once unless [`Reset::reset`] + /// is called, after which point it can be called again and reset again + /// (and so on). + fn finalize_fixed_core( + &mut self, + buffer: &mut block_buffer::BlockBuffer, + out: &mut GenericArray, + ); +} + +/// Trait for extendable-output function (XOF) core implementations to use to +/// retrieve the hash output. +/// +/// Usage of this trait in user code is discouraged. Instead use core algorithm +/// wrapped by [`crate::CoreWrapper`], which implements the +/// [`ExtendableOutput`] trait. +pub trait ExtendableOutputCore: crate::UpdateCore { + /// XOF reader core state. + type ReaderCore: XofReaderCore; + + /// Retrieve XOF reader using remaining data stored in the block buffer + /// and leave hasher in a dirty state. + /// + /// This method is expected to only be called once unless [`Reset::reset`] + /// is called, after which point it can be called again and reset again + /// (and so on). + fn finalize_xof_core( + &mut self, + buffer: &mut block_buffer::BlockBuffer, + ) -> Self::ReaderCore; +} + +/// Core reader trait for extendable-output function (XOF) result. +pub trait XofReaderCore { + /// Block size in bytes. + type BlockSize: ArrayLength; + + /// Read next XOF block. + fn read_block(&mut self) -> GenericArray; +} + +/// Wrapper around core trait implementations. +/// +/// It handles data buffering and implements the mid-level traits. +#[derive(Clone, Default)] +pub struct CoreWrapper> { + core: C, + buffer: BlockBuffer, +} + +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 FixedOutput for CoreWrapper { + type OutputSize = D::OutputSize; + + #[inline] + fn finalize_into(mut self, out: &mut GenericArray) { + let Self { core, buffer } = &mut self; + core.finalize_fixed_core(buffer, out); + } + + #[inline] + fn finalize_into_reset(&mut self, out: &mut GenericArray) { + let Self { core, buffer } = self; + core.finalize_fixed_core(buffer, out); + self.reset(); + } +} + +impl XofReader for CoreWrapper { + #[inline] + fn read(&mut self, buffer: &mut [u8]) { + let Self { core, buffer: buf } = self; + buf.set_data(buffer, || core.read_block()); + } +} + +impl ExtendableOutput for CoreWrapper { + type Reader = CoreWrapper::BlockSize>; + + #[inline] + fn finalize_xof(mut self) -> Self::Reader { + let Self { core, buffer } = &mut self; + let reader_core = core.finalize_xof_core(buffer); + CoreWrapper { + core: reader_core, + buffer: Default::default(), + } + } + + #[inline] + fn finalize_xof_reset(&mut self) -> Self::Reader { + let Self { core, buffer } = self; + let reader_core = core.finalize_xof_core(buffer); + self.reset(); + CoreWrapper { + core: reader_core, + buffer: Default::default(), + } + } +} + +#[cfg(feature = "std")] +impl std::io::Write for CoreWrapper { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result { + Update::update(self, buf); + Ok(buf.len()) + } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +#[cfg(feature = "std")] +impl std::io::Read for CoreWrapper { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + XofReader::read(self, buf); + Ok(buf.len()) + } +} diff --git a/digest/src/dev.rs b/digest/src/dev.rs index c205cab43..f3f8e2208 100644 --- a/digest/src/dev.rs +++ b/digest/src/dev.rs @@ -2,7 +2,7 @@ pub use blobby; -use super::{ExtendableOutput, Reset, Update, VariableOutput, XofReader}; +use super::{ExtendableOutput, Reset, Update, XofReader}; use core::fmt::Debug; /// Define test @@ -15,9 +15,7 @@ macro_rules! new_test { use digest::dev::blobby::Blob2Iterator; let data = include_bytes!(concat!("data/", $test_name, ".blb")); - for (i, row) in Blob2Iterator::new(data).unwrap().enumerate() { - let input = row[0]; - let output = row[1]; + for (i, [input, output]) in Blob2Iterator::new(data).unwrap().enumerate() { if let Some(desc) = $test_func::<$hasher>(input, output) { panic!( "\n\ @@ -165,56 +163,6 @@ where None } -/// Variable-output digest test -pub fn variable_test(input: &[u8], output: &[u8]) -> Option<&'static str> -where - D: Update + VariableOutput + Reset + Debug + Clone, -{ - let mut hasher = D::new(output.len()).unwrap(); - let mut buf = [0u8; 128]; - let buf = &mut buf[..output.len()]; - // Test that it works when accepting the message all at once - hasher.update(input); - let mut hasher2 = hasher.clone(); - hasher.finalize_variable(|res| buf.copy_from_slice(res)); - if buf != output { - return Some("whole message"); - } - - // Test if reset works correctly - hasher2.reset(); - hasher2.update(input); - hasher2.finalize_variable(|res| buf.copy_from_slice(res)); - if buf != output { - return Some("whole message after reset"); - } - - // Test that it works when accepting the message in pieces - let mut hasher = D::new(output.len()).unwrap(); - let len = input.len(); - let mut left = len; - while left > 0 { - let take = (left + 1) / 2; - hasher.update(&input[len - left..take + len - left]); - left -= take; - } - hasher.finalize_variable(|res| buf.copy_from_slice(res)); - if buf != output { - return Some("message in pieces"); - } - - // Test processing byte-by-byte - let mut hasher = D::new(output.len()).unwrap(); - for chunk in input.chunks(1) { - hasher.update(chunk) - } - hasher.finalize_variable(|res| buf.copy_from_slice(res)); - if buf != output { - return Some("message byte-by-byte"); - } - None -} - /// Define benchmark #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] diff --git a/digest/src/digest.rs b/digest/src/digest.rs index ef72951d8..674a4e636 100644 --- a/digest/src/digest.rs +++ b/digest/src/digest.rs @@ -32,6 +32,12 @@ pub trait Digest { /// re-creation. fn finalize_reset(&mut self) -> Output; + /// Write result into provided array and consume the hasher instance. + fn finalize_into(self, out: &mut GenericArray); + + /// Write result into provided array and reset the hasher instance. + fn finalize_into_reset(&mut self, out: &mut GenericArray); + /// Reset hasher instance to its initial state. fn reset(&mut self); @@ -52,39 +58,56 @@ pub trait Digest { impl Digest for D { type OutputSize = ::OutputSize; + #[inline] fn new() -> Self { Self::default() } + #[inline] fn update(&mut self, data: impl AsRef<[u8]>) { - Update::update(self, data); + Update::update(self, data.as_ref()); } - fn chain(self, data: impl AsRef<[u8]>) -> Self + #[inline] + fn chain(mut self, data: impl AsRef<[u8]>) -> Self where Self: Sized, { - Update::chain(self, data) + Update::update(&mut self, data.as_ref()); + self } + #[inline] fn finalize(self) -> Output { self.finalize_fixed() } + #[inline] fn finalize_reset(&mut self) -> Output { - let res = self.clone().finalize_fixed(); - self.reset(); - res + self.finalize_fixed_reset() + } + + #[inline] + fn finalize_into(self, out: &mut Output) { + self.finalize_into(out); + } + + #[inline] + fn finalize_into_reset(&mut self, out: &mut Output) { + self.finalize_into_reset(out); } + #[inline] fn reset(&mut self) { - ::reset(self) + Reset::reset(self) } + #[inline] fn output_size() -> usize { Self::OutputSize::to_usize() } + #[inline] fn digest(data: &[u8]) -> Output { let mut hasher = Self::default(); Update::update(&mut hasher, data); @@ -92,5 +115,5 @@ impl Digest for D { } } -/// Output of a [`Digest`] function +/// Fixed of fixed-sized hash-function used by [`Digest`] methods. pub type Output = GenericArray::OutputSize>; diff --git a/digest/src/dyn_digest.rs b/digest/src/dyn_digest.rs index 156f9bdbf..f6936badd 100644 --- a/digest/src/dyn_digest.rs +++ b/digest/src/dyn_digest.rs @@ -1,8 +1,8 @@ -#![cfg(feature = "alloc")] use alloc::boxed::Box; +use core::fmt; use super::{FixedOutput, Reset, Update}; -use generic_array::typenum::Unsigned; +use generic_array::{typenum::Unsigned, GenericArray}; /// The `DynDigest` trait is a modification of `Digest` trait suitable /// for trait objects. @@ -19,6 +19,16 @@ pub trait DynDigest { /// Retrieve result and consume boxed hasher instance fn finalize(self: Box) -> Box<[u8]>; + /// Write result into provided array and consume the hasher instance. + /// + /// Returns error if buffer length is not equal to `output_size`. + fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferLength>; + + /// Write result into provided array and reset the hasher instance. + /// + /// Returns error if buffer length is not equal to `output_size`. + fn finalize_into_reset(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferLength>; + /// Reset hasher instance to its initial state. fn reset(&mut self); @@ -35,15 +45,31 @@ impl DynDigest for D { } fn finalize_reset(&mut self) -> Box<[u8]> { - let res = self.finalize_fixed_reset().to_vec().into_boxed_slice(); - Reset::reset(self); - res + self.finalize_fixed_reset().to_vec().into_boxed_slice() } fn finalize(self: Box) -> Box<[u8]> { self.finalize_fixed().to_vec().into_boxed_slice() } + fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferLength> { + if buf.len() == self.output_size() { + self.finalize_into(GenericArray::from_mut_slice(buf)); + Ok(()) + } else { + Err(InvalidBufferLength) + } + } + + fn finalize_into_reset(&mut self, buf: &mut [u8]) -> Result<(), InvalidBufferLength> { + if buf.len() == self.output_size() { + self.finalize_into_reset(GenericArray::from_mut_slice(buf)); + Ok(()) + } else { + Err(InvalidBufferLength) + } + } + fn reset(&mut self) { Reset::reset(self); } @@ -62,3 +88,17 @@ impl Clone for Box { self.box_clone() } } + +/// Buffer length is not equal to the hash output size. +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] +pub struct InvalidBufferLength; + +impl fmt::Display for InvalidBufferLength { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("invalid buffer length") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidBufferLength {} diff --git a/digest/src/errors.rs b/digest/src/errors.rs deleted file mode 100644 index 6071e456b..000000000 --- a/digest/src/errors.rs +++ /dev/null @@ -1,14 +0,0 @@ -use core::fmt; - -/// The error type for variable hasher initialization -#[derive(Clone, Copy, Debug, Default)] -pub struct InvalidOutputSize; - -impl fmt::Display for InvalidOutputSize { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("invalid output size") - } -} - -#[cfg(feature = "std")] -impl std::error::Error for InvalidOutputSize {} diff --git a/digest/src/fixed.rs b/digest/src/fixed.rs deleted file mode 100644 index 432a6c42c..000000000 --- a/digest/src/fixed.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Fixed-size output digest support - -use crate::Reset; -use generic_array::{ArrayLength, GenericArray}; - -/// Trait for returning digest result with the fixed size -pub trait FixedOutput { - /// Output size for fixed output digest - type OutputSize: ArrayLength; - - /// Write result into provided array and consume the hasher instance. - fn finalize_into(self, out: &mut GenericArray); - - /// Write result into provided array and reset the hasher instance. - fn finalize_into_reset(&mut self, out: &mut GenericArray); - - /// Retrieve result and consume the hasher instance. - #[inline] - fn finalize_fixed(self) -> GenericArray - where - Self: Sized, - { - let mut out = Default::default(); - self.finalize_into(&mut out); - out - } - - /// Retrieve result and reset the hasher instance. - #[inline] - fn finalize_fixed_reset(&mut self) -> GenericArray { - let mut out = Default::default(); - self.finalize_into_reset(&mut out); - out - } -} - -/// Trait for fixed-output digest implementations to use to retrieve the -/// hash output. -/// -/// Usage of this trait in user code is discouraged. Instead use the -/// [`FixedOutput::finalize_fixed`] or [`FixedOutput::finalize_fixed_reset`] -/// methods. -/// -/// Types which impl this trait along with [`Reset`] will receive a blanket -/// impl of [`FixedOutput`]. -pub trait FixedOutputDirty { - /// Output size for fixed output digest - type OutputSize: ArrayLength; - - /// Retrieve result into provided buffer and leave hasher in a dirty state. - /// - /// This method is expected to only be called once unless - /// [`Reset::reset`] is called, after which point it can be - /// called again and reset again (and so on). - fn finalize_into_dirty(&mut self, out: &mut GenericArray); -} - -impl FixedOutput for D { - type OutputSize = D::OutputSize; - - #[inline] - fn finalize_into(mut self, out: &mut GenericArray) { - self.finalize_into_dirty(out); - } - - #[inline] - fn finalize_into_reset(&mut self, out: &mut GenericArray) { - self.finalize_into_dirty(out); - self.reset(); - } -} diff --git a/digest/src/lib.rs b/digest/src/lib.rs index 9ded58214..b93746e61 100644 --- a/digest/src/lib.rs +++ b/digest/src/lib.rs @@ -7,18 +7,20 @@ //! //! - **High-level convenience traits**: [`Digest`], [`DynDigest`]. They are wrappers //! around lower-level traits for most common hash-function use-cases. -//! - **Mid-level traits**: [`Update`], [`BlockInput`], [`Reset`], [`FixedOutput`], -//! [`VariableOutput`], [`ExtendableOutput`]. These traits atomically describe -//! available functionality of hash function implementations. -//! - **Low-level traits**: [`FixedOutputDirty`], [`VariableOutputDirty`], -//! [`ExtendableOutputDirty`]. These traits are intended to be implemented by -//! low-level algorithm providers only and simplify the amount of work -//! implementers need to do and therefore shouldn't be used in +//! - **Mid-level traits**: [`Update`], [`FixedOutput`], [`ExtendableOutput`], [`Reset`]. +//! These traits atomically describe available functionality of hash function +//! implementations. +//! - **Low-level traits**: [`UpdateCore`], [`FixedOutputCore`], +//! [`ExtendableOutputCore`]. 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 and simplify the amount of work +//! implementers need to do and therefore usually shouldn't be used in //! application-level code. //! //! Additionally hash functions implement traits from the standard library: -//! `Default`, `Clone`, `Write`. The latter is feature-gated behind `std` feature, -//! which is usually enabled by default by hash implementation crates. +//! [`Default`], [`Clone`], [`Write`][std::io::Write]. The latter is +//! feature-gated behind `std` feature, which is usually enabled by default +//! by hash implementation crates. //! //! The [`Digest`] trait is the most commonly used trait. @@ -38,76 +40,133 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + #[cfg(feature = "dev")] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] pub mod dev; +#[cfg(feature = "core-api")] +mod core_api; mod digest; +#[cfg(feature = "alloc")] mod dyn_digest; -mod errors; -mod fixed; -mod variable; -mod xof; +#[cfg(feature = "core-api")] +pub use crate::core_api::{CoreWrapper, ExtendableOutputCore, FixedOutputCore, UpdateCore}; pub use crate::digest::{Digest, Output}; -pub use crate::errors::InvalidOutputSize; -pub use crate::fixed::{FixedOutput, FixedOutputDirty}; -pub use crate::variable::{VariableOutput, VariableOutputDirty}; -pub use crate::xof::{ExtendableOutput, ExtendableOutputDirty, XofReader}; -pub use generic_array::{self, typenum::consts}; - +#[cfg(feature = "core-api")] +pub use block_buffer; #[cfg(feature = "alloc")] -pub use dyn_digest::DynDigest; - -use generic_array::ArrayLength; +pub use dyn_digest::{DynDigest, InvalidBufferLength}; +pub use generic_array::{self, typenum::consts, ArrayLength, GenericArray}; -/// Trait for updating digest state with input data. +/// Trait for updating hasher state with input data. pub trait Update { - /// Digest input data. - /// - /// This method can be called repeatedly, e.g. for processing streaming - /// messages. - fn update(&mut self, data: impl AsRef<[u8]>); + /// Update the hasher state using the provided data. + fn update(&mut self, data: &[u8]); +} + +/// Trait for resetting hasher instances +pub trait Reset { + /// Reset hasher instance to its initial state. + fn reset(&mut self); +} + +/// Trait for returning digest result with the fixed size +pub trait FixedOutput { + /// Output size for fixed output digest + type OutputSize: ArrayLength; - /// Digest input data in a chained manner. - fn chain(mut self, data: impl AsRef<[u8]>) -> Self + /// Write result into provided array and consume the hasher instance. + fn finalize_into(self, out: &mut GenericArray) + where + Self: Sized; + + /// Write result into provided array and reset the hasher instance. + fn finalize_into_reset(&mut self, out: &mut GenericArray); + + /// Retrieve result and consume the hasher instance. + #[inline] + fn finalize_fixed(self) -> GenericArray where Self: Sized, { - self.update(data); - self + let mut out = Default::default(); + self.finalize_into(&mut out); + out } -} -/// Trait to indicate that digest function processes data in blocks of size -/// `BlockSize`. -/// -/// The main usage of this trait is for implementing HMAC generically. -pub trait BlockInput { - /// Block size - type BlockSize: ArrayLength; + /// Retrieve result and reset the hasher instance. + #[inline] + fn finalize_fixed_reset(&mut self) -> GenericArray { + let mut out = Default::default(); + self.finalize_into_reset(&mut out); + out + } } -/// Trait for resetting hash instances -pub trait Reset { - /// Reset hasher instance to its initial state and return current state. - fn reset(&mut self); +/// Trait for describing readers which are used to extract extendable output +/// from XOF (extendable-output function) result. +pub trait XofReader { + /// Read output into the `buffer`. Can be called an unlimited number of times. + fn read(&mut self, buffer: &mut [u8]); + + /// Read output into a boxed slice of the specified size. + /// + /// Can be called an unlimited number of times in combination with `read`. + /// + /// `Box<[u8]>` is used instead of `Vec` to save stack space, since + /// they have size of 2 and 3 words respectively. + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + fn read_boxed(&mut self, n: usize) -> Box<[u8]> { + let mut buf = vec![0u8; n].into_boxed_slice(); + self.read(&mut buf); + buf + } } -#[macro_export] -/// Implements `std::io::Write` trait for implementer of [`Update`] -macro_rules! impl_write { - ($hasher:ident) => { - #[cfg(feature = "std")] - impl std::io::Write for $hasher { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - Update::update(self, buf); - Ok(buf.len()) - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } - } - }; +/// Trait which describes extendable-output functions (XOF). +pub trait ExtendableOutput { + /// Reader + type Reader: XofReader; + + /// Retrieve XOF reader and consume hasher instance. + fn finalize_xof(self) -> Self::Reader + where + Self: Sized; + + /// Retrieve XOF reader and reset hasher instance state. + fn finalize_xof_reset(&mut self) -> Self::Reader; + + /// Retrieve result into a boxed slice of the specified size and consume + /// the hasher. + /// + /// `Box<[u8]>` is used instead of `Vec` to save stack space, since + /// they have size of 2 and 3 words respectively. + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + fn finalize_boxed(self, n: usize) -> Box<[u8]> + where + Self: Sized, + { + let mut buf = vec![0u8; n].into_boxed_slice(); + self.finalize_xof().read(&mut buf); + buf + } + + /// Retrieve result into a boxed slice of the specified size and reset + /// the hasher's state. + /// + /// `Box<[u8]>` is used instead of `Vec` to save stack space, since + /// they have size of 2 and 3 words respectively. + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + fn finalize_boxed_reset(&mut self, n: usize) -> Box<[u8]> { + let mut buf = vec![0u8; n].into_boxed_slice(); + self.finalize_xof_reset().read(&mut buf); + buf + } } diff --git a/digest/src/variable.rs b/digest/src/variable.rs deleted file mode 100644 index 7f444931f..000000000 --- a/digest/src/variable.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Variable-sized output digest support - -use crate::{InvalidOutputSize, Reset}; - -#[cfg(feature = "alloc")] -use alloc::boxed::Box; - -/// Trait for returning digest result with the variable size -pub trait VariableOutput: Sized { - /// Create new hasher instance with the given output size. - /// - /// It will return `Err(InvalidOutputSize)` in case if hasher can not return - /// specified output size. It will always return an error if output size - /// equals to zero. - fn new(output_size: usize) -> Result; - - /// Get output size of the hasher instance provided to the `new` method - fn output_size(&self) -> usize; - - /// Retrieve result via closure and consume hasher. - /// - /// Closure is guaranteed to be called, length of the buffer passed to it - /// will be equal to `output_size`. - fn finalize_variable(self, f: impl FnOnce(&[u8])); - - /// Retrieve result via closure and reset the hasher state. - /// - /// Closure is guaranteed to be called, length of the buffer passed to it - /// will be equal to `output_size`. - fn finalize_variable_reset(&mut self, f: impl FnOnce(&[u8])); - - /// Retrieve result into a boxed slice and consume hasher. - /// - /// `Box<[u8]>` is used instead of `Vec` to save stack space, since - /// they have size of 2 and 3 words respectively. - #[cfg(feature = "alloc")] - #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] - fn finalize_boxed(self) -> Box<[u8]> { - let n = self.output_size(); - let mut buf = vec![0u8; n].into_boxed_slice(); - self.finalize_variable(|res| buf.copy_from_slice(res)); - buf - } - - /// Retrieve result into a boxed slice and reset hasher state. - /// - /// `Box<[u8]>` is used instead of `Vec` to save stack space, since - /// they have size of 2 and 3 words respectively. - #[cfg(feature = "alloc")] - #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] - fn finalize_boxed_reset(&mut self) -> Box<[u8]> { - let n = self.output_size(); - let mut buf = vec![0u8; n].into_boxed_slice(); - self.finalize_variable_reset(|res| buf.copy_from_slice(res)); - buf - } -} - -/// Trait for variable-sized output digest implementations to use to retrieve -/// the hash output. -/// -/// Usage of this trait in user code is discouraged. Instead use the -/// [`VariableOutput::finalize_variable`] or -/// [`VariableOutput::finalize_variable_reset`] methods. -/// -/// Types which impl this trait along with [`Reset`] will receive a blanket -/// impl of [`VariableOutput`]. -pub trait VariableOutputDirty: Sized { - /// Create new hasher instance with the given output size. - /// - /// It will return `Err(InvalidOutputSize)` in case if hasher can not return - /// specified output size. It will always return an error if output size - /// equals to zero. - fn new(output_size: usize) -> Result; - - /// Get output size of the hasher instance provided to the `new` method - fn output_size(&self) -> usize; - - /// Retrieve result into provided buffer and leave hasher in a dirty state. - /// - /// This method is expected to only be called once unless - /// [`Reset::reset`] is called, after which point it can be - /// called again and reset again (and so on). - fn finalize_variable_dirty(&mut self, f: impl FnOnce(&[u8])); -} - -impl VariableOutput for D { - fn new(output_size: usize) -> Result { - ::new(output_size) - } - - fn output_size(&self) -> usize { - ::output_size(self) - } - - #[inline] - fn finalize_variable(mut self, f: impl FnOnce(&[u8])) { - self.finalize_variable_dirty(f); - } - - #[inline] - fn finalize_variable_reset(&mut self, f: impl FnOnce(&[u8])) { - self.finalize_variable_dirty(f); - self.reset(); - } -} diff --git a/digest/src/xof.rs b/digest/src/xof.rs deleted file mode 100644 index 0a41833b0..000000000 --- a/digest/src/xof.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Extendable-Output Function (XOF) support - -use crate::Reset; - -#[cfg(feature = "alloc")] -use alloc::boxed::Box; - -/// Trait for describing readers which are used to extract extendable output -/// from XOF (extendable-output function) result. -pub trait XofReader { - /// Read output into the `buffer`. Can be called an unlimited number of times. - fn read(&mut self, buffer: &mut [u8]); - - /// Read output into a boxed slice of the specified size. - /// - /// Can be called an unlimited number of times in combination with `read`. - /// - /// `Box<[u8]>` is used instead of `Vec` to save stack space, since - /// they have size of 2 and 3 words respectively. - #[cfg(feature = "alloc")] - #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] - fn read_boxed(&mut self, n: usize) -> Box<[u8]> { - let mut buf = vec![0u8; n].into_boxed_slice(); - self.read(&mut buf); - buf - } -} - -/// Trait which describes extendable-output functions (XOF). -pub trait ExtendableOutput: Sized { - /// Reader - type Reader: XofReader; - - /// Retrieve XOF reader and consume hasher instance. - fn finalize_xof(self) -> Self::Reader; - - /// Retrieve XOF reader and reset hasher instance state. - fn finalize_xof_reset(&mut self) -> Self::Reader; - - /// Retrieve result into a boxed slice of the specified size and consume - /// the hasher. - /// - /// `Box<[u8]>` is used instead of `Vec` to save stack space, since - /// they have size of 2 and 3 words respectively. - #[cfg(feature = "alloc")] - #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] - fn finalize_boxed(self, n: usize) -> Box<[u8]> { - let mut buf = vec![0u8; n].into_boxed_slice(); - self.finalize_xof().read(&mut buf); - buf - } - - /// Retrieve result into a boxed slice of the specified size and reset - /// the hasher's state. - /// - /// `Box<[u8]>` is used instead of `Vec` to save stack space, since - /// they have size of 2 and 3 words respectively. - #[cfg(feature = "alloc")] - #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] - fn finalize_boxed_reset(&mut self, n: usize) -> Box<[u8]> { - let mut buf = vec![0u8; n].into_boxed_slice(); - self.finalize_xof_reset().read(&mut buf); - buf - } -} - -/// Trait for extendable-output function (XOF) implementations to use to -/// retrieve the hash output. -/// -/// Usage of this trait in user code is discouraged. Instead use the -/// [`ExtendableOutput::finalize_xof`] or -/// [`ExtendableOutput::finalize_xof_reset`] methods. -/// -/// Types which impl this trait along with [`Reset`] will receive a blanket -/// impl of [`ExtendableOutput`]. -pub trait ExtendableOutputDirty: Sized { - /// Reader - type Reader: XofReader; - - /// Retrieve XOF reader. - /// - /// This method is expected to only be called once unless - /// [`Reset::reset`] is called, after which point it can be - /// called again and reset again (and so on). - fn finalize_xof_dirty(&mut self) -> Self::Reader; -} - -impl ExtendableOutput for X { - type Reader = X::Reader; - - #[inline] - fn finalize_xof(mut self) -> Self::Reader { - self.finalize_xof_dirty() - } - - #[inline] - fn finalize_xof_reset(&mut self) -> Self::Reader { - let reader = self.finalize_xof_dirty(); - self.reset(); - reader - } -}