#![cfg_attr(not(feature = "std"), no_std)]
use arrayref::{array_refs, mut_array_refs};
use core::cmp;
use core::fmt;
use core::mem::size_of;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod avx2;
mod portable;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod sse41;
pub mod blake2bp;
mod guts;
pub mod many;
#[cfg(test)]
mod test;
type Word = u64;
type Count = u128;
pub const OUTBYTES: usize = 8 * size_of::<Word>();
pub const KEYBYTES: usize = 8 * size_of::<Word>();
pub const SALTBYTES: usize = 2 * size_of::<Word>();
pub const PERSONALBYTES: usize = 2 * size_of::<Word>();
pub const BLOCKBYTES: usize = 16 * size_of::<Word>();
const IV: [Word; 8] = [
0x6A09E667F3BCC908,
0xBB67AE8584CAA73B,
0x3C6EF372FE94F82B,
0xA54FF53A5F1D36F1,
0x510E527FADE682D1,
0x9B05688C2B3E6C1F,
0x1F83D9ABFB41BD6B,
0x5BE0CD19137E2179,
];
const SIGMA: [[u8; 16]; 12] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
];
pub fn blake2b(input: &[u8]) -> Hash {
Params::new().hash(input)
}
#[derive(Clone)]
pub struct Params {
hash_length: u8,
key_length: u8,
key_block: [u8; BLOCKBYTES],
salt: [u8; SALTBYTES],
personal: [u8; PERSONALBYTES],
fanout: u8,
max_depth: u8,
max_leaf_length: u32,
node_offset: u64,
node_depth: u8,
inner_hash_length: u8,
last_node: guts::LastNode,
implementation: guts::Implementation,
}
impl Params {
#[inline]
pub fn new() -> Self {
Self {
hash_length: OUTBYTES as u8,
key_length: 0,
key_block: [0; BLOCKBYTES],
salt: [0; SALTBYTES],
personal: [0; PERSONALBYTES],
fanout: 1,
max_depth: 1,
max_leaf_length: 0,
node_offset: 0,
node_depth: 0,
inner_hash_length: 0,
last_node: guts::LastNode::No,
implementation: guts::Implementation::detect(),
}
}
#[inline(always)]
fn to_words(&self) -> [Word; 8] {
let (salt_left, salt_right) = array_refs!(&self.salt, SALTBYTES / 2, SALTBYTES / 2);
let (personal_left, personal_right) =
array_refs!(&self.personal, PERSONALBYTES / 2, PERSONALBYTES / 2);
[
IV[0]
^ self.hash_length as u64
^ (self.key_length as u64) << 8
^ (self.fanout as u64) << 16
^ (self.max_depth as u64) << 24
^ (self.max_leaf_length as u64) << 32,
IV[1] ^ self.node_offset,
IV[2] ^ self.node_depth as u64 ^ (self.inner_hash_length as u64) << 8,
IV[3],
IV[4] ^ Word::from_le_bytes(*salt_left),
IV[5] ^ Word::from_le_bytes(*salt_right),
IV[6] ^ Word::from_le_bytes(*personal_left),
IV[7] ^ Word::from_le_bytes(*personal_right),
]
}
#[inline]
pub fn hash(&self, input: &[u8]) -> Hash {
if self.key_length > 0 {
return self.to_state().update(input).finalize();
}
let mut words = self.to_words();
self.implementation.compress1_loop(
input,
&mut words,
0,
self.last_node,
guts::Finalize::Yes,
guts::Stride::Serial,
);
Hash {
bytes: state_words_to_bytes(&words),
len: self.hash_length,
}
}
pub fn to_state(&self) -> State {
State::with_params(self)
}
#[inline]
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
}
#[inline]
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_block = [0; BLOCKBYTES];
self.key_block[..key.len()].copy_from_slice(key);
self
}
#[inline]
pub fn salt(&mut self, salt: &[u8]) -> &mut Self {
assert!(salt.len() <= SALTBYTES, "Bad salt length: {}", salt.len());
self.salt = [0; SALTBYTES];
self.salt[..salt.len()].copy_from_slice(salt);
self
}
#[inline]
pub fn personal(&mut self, personalization: &[u8]) -> &mut Self {
assert!(
personalization.len() <= PERSONALBYTES,
"Bad personalization length: {}",
personalization.len()
);
self.personal = [0; PERSONALBYTES];
self.personal[..personalization.len()].copy_from_slice(personalization);
self
}
#[inline]
pub fn fanout(&mut self, fanout: u8) -> &mut Self {
self.fanout = fanout;
self
}
#[inline]
pub fn max_depth(&mut self, depth: u8) -> &mut Self {
self.max_depth = depth;
self
}
#[inline]
pub fn max_leaf_length(&mut self, length: u32) -> &mut Self {
self.max_leaf_length = length;
self
}
#[inline]
pub fn node_offset(&mut self, offset: u64) -> &mut Self {
self.node_offset = offset;
self
}
#[inline]
pub fn node_depth(&mut self, depth: u8) -> &mut Self {
self.node_depth = depth;
self
}
#[inline]
pub fn inner_hash_length(&mut self, length: usize) -> &mut Self {
assert!(length <= OUTBYTES, "Bad inner hash length: {}", length);
self.inner_hash_length = length as u8;
self
}
#[inline]
pub fn last_node(&mut self, last_node: bool) -> &mut Self {
self.last_node = if last_node {
guts::LastNode::Yes
} else {
guts::LastNode::No
};
self
}
}
impl Default for Params {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for Params {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Params {{ hash_length: {}, key_length: {}, salt: {:?}, personal: {:?}, fanout: {}, \
max_depth: {}, max_leaf_length: {}, node_offset: {}, node_depth: {}, \
inner_hash_length: {}, last_node: {} }}",
self.hash_length,
self.key_length,
&self.salt,
&self.personal,
self.fanout,
self.max_depth,
self.max_leaf_length,
self.node_offset,
self.node_depth,
self.inner_hash_length,
self.last_node.yes(),
)
}
}
#[derive(Clone)]
pub struct State {
words: [Word; 8],
count: Count,
buf: [u8; BLOCKBYTES],
buflen: u8,
last_node: guts::LastNode,
hash_length: u8,
implementation: guts::Implementation,
is_keyed: bool,
}
impl State {
pub fn new() -> Self {
Self::with_params(&Params::default())
}
fn with_params(params: &Params) -> Self {
let mut state = Self {
words: params.to_words(),
count: 0,
buf: [0; BLOCKBYTES],
buflen: 0,
last_node: params.last_node,
hash_length: params.hash_length,
implementation: params.implementation,
is_keyed: params.key_length > 0,
};
if state.is_keyed {
state.buf = params.key_block;
state.buflen = state.buf.len() as u8;
}
state
}
fn fill_buf(&mut self, input: &mut &[u8]) {
let take = cmp::min(BLOCKBYTES - 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 u8;
*input = &input[take..];
}
fn compress_buffer_if_possible(&mut self, input: &mut &[u8]) {
if self.buflen > 0 {
self.fill_buf(input);
if !input.is_empty() {
self.implementation.compress1_loop(
&self.buf,
&mut self.words,
self.count,
self.last_node,
guts::Finalize::No,
guts::Stride::Serial,
);
self.count = self.count.wrapping_add(BLOCKBYTES as Count);
self.buflen = 0;
}
}
}
pub fn update(&mut self, mut input: &[u8]) -> &mut Self {
self.compress_buffer_if_possible(&mut input);
let mut end = input.len().saturating_sub(1);
end -= end % BLOCKBYTES;
if end > 0 {
self.implementation.compress1_loop(
&input[..end],
&mut self.words,
self.count,
self.last_node,
guts::Finalize::No,
guts::Stride::Serial,
);
self.count = self.count.wrapping_add(end as Count);
input = &input[end..];
}
self.fill_buf(&mut input);
self
}
pub fn finalize(&self) -> Hash {
let mut words_copy = self.words;
self.implementation.compress1_loop(
&self.buf[..self.buflen as usize],
&mut words_copy,
self.count,
self.last_node,
guts::Finalize::Yes,
guts::Stride::Serial,
);
Hash {
bytes: state_words_to_bytes(&words_copy),
len: self.hash_length,
}
}
pub fn set_last_node(&mut self, last_node: bool) -> &mut Self {
self.last_node = if last_node {
guts::LastNode::Yes
} else {
guts::LastNode::No
};
self
}
pub fn count(&self) -> Count {
let mut ret = self.count.wrapping_add(self.buflen as Count);
if self.is_keyed {
ret -= BLOCKBYTES as Count;
}
ret
}
}
#[inline(always)]
fn state_words_to_bytes(state_words: &[Word; 8]) -> [u8; OUTBYTES] {
let mut bytes = [0; OUTBYTES];
{
const W: usize = size_of::<Word>();
let refs = mut_array_refs!(&mut bytes, W, W, W, W, W, W, W, W);
*refs.0 = state_words[0].to_le_bytes();
*refs.1 = state_words[1].to_le_bytes();
*refs.2 = state_words[2].to_le_bytes();
*refs.3 = state_words[3].to_le_bytes();
*refs.4 = state_words[4].to_le_bytes();
*refs.5 = state_words[5].to_le_bytes();
*refs.6 = state_words[6].to_le_bytes();
*refs.7 = state_words[7].to_le_bytes();
}
bytes
}
#[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: {}, hash_length: {}, last_node: {} }}",
self.count(),
self.hash_length,
self.last_node.yes(),
)
}
}
impl Default for State {
fn default() -> Self {
Self::with_params(&Params::default())
}
}
type HexString = arrayvec::ArrayString<{ 2 * OUTBYTES }>;
#[derive(Clone, Copy)]
pub struct Hash {
bytes: [u8; OUTBYTES],
len: u8,
}
impl Hash {
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len as usize]
}
#[inline]
pub fn as_array(&self) -> &[u8; OUTBYTES] {
debug_assert_eq!(self.len as usize, OUTBYTES);
&self.bytes
}
pub fn to_hex(&self) -> HexString {
bytes_to_hex(self.as_bytes())
}
}
fn bytes_to_hex(bytes: &[u8]) -> HexString {
let mut s = arrayvec::ArrayString::new();
let table = b"0123456789abcdef";
for &b in bytes {
s.push(table[(b >> 4) as usize] as char);
s.push(table[(b & 0xf) as usize] as char);
}
s
}
impl From<[u8; OUTBYTES]> for Hash {
fn from(bytes: [u8; OUTBYTES]) -> Self {
Self {
bytes,
len: OUTBYTES as u8,
}
}
}
impl From<&[u8; OUTBYTES]> for Hash {
fn from(bytes: &[u8; OUTBYTES]) -> Self {
Self::from(*bytes)
}
}
impl PartialEq for Hash {
fn eq(&self, other: &Hash) -> bool {
constant_time_eq::constant_time_eq(&self.as_bytes(), &other.as_bytes())
}
}
impl PartialEq<[u8]> for Hash {
fn eq(&self, other: &[u8]) -> bool {
constant_time_eq::constant_time_eq(&self.as_bytes(), other)
}
}
impl Eq for Hash {}
impl AsRef<[u8]> for Hash {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl fmt::Debug for Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Hash(0x{})", self.to_hex())
}
}
#[cfg(test)]
fn paint_test_input(buf: &mut [u8]) {
let mut offset = 0;
let mut counter: u32 = 1;
while offset < buf.len() {
let bytes = counter.to_le_bytes();
let take = cmp::min(bytes.len(), buf.len() - offset);
buf[offset..][..take].copy_from_slice(&bytes[..take]);
counter += 1;
offset += take;
}
}
#[doc(hidden)]
pub mod benchmarks {
use super::*;
pub fn force_portable(params: &mut Params) {
params.implementation = guts::Implementation::portable();
}
pub fn force_portable_blake2bp(params: &mut blake2bp::Params) {
blake2bp::force_portable(params);
}
}