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! {
(Digest, test_digest, 1, BLAKE2B_OUTSIZE)
}
#[derive(Debug, Clone)]
pub struct Blake2b {
_state: blake2b_core::State,
}
impl Blake2b {
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn new(size: usize) -> Result<Self, UnknownCryptoError> {
Ok(Self {
_state: blake2b_core::State::_new(&[], size)?,
})
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn reset(&mut self) -> Result<(), UnknownCryptoError> {
self._state._reset(&[])
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> {
self._state._update(data)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> {
let mut tmp = [0u8; BLAKE2B_OUTSIZE];
self._state._finalize(&mut tmp)?;
Digest::from_slice(&tmp[..self._state.size])
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Debug, PartialEq)]
pub enum Hasher {
Blake2b256,
Blake2b384,
Blake2b512,
}
impl Hasher {
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn digest(&self, data: &[u8]) -> Result<Digest, UnknownCryptoError> {
let size: usize = match *self {
Hasher::Blake2b256 => 32,
Hasher::Blake2b384 => 48,
Hasher::Blake2b512 => 64,
};
let mut state = Blake2b::new(size)?;
state.update(data)?;
state.finalize()
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn init(&self) -> Result<Blake2b, UnknownCryptoError> {
match *self {
Hasher::Blake2b256 => Blake2b::new(32),
Hasher::Blake2b384 => Blake2b::new(48),
Hasher::Blake2b512 => Blake2b::new(64),
}
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
#[cfg(feature = "safe_api")]
impl io::Write for Blake2b {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
self.update(bytes).map_err(io::Error::other)?;
Ok(bytes.len())
}
fn flush(&mut self) -> Result<(), io::Error> {
Ok(())
}
}
#[cfg(test)]
mod public {
mod test_streaming_interface_no_key {
use crate::errors::UnknownCryptoError;
use crate::hazardous::hash::blake2::blake2b::{Blake2b, Digest};
use crate::hazardous::hash::blake2::blake2b_core::{
compare_blake2b_states, BLAKE2B_BLOCKSIZE, BLAKE2B_OUTSIZE,
};
use crate::test_framework::incremental_interface::{
StreamingContextConsistencyTester, TestableStreamingContext,
};
impl TestableStreamingContext<Digest> for Blake2b {
fn reset(&mut self) -> Result<(), UnknownCryptoError> {
self.reset()
}
fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> {
self.update(input)
}
fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> {
self.finalize()
}
fn one_shot(input: &[u8]) -> Result<Digest, UnknownCryptoError> {
let mut ctx = Blake2b::new(BLAKE2B_OUTSIZE)?;
ctx.update(input)?;
ctx.finalize()
}
fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> {
let actual = Self::one_shot(input)?;
if &actual == expected {
Ok(())
} else {
Err(UnknownCryptoError)
}
}
fn compare_states(state_1: &Blake2b, state_2: &Blake2b) {
compare_blake2b_states(&state_1._state, &state_2._state)
}
}
#[test]
fn default_consistency_tests() {
let initial_state: Blake2b = Blake2b::new(BLAKE2B_OUTSIZE).unwrap();
let test_runner = StreamingContextConsistencyTester::<Digest, Blake2b>::new(
initial_state,
BLAKE2B_BLOCKSIZE,
);
test_runner.run_all_tests();
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_input_to_consistency(data: Vec<u8>) -> bool {
let initial_state: Blake2b = Blake2b::new(BLAKE2B_OUTSIZE).unwrap();
let test_runner = StreamingContextConsistencyTester::<Digest, Blake2b>::new(
initial_state,
BLAKE2B_BLOCKSIZE,
);
test_runner.run_all_tests_property(&data);
true
}
}
mod test_hasher {
use crate::hazardous::hash::blake2::blake2b::Hasher;
#[test]
fn test_hasher_interface_no_panic_and_same_result() {
let digest_256 = Hasher::Blake2b256.digest(b"Test").unwrap();
let digest_384 = Hasher::Blake2b384.digest(b"Test").unwrap();
let digest_512 = Hasher::Blake2b512.digest(b"Test").unwrap();
assert_eq!(digest_256, Hasher::Blake2b256.digest(b"Test").unwrap());
assert_eq!(digest_384, Hasher::Blake2b384.digest(b"Test").unwrap());
assert_eq!(digest_512, Hasher::Blake2b512.digest(b"Test").unwrap());
assert_ne!(digest_256, Hasher::Blake2b256.digest(b"Wrong").unwrap());
assert_ne!(digest_384, Hasher::Blake2b384.digest(b"Wrong").unwrap());
assert_ne!(digest_512, Hasher::Blake2b512.digest(b"Wrong").unwrap());
let _state_256 = Hasher::Blake2b256.init().unwrap();
let _state_384 = Hasher::Blake2b384.init().unwrap();
let _state_512 = Hasher::Blake2b512.init().unwrap();
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_hasher_digest_no_panic_and_same_result(data: Vec<u8>) -> bool {
let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap();
let d384 = Hasher::Blake2b384.digest(&data[..]).unwrap();
let d512 = Hasher::Blake2b512.digest(&data[..]).unwrap();
let d256_re = Hasher::Blake2b256.digest(&data[..]).unwrap();
let d384_re = Hasher::Blake2b384.digest(&data[..]).unwrap();
let d512_re = Hasher::Blake2b512.digest(&data[..]).unwrap();
(d256 == d256_re) && (d384 == d384_re) && (d512 == d512_re)
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_hasher_digest_256_same_as_streaming(data: Vec<u8>) -> bool {
use crate::hazardous::hash::blake2::blake2b::Blake2b;
let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap();
let mut state = Blake2b::new(32).unwrap();
state.update(&data[..]).unwrap();
d256 == state.finalize().unwrap()
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_hasher_digest_384_same_as_streaming(data: Vec<u8>) -> bool {
use crate::hazardous::hash::blake2::blake2b::Blake2b;
let d384 = Hasher::Blake2b384.digest(&data[..]).unwrap();
let mut state = Blake2b::new(48).unwrap();
state.update(&data[..]).unwrap();
d384 == state.finalize().unwrap()
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_hasher_digest_512_same_as_streaming(data: Vec<u8>) -> bool {
use crate::hazardous::hash::blake2::blake2b::Blake2b;
let d512 = Hasher::Blake2b512.digest(&data[..]).unwrap();
let mut state = Blake2b::new(64).unwrap();
state.update(&data[..]).unwrap();
d512 == state.finalize().unwrap()
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_hasher_digest_diff_input_diff_result(data: Vec<u8>) -> bool {
let d256 = Hasher::Blake2b256.digest(&data[..]).unwrap();
let d384 = Hasher::Blake2b384.digest(&data[..]).unwrap();
let d512 = Hasher::Blake2b512.digest(&data[..]).unwrap();
let d256_re = Hasher::Blake2b256.digest(b"Wrong data").unwrap();
let d384_re = Hasher::Blake2b384.digest(b"Wrong data").unwrap();
let d512_re = Hasher::Blake2b512.digest(b"Wrong data").unwrap();
(d256 != d256_re) && (d384 != d384_re) && (d512 != d512_re)
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_hasher_init_no_panic() -> bool {
let _d256 = Hasher::Blake2b256.init().unwrap();
let _d384 = Hasher::Blake2b384.init().unwrap();
let _d512 = Hasher::Blake2b512.init().unwrap();
true
}
}
mod test_new {
use crate::hazardous::hash::blake2::blake2b::Blake2b;
#[test]
fn test_init_size() {
assert!(Blake2b::new(0).is_err());
assert!(Blake2b::new(65).is_err());
assert!(Blake2b::new(1).is_ok());
assert!(Blake2b::new(64).is_ok());
}
}
#[cfg(feature = "safe_api")]
mod test_io_impls {
use crate::hazardous::hash::blake2::blake2b::Blake2b;
use crate::hazardous::hash::blake2::blake2b_core::compare_blake2b_states;
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();
hasher_b.flush().unwrap();
compare_blake2b_states(&hasher_a._state, &hasher_b._state);
let hash_a = hasher_a.finalize().unwrap();
let hash_b = hasher_b.finalize().unwrap();
hasher_b.flush().unwrap();
compare_blake2b_states(&hasher_a._state, &hasher_b._state);
hash_a == hash_b
}
}
}