#![allow(clippy::needless_range_loop)]
use std::convert::TryInto;
#[derive(Clone)]
pub struct Blake2b {
h: [u64; 8],
t: [u64; 2],
f: [u64; 2],
buf: [u8; 128],
buf_len: usize,
out_len: usize,
}
const IV: [u64; 8] = [
0x6a09e667f3bcc908,
0xbb67ae8584caa73b,
0x3c6ef372fe94f82b,
0xa54ff53a5f1d36f1,
0x510e527fade682d1,
0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b,
0x5be0cd19137e2179,
];
const SIGMA: [[usize; 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], ];
impl Blake2b {
pub fn new(out_len: usize) -> Self {
Self::new_keyed(out_len, &[])
}
pub fn new_keyed(out_len: usize, key: &[u8]) -> Self {
assert!(out_len > 0 && out_len <= 64);
assert!(key.len() <= 64);
let mut h = IV;
h[0] ^= 0x01010000 ^ ((key.len() as u64) << 8) ^ (out_len as u64);
let mut state = Self {
h,
t: [0, 0],
f: [0, 0],
buf: [0; 128],
buf_len: 0,
out_len,
};
if !key.is_empty() {
state.update(key);
state.buf_len = 128; }
state
}
pub fn update(&mut self, data: &[u8]) {
let mut offset = 0;
let mut len = data.len();
while len > 0 {
if self.buf_len == 128 {
self.t[0] = self.t[0].wrapping_add(128);
if self.t[0] < 128 {
self.t[1] = self.t[1].wrapping_add(1);
}
self.compress();
self.buf_len = 0;
}
let space = 128 - self.buf_len;
let chunk = if len < space { len } else { space };
self.buf[self.buf_len..self.buf_len + chunk]
.copy_from_slice(&data[offset..offset + chunk]);
self.buf_len += chunk;
offset += chunk;
len -= chunk;
}
}
pub fn finalize(mut self) -> Vec<u8> {
self.t[0] = self.t[0].wrapping_add(self.buf_len as u64);
if self.t[0] < self.buf_len as u64 {
self.t[1] = self.t[1].wrapping_add(1);
}
self.f[0] = !0;
for i in self.buf_len..128 {
self.buf[i] = 0;
}
self.compress();
let mut out = Vec::with_capacity(self.out_len);
for i in 0..8 {
let bytes = self.h[i].to_le_bytes();
out.extend_from_slice(&bytes);
}
out.truncate(self.out_len);
out
}
fn compress(&mut self) {
let mut v = [0u64; 16];
let mut m = [0u64; 16];
for i in 0..16 {
m[i] = u64::from_le_bytes(self.buf[i * 8..(i + 1) * 8].try_into().unwrap());
}
v[0..8].copy_from_slice(&self.h);
v[8..16].copy_from_slice(&IV);
v[12] ^= self.t[0];
v[13] ^= self.t[1];
v[14] ^= self.f[0];
v[15] ^= self.f[1];
for i in 0..12 {
mix(&mut v, 0, 4, 8, 12, m[SIGMA[i][0]], m[SIGMA[i][1]]);
mix(&mut v, 1, 5, 9, 13, m[SIGMA[i][2]], m[SIGMA[i][3]]);
mix(&mut v, 2, 6, 10, 14, m[SIGMA[i][4]], m[SIGMA[i][5]]);
mix(&mut v, 3, 7, 11, 15, m[SIGMA[i][6]], m[SIGMA[i][7]]);
mix(&mut v, 0, 5, 10, 15, m[SIGMA[i][8]], m[SIGMA[i][9]]);
mix(&mut v, 1, 6, 11, 12, m[SIGMA[i][10]], m[SIGMA[i][11]]);
mix(&mut v, 2, 7, 8, 13, m[SIGMA[i][12]], m[SIGMA[i][13]]);
mix(&mut v, 3, 4, 9, 14, m[SIGMA[i][14]], m[SIGMA[i][15]]);
}
for i in 0..8 {
self.h[i] ^= v[i] ^ v[i + 8];
}
}
}
#[inline(always)]
fn mix(v: &mut [u64; 16], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) {
v[a] = v[a].wrapping_add(v[b]).wrapping_add(x);
v[d] = (v[d] ^ v[a]).rotate_right(32);
v[c] = v[c].wrapping_add(v[d]);
v[b] = (v[b] ^ v[c]).rotate_right(24);
v[a] = v[a].wrapping_add(v[b]).wrapping_add(y);
v[d] = (v[d] ^ v[a]).rotate_right(16);
v[c] = v[c].wrapping_add(v[d]);
v[b] = (v[b] ^ v[c]).rotate_right(63);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_blake2b_empty() {
let mut hasher = Blake2b::new(64);
hasher.update(b"");
let hash = hasher.finalize();
let expected = hex::decode("786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce").unwrap();
assert_eq!(hash, expected);
}
#[test]
fn test_blake2b_hello() {
let mut hasher = Blake2b::new(64);
hasher.update(b"Hello, world!");
let hash = hasher.finalize();
let expected = hex::decode("a2764d133a16816b5847a737a786f2ece4c148095c5faa73e24b4cc5d666c3e45ec271504e14dc6127ddfce4e144fb23b91a6f7b04b53d695502290722953b0f").unwrap();
assert_eq!(hash, expected);
}
#[test]
fn test_blake2b_keyed() {
let key = b"secret key";
let mut hasher = Blake2b::new_keyed(64, key);
hasher.update(b"Hello, world!");
let hash = hasher.finalize();
let mut unkeyed = Blake2b::new(64);
unkeyed.update(b"Hello, world!");
assert_ne!(hash, unkeyed.finalize());
}
}