Skip to content

Commit 5866cd8

Browse files
author
Vince Mutolo
committed
impl digest_from_reader, Write for Blake2b
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.
1 parent e2af271 commit 5866cd8

File tree

2 files changed

+98
-1
lines changed

2 files changed

+98
-1
lines changed

src/hazardous/hash/blake2/blake2b.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ use crate::errors::UnknownCryptoError;
6565
use crate::hazardous::hash::blake2::blake2b_core;
6666
use crate::hazardous::hash::blake2::blake2b_core::BLAKE2B_OUTSIZE;
6767

68+
#[cfg(feature = "safe_api")]
69+
use std::io;
70+
6871
construct_public! {
6972
/// A type to represent the `Digest` that BLAKE2b returns.
7073
///
@@ -148,6 +151,38 @@ impl Hasher {
148151
}
149152
}
150153

154+
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
155+
/// Example: custom digest size.
156+
/// ```rust
157+
/// use orion::{
158+
/// hazardous::hash::blake2::blake2b::{Blake2b, Digest},
159+
/// errors::UnknownCryptoError,
160+
/// };
161+
/// use std::io::{self, Read, Write};
162+
///
163+
/// // `reader` could also be a `File::open(...)?`.
164+
/// let mut reader = io::Cursor::new(b"some data");
165+
/// let mut hasher = Blake2b::new(64)?; // 512-bit hash
166+
/// std::io::copy(&mut reader, &mut hasher)?;
167+
///
168+
/// let digest: Digest = hasher.finalize()?;
169+
///
170+
/// # Ok::<(), Box<dyn std::error::Error>>(())
171+
/// ```
172+
#[cfg(feature = "safe_api")]
173+
impl io::Write for Blake2b {
174+
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
175+
self.update(bytes)
176+
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
177+
Ok(bytes.len())
178+
}
179+
180+
fn flush(&mut self) -> Result<(), std::io::Error> {
181+
// This type doesn't buffer writes, so flushing is a no-op.
182+
Ok(())
183+
}
184+
}
185+
151186
#[cfg(test)]
152187
mod public {
153188
mod test_streaming_interface_no_key {
@@ -344,4 +379,24 @@ mod public {
344379
assert!(Blake2b::new(64).is_ok());
345380
}
346381
}
382+
383+
#[cfg(feature = "safe_api")]
384+
mod test_io_impls {
385+
use crate::hazardous::hash::blake2::blake2b::Blake2b;
386+
use std::io::Write;
387+
388+
#[quickcheck]
389+
fn prop_hasher_write_same_as_update(data: Vec<u8>) -> bool {
390+
let mut hasher_a = Blake2b::new(64).unwrap();
391+
let mut hasher_b = hasher_a.clone();
392+
393+
hasher_a.update(&data).unwrap();
394+
hasher_b.write_all(&data).unwrap();
395+
396+
let hash_a = hasher_a.finalize().unwrap();
397+
let hash_b = hasher_b.finalize().unwrap();
398+
399+
hash_a == hash_b
400+
}
401+
}
347402
}

src/high_level/hash.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,25 @@
4848
//! - BLAKE2b is not suitable for password hashing. See [`orion::pwhash`](super::pwhash)
4949
//! instead.
5050
//!
51-
//! # Example:
51+
//! # Examples
52+
//!
53+
//! ## Hashing in-memory data
5254
//! ```rust
5355
//! use orion::hash::{digest, Digest};
5456
//!
5557
//! let hash: Digest = digest(b"Some data")?;
5658
//! # Ok::<(), orion::errors::UnknownCryptoError>(())
5759
//! ```
60+
//!
61+
//! ## Hashing data from an arbitrary reader
62+
//! ```rust
63+
//! use orion::hash::{digest_from_reader, Digest};
64+
//!
65+
//! // `reader` could instead be `File::open("file.txt")?`
66+
//! let reader = std::io::Cursor::new(b"some data");
67+
//! let hash: Digest = digest_from_reader(reader)?;
68+
//! # Ok::<(), orion::errors::UnknownCryptoError>(())
69+
//! ```
5870
5971
#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
6072

@@ -67,6 +79,27 @@ pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> {
6779
blake2b::Hasher::Blake2b256.digest(data)
6880
}
6981

82+
/// Hash data from `impl Read` using BLAKE2b-256.
83+
///
84+
/// See the [module-level docs](crate::hash) for an example of how to use this function.
85+
/// Internally calls `std::io::copy()` to move data from the reader into the Blake2b writer.
86+
/// The `std::io::copy` function buffers reads, so passing in a `BufReader` may be unnecessary.
87+
///
88+
/// For lower-level control over reads, writes, buffer sizes, *etc.*, consider using the
89+
/// [`Blake2b`](crate::hazardous::hash::blake2::blake2b::Blake2b) type and its
90+
/// [`Write`](std::io::Write) implementation directly. See the Blake2b type's source code
91+
/// for an example.
92+
///
93+
/// Note that if an error is returned, data may still have been consumed
94+
/// from the given reader.
95+
#[cfg(feature = "safe_api")]
96+
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
97+
pub fn digest_from_reader(mut reader: impl std::io::Read) -> Result<Digest, UnknownCryptoError> {
98+
let mut hasher = blake2b::Blake2b::new(32)?;
99+
std::io::copy(&mut reader, &mut hasher).map_err(|_| UnknownCryptoError)?;
100+
hasher.finalize()
101+
}
102+
70103
// Testing public functions in the module.
71104
#[cfg(feature = "safe_api")]
72105
#[cfg(test)]
@@ -79,6 +112,15 @@ mod public {
79112
digest(&input[..]).unwrap() == digest(&input[..]).unwrap()
80113
}
81114

115+
#[quickcheck]
116+
/// Hashing all input should be the same as wrapping it in a
117+
/// cursor and using digest_from_reader.
118+
fn prop_digest_same_as_digest_from_reader(input: Vec<u8>) -> bool {
119+
let digest_a = digest_from_reader(std::io::Cursor::new(&input)).unwrap();
120+
let digest_b = digest(&input).unwrap();
121+
digest_a == digest_b
122+
}
123+
82124
#[quickcheck]
83125
/// Hashing twice with different input should never produce same output.
84126
fn prop_digest_diff_result(input: Vec<u8>) -> bool {

0 commit comments

Comments
 (0)