diff --git a/src/hazardous/hash/blake2/blake2b.rs b/src/hazardous/hash/blake2/blake2b.rs index 99a0db3c..22872ee4 100644 --- a/src/hazardous/hash/blake2/blake2b.rs +++ b/src/hazardous/hash/blake2/blake2b.rs @@ -65,6 +65,9 @@ use crate::errors::UnknownCryptoError; use crate::hazardous::hash::blake2::blake2b_core; use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE; +#[cfg(feature = "safe_api")] +use std::io; + construct_public! { /// A type to represent the `Digest` that BLAKE2b returns. /// @@ -148,6 +151,37 @@ impl Hasher { } } +/// Example: custom digest size. +/// ```rust +/// use orion::{ +/// hazardous::hash::blake2::blake2b::{Blake2b, Digest}, +/// errors::UnknownCryptoError, +/// }; +/// use std::io::{self, Read, Write}; +/// +/// // `reader` could also be a `File::open(...)?`. +/// let mut reader = io::Cursor::new(b"some data"); +/// let mut hasher = Blake2b::new(64)?; // 512-bit hash +/// std::io::copy(&mut reader, &mut hasher)?; +/// +/// let digest: Digest = hasher.finalize()?; +/// +/// # Ok::<(), Box>(()) +/// ``` +#[cfg(feature = "safe_api")] +impl io::Write for Blake2b { + fn write(&mut self, bytes: &[u8]) -> io::Result { + self.update(bytes) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + Ok(bytes.len()) + } + + fn flush(&mut self) -> Result<(), std::io::Error> { + // Every write completes immediately, so flushing is a no-op. + Ok(()) + } +} + #[cfg(test)] mod public { mod test_streaming_interface_no_key { @@ -344,4 +378,24 @@ mod public { assert!(Blake2b::new(64).is_ok()); } } + + #[cfg(feature = "safe_api")] + mod test_io_impls { + use crate::hazardous::hash::blake2::blake2b::Blake2b; + use std::io::Write; + + #[quickcheck] + fn prop_hasher_write_same_as_update(data: Vec) -> bool { + let mut hasher_a = Blake2b::new(64).unwrap(); + let mut hasher_b = hasher_a.clone(); + + hasher_a.update(&data).unwrap(); + hasher_b.write_all(&data).unwrap(); + + let hash_a = hasher_a.finalize().unwrap(); + let hash_b = hasher_b.finalize().unwrap(); + + hash_a == hash_b + } + } } diff --git a/src/high_level/hash.rs b/src/high_level/hash.rs index 91d04ab2..8873e2f5 100644 --- a/src/high_level/hash.rs +++ b/src/high_level/hash.rs @@ -48,13 +48,25 @@ //! - BLAKE2b is not suitable for password hashing. See [`orion::pwhash`](super::pwhash) //! instead. //! -//! # Example: +//! # Examples +//! +//! ## Hashing in-memory data //! ```rust //! use orion::hash::{digest, Digest}; //! //! let hash: Digest = digest(b"Some data")?; //! # Ok::<(), orion::errors::UnknownCryptoError>(()) //! ``` +//! +//! ## Hashing data from an arbitrary reader +//! ```rust +//! use orion::hash::{digest_from_reader, Digest}; +//! +//! // `reader` could instead be `File::open("file.txt")?` +//! let reader = std::io::Cursor::new(b"some data"); +//! let hash: Digest = digest_from_reader(reader)?; +//! # Ok::<(), orion::errors::UnknownCryptoError>(()) +//! ``` #![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))] @@ -67,6 +79,27 @@ pub fn digest(data: &[u8]) -> Result { blake2b::Hasher::Blake2b256.digest(data) } +/// Hash data from `impl Read` using BLAKE2b-256. +/// +/// See the [module-level docs](crate::hash) for an example of how to use this function. +/// Internally calls `std::io::copy()` to move data from the reader into the Blake2b writer. +/// The `std::io::copy` function buffers reads, so passing in a `BufReader` may be unnecessary. +/// +/// For lower-level control over reads, writes, buffer sizes, *etc.*, consider using the +/// [`Blake2b`](crate::hazardous::hash::blake2::blake2b::Blake2b) type and its +/// [`Write`](std::io::Write) implementation directly. See the Blake2b type's source code +/// for an example. +/// +/// Note that if an error is returned, data may still have been consumed +/// from the given reader. +#[cfg(feature = "safe_api")] +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +pub fn digest_from_reader(mut reader: impl std::io::Read) -> Result { + let mut hasher = blake2b::Blake2b::new(32)?; + std::io::copy(&mut reader, &mut hasher).map_err(|_| UnknownCryptoError)?; + hasher.finalize() +} + // Testing public functions in the module. #[cfg(feature = "safe_api")] #[cfg(test)]