use core::cmp;
use core::fmt;
use Compress4xFn;
use Hash;
use Params as Blake2bParams;
use State as Blake2bState;
use BLOCKBYTES;
use KEYBYTES;
use OUTBYTES;
#[cfg(feature = "std")]
use std;
pub fn blake2bp(input: &[u8]) -> Hash {
State::new().update(input).finalize()
}
#[derive(Clone)]
pub struct Params {
hash_length: u8,
key_length: u8,
key: [u8; KEYBYTES],
}
impl Params {
pub fn new() -> Self {
Self::default()
}
pub fn to_state(&self) -> State {
State::with_params(self)
}
pub fn hash_length(&mut self, length: usize) -> &mut Self {
assert!(
1 <= length && length <= OUTBYTES,
"Bad hash length: {}",
length
);
self.hash_length = length as u8;
self
}
pub fn key(&mut self, key: &[u8]) -> &mut Self {
assert!(key.len() <= KEYBYTES, "Bad key length: {}", key.len());
self.key_length = key.len() as u8;
self.key = [0; KEYBYTES];
self.key[..key.len()].copy_from_slice(key);
self
}
}
impl Default for Params {
fn default() -> Self {
Self {
hash_length: OUTBYTES as u8,
key_length: 0,
key: [0; KEYBYTES],
}
}
}
impl fmt::Debug for Params {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Params {{ hash_length: {}, key_length: {} }}",
self.hash_length,
self.key_length,
)
}
}
#[derive(Clone)]
pub struct State {
leaf0: Blake2bState,
leaf1: Blake2bState,
leaf2: Blake2bState,
leaf3: Blake2bState,
root: Blake2bState,
buf: [u8; 8 * BLOCKBYTES],
buflen: u16,
count: u128,
compress_4x_fn: Compress4xFn,
}
impl State {
pub fn new() -> Self {
Self::with_params(&Params::default())
}
fn with_params(params: &Params) -> Self {
let mut base_params = Blake2bParams::new();
base_params
.hash_length(params.hash_length as usize)
.key(¶ms.key[..params.key_length as usize])
.fanout(4)
.max_depth(2)
.max_leaf_length(0)
.inner_hash_length(OUTBYTES);
let leaf_state = |worker_index| {
let mut state = base_params
.clone()
.node_offset(worker_index)
.node_depth(0)
.last_node(worker_index == 3)
.to_state();
state.hash_length = OUTBYTES as u8;
state
};
let mut root_state = base_params
.clone()
.node_offset(0)
.node_depth(1)
.last_node(true)
.to_state();
root_state.buflen = 0;
root_state.count = 0;
Self {
leaf0: leaf_state(0),
leaf1: leaf_state(1),
leaf2: leaf_state(2),
leaf3: leaf_state(3),
root: root_state,
buf: [0; 8 * BLOCKBYTES],
buflen: 0,
count: 0,
compress_4x_fn: ::default_compress_impl().1,
}
}
fn fill_buf(&mut self, input: &mut &[u8]) {
let take = cmp::min(self.buf.len() - self.buflen as usize, input.len());
self.buf[self.buflen as usize..self.buflen as usize + take].copy_from_slice(&input[..take]);
self.buflen += take as u16;
self.count += take as u128;
*input = &input[take..];
}
fn compress_4x(
input: &[u8; 4 * BLOCKBYTES],
leaf0: &mut Blake2bState,
leaf1: &mut Blake2bState,
leaf2: &mut Blake2bState,
leaf3: &mut Blake2bState,
compress_4x_fn: Compress4xFn,
) {
debug_assert_eq!(0, leaf0.buflen);
debug_assert_eq!(0, leaf1.buflen);
debug_assert_eq!(0, leaf2.buflen);
debug_assert_eq!(0, leaf3.buflen);
debug_assert_eq!(leaf0.count, leaf1.count);
debug_assert_eq!(leaf0.count, leaf2.count);
debug_assert_eq!(leaf0.count, leaf3.count);
leaf0.count += BLOCKBYTES as u128;
leaf1.count += BLOCKBYTES as u128;
leaf2.count += BLOCKBYTES as u128;
leaf3.count += BLOCKBYTES as u128;
let msg_refs = array_refs!(input, BLOCKBYTES, BLOCKBYTES, BLOCKBYTES, BLOCKBYTES);
unsafe {
(compress_4x_fn)(
&mut leaf0.h,
&mut leaf1.h,
&mut leaf2.h,
&mut leaf3.h,
msg_refs.0,
msg_refs.1,
msg_refs.2,
msg_refs.3,
leaf0.count,
leaf1.count,
leaf2.count,
leaf3.count,
0,
0,
0,
0,
0,
0,
0,
0,
);
}
}
pub fn update(&mut self, mut input: &[u8]) -> &mut Self {
if self.buflen > 0 {
self.fill_buf(&mut input);
if !input.is_empty() {
Self::compress_4x(
array_ref!(self.buf, 0, 4 * BLOCKBYTES),
&mut self.leaf0,
&mut self.leaf1,
&mut self.leaf2,
&mut self.leaf3,
self.compress_4x_fn,
);
self.buflen -= 4 * BLOCKBYTES as u16;
if input.len() > 3 * BLOCKBYTES {
Self::compress_4x(
array_ref!(self.buf, 4 * BLOCKBYTES, 4 * BLOCKBYTES),
&mut self.leaf0,
&mut self.leaf1,
&mut self.leaf2,
&mut self.leaf3,
self.compress_4x_fn,
);
self.buflen = 0;
} else {
let (left, right) = self.buf.split_at_mut(4 * BLOCKBYTES);
left[..self.buflen as usize].copy_from_slice(&right[..self.buflen as usize]);
}
}
}
while input.len() > 7 * BLOCKBYTES {
self.count += 4 * BLOCKBYTES as u128;
let block = array_ref!(input, 0, 4 * BLOCKBYTES);
Self::compress_4x(
block,
&mut self.leaf0,
&mut self.leaf1,
&mut self.leaf2,
&mut self.leaf3,
self.compress_4x_fn,
);
input = &input[4 * BLOCKBYTES..];
}
self.fill_buf(&mut input);
debug_assert_eq!(0, input.len());
self
}
pub fn finalize(&mut self) -> Hash {
let mut leaf0 = self.leaf0.clone();
let mut leaf1 = self.leaf1.clone();
let mut leaf2 = self.leaf2.clone();
let mut leaf3 = self.leaf3.clone();
let chunks = array_refs!(
&self.buf, BLOCKBYTES, BLOCKBYTES, BLOCKBYTES, BLOCKBYTES, BLOCKBYTES, BLOCKBYTES,
BLOCKBYTES, BLOCKBYTES
);
let mut buflen = self.buflen as usize;
leaf0.update(&chunks.0[..cmp::min(buflen, BLOCKBYTES)]);
buflen = buflen.saturating_sub(BLOCKBYTES);
leaf1.update(&chunks.1[..cmp::min(buflen, BLOCKBYTES)]);
buflen = buflen.saturating_sub(BLOCKBYTES);
leaf2.update(&chunks.2[..cmp::min(buflen, BLOCKBYTES)]);
buflen = buflen.saturating_sub(BLOCKBYTES);
leaf3.update(&chunks.3[..cmp::min(buflen, BLOCKBYTES)]);
buflen = buflen.saturating_sub(BLOCKBYTES);
leaf0.update(&chunks.4[..cmp::min(buflen, BLOCKBYTES)]);
buflen = buflen.saturating_sub(BLOCKBYTES);
leaf1.update(&chunks.5[..cmp::min(buflen, BLOCKBYTES)]);
buflen = buflen.saturating_sub(BLOCKBYTES);
leaf2.update(&chunks.6[..cmp::min(buflen, BLOCKBYTES)]);
buflen = buflen.saturating_sub(BLOCKBYTES);
leaf3.update(&chunks.7[..cmp::min(buflen, BLOCKBYTES)]);
let mut root = self.root.clone();
root.update(leaf0.finalize().as_bytes());
root.update(leaf1.finalize().as_bytes());
root.update(leaf2.finalize().as_bytes());
root.update(leaf3.finalize().as_bytes());
root.finalize()
}
pub fn count(&self) -> u128 {
self.count
}
}
#[cfg(feature = "std")]
impl std::io::Write for State {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.update(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl fmt::Debug for State {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"State {{ count: {}, root: {:?}, leaf0: {:?}, leaf1: {:?}, \
leaf2: {:?}, leaf3: {:?} }}",
self.count, self.root, self.leaf0, self.leaf1, self.leaf2, self.leaf3
)
}
}
impl Default for State {
fn default() -> Self {
Self::with_params(&Params::default())
}
}
pub(crate) fn force_portable(state: &mut State) {
state.compress_4x_fn = ::portable::compress_4x;
state.root.compress_fn = ::portable::compress;
state.leaf0.compress_fn = ::portable::compress;
state.leaf1.compress_fn = ::portable::compress;
state.leaf2.compress_fn = ::portable::compress;
state.leaf3.compress_fn = ::portable::compress;
}
#[cfg(test)]
pub(crate) mod test {
use super::*;
use byteorder::{ByteOrder, LittleEndian};
pub(crate) fn paint_input(buf: &mut [u8]) {
let mut offset = 0;
let mut counter: u32 = 1;
while offset < buf.len() {
let mut bytes = [0; 4];
LittleEndian::write_u32(&mut bytes, counter);
let take = cmp::min(4, buf.len() - offset);
buf[offset..][..take].copy_from_slice(&bytes[..take]);
counter += 1;
offset += take;
}
}
fn blake2bp_reference(input: &[u8]) -> Hash {
let mut leaves = [
Blake2bParams::new()
.fanout(4)
.max_depth(2)
.node_offset(0)
.inner_hash_length(OUTBYTES)
.to_state(),
Blake2bParams::new()
.fanout(4)
.max_depth(2)
.node_offset(1)
.inner_hash_length(OUTBYTES)
.to_state(),
Blake2bParams::new()
.fanout(4)
.max_depth(2)
.node_offset(2)
.inner_hash_length(OUTBYTES)
.to_state(),
Blake2bParams::new()
.fanout(4)
.max_depth(2)
.node_offset(3)
.inner_hash_length(OUTBYTES)
.last_node(true)
.to_state(),
];
for (i, chunk) in input.chunks(BLOCKBYTES).enumerate() {
leaves[i % 4].update(chunk);
}
let mut root = Blake2bParams::new()
.fanout(4)
.max_depth(2)
.node_depth(1)
.inner_hash_length(OUTBYTES)
.last_node(true)
.to_state();
for leaf in &mut leaves {
root.update(leaf.finalize().as_bytes());
}
root.finalize()
}
#[test]
fn test_buffering() {
let mut buf = [0; 20 * BLOCKBYTES];
paint_input(&mut buf);
for num_chunks in 1..=20 {
let input = &buf[..num_chunks * BLOCKBYTES];
let expected = blake2bp_reference(&input);
let found = blake2bp(&input);
assert_eq!(expected, found);
let mut state = State::new();
state.update(&input[..1]);
state.update(&input[1..]);
let found = state.finalize();
assert_eq!(expected, found);
}
}
}