Skip to content

Commit

Permalink
impl digest_from_reader, Write for Blake2b
Browse files Browse the repository at this point in the history
This commit adds support for Rust's standard Write trait to the
Blake2b type. It also adds a convenience function digest_from_reader
that will consume all data from a reader and output the digest.
  • Loading branch information
Vince Mutolo committed Dec 30, 2021
1 parent e2af271 commit f2b5c25
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 1 deletion.
54 changes: 54 additions & 0 deletions src/hazardous/hash/blake2/blake2b.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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<dyn std::error::Error>>(())
/// ```
#[cfg(feature = "safe_api")]
impl io::Write for Blake2b {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
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 {
Expand Down Expand Up @@ -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<u8>) -> 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
}
}
}
35 changes: 34 additions & 1 deletion src/high_level/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")))]

Expand All @@ -67,6 +79,27 @@ pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> {
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<Digest, UnknownCryptoError> {
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)]
Expand Down

0 comments on commit f2b5c25

Please sign in to comment.