From e9b01588c02c3528c30f5f79b16aec3dd6b7488e Mon Sep 17 00:00:00 2001 From: Vince Mutolo Date: Sun, 17 Oct 2021 18:36:13 -0400 Subject: [PATCH] impl Write, from_reader for Argon2i hashing --- src/hazardous/hash/blake2b.rs | 36 +++++++++++++++++++++++++++++++++++ src/high_level/hash.rs | 25 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/hazardous/hash/blake2b.rs b/src/hazardous/hash/blake2b.rs index 8193e79f..cd34623b 100644 --- a/src/hazardous/hash/blake2b.rs +++ b/src/hazardous/hash/blake2b.rs @@ -79,6 +79,8 @@ //! [`verify()`]: blake2b::Blake2b::verify //! [`as_ref()`]: blake2b::Digest::as_ref +use std::io::{Read, Write}; + use crate::{errors::UnknownCryptoError, util::endianness::load_u64_into_le, util::u64x4::U64x4}; /// The blocksize for the hash function BLAKE2b. @@ -216,6 +218,23 @@ impl Hasher { state.finalize() } + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] + /// Return a digest selected by the given Blake2b variant, where the digest + /// is the result of hashing *all* the data available from the passed `Read` type. + /// This will read from `reader` until it is exhausted. Note that internally + /// we use [`std::io::copy`](std::io::copy).. + pub fn from_reader(&self, mut reader: R) -> Result { + let size: usize = match *self { + Hasher::Blake2b256 => 32, + Hasher::Blake2b384 => 48, + Hasher::Blake2b512 => 64, + }; + + let mut state = Blake2b::new(None, size)?; + std::io::copy(&mut reader, &mut state).map_err(|_| UnknownCryptoError)?; + state.finalize() + } + #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] /// Return a `Blake2b` state selected by the given Blake2b variant. pub fn init(&self) -> Result { @@ -472,6 +491,23 @@ impl Blake2b { } } +impl Write for Blake2b { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + use std::io::{Error as IoError, ErrorKind}; + self.update(buf).map_err(|_| { + dbg!("hi"); + IoError::new(ErrorKind::Other, UnknownCryptoError) + })?; + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + // This function doesn't have to do anything because the `write` + // implementation writes everything immediately. + Ok(()) + } +} + // Testing public functions in the module. #[cfg(test)] mod public { diff --git a/src/high_level/hash.rs b/src/high_level/hash.rs index 1c15676f..66aa745a 100644 --- a/src/high_level/hash.rs +++ b/src/high_level/hash.rs @@ -54,6 +54,8 @@ //! # Ok::<(), orion::errors::UnknownCryptoError>(()) //! ``` +use std::io::Read; + pub use crate::hazardous::hash::blake2b::Digest; use crate::{errors::UnknownCryptoError, hazardous::hash::blake2b}; @@ -63,6 +65,20 @@ pub fn digest(data: &[u8]) -> Result { blake2b::Hasher::Blake2b256.digest(data) } +/// Hash all data from an object implementing `Read`. Note that internally +/// this calls [`std::io::copy`](std::io::copy) and will consume *all* data +/// available in the reader. +/// +/// ```rust +/// use orion::hash::{self, Digest}; +/// let data = std::io::Cursor::new(vec![1, 2, 3]); +/// let digest: Digest = hash::from_reader(data).unwrap(); +/// ``` +#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."] +pub fn from_reader(reader: R) -> Result { + blake2b::Hasher::Blake2b256.from_reader(reader) +} + // Testing public functions in the module. #[cfg(feature = "safe_api")] #[cfg(test)] @@ -80,4 +96,13 @@ mod public { fn prop_digest_diff_result(input: Vec) -> bool { digest(&input[..]).unwrap() != digest(b"Completely wrong input").unwrap() } + + #[quickcheck] + /// Hashing with `from_reader` should always return the same as a `digest` of the data. + fn prop_digest_reader_same_result(input: Vec) -> bool { + use std::io::Cursor; + let expected = digest(input.as_slice()).unwrap(); + let using_reader = from_reader(Cursor::new(input)).unwrap(); + expected == using_reader + } }