Skip to content

Commit 662ffe3

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 662ffe3

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

src/hazardous/hash/blake2/blake2b.rs

Lines changed: 54 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,37 @@ impl Hasher {
148151
}
149152
}
150153

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

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)