const IV: [u32; 8] = [
0x6A09_E667,
0xBB67_AE85,
0x3C6E_F372,
0xA54F_F53A,
0x510E_527F,
0x9B05_688C,
0x1F83_D9AB,
0x5BE0_CD19,
];
#[derive(Debug)]
pub enum Error {
InvalidInput,
}
#[derive(Clone)]
#[doc = "Blake2s instance with a variable output."]
pub struct Blake2s {
b: [u8; 64],
h: [u32; 8],
t: u64,
c: u8,
outlen: u8,
}
impl Blake2s {
#[allow(clippy::cast_possible_truncation)]
fn new_with_params(
outlen: usize,
salt: &[u8],
persona: &[u8],
key_size: usize,
) -> Result<Self, Error> {
if outlen == 0 || outlen > 32 || key_size > 32 || salt.len() > 8 || persona.len() > 8 {
return Err(Error::InvalidInput);
}
let mut p = [0u32; 8];
p[0] = 0x0101_0000 ^ (key_size << 8) as u32 ^ outlen as u32;
let mut padded = [0u8; 8];
padded[..salt.len()].copy_from_slice(salt);
p[4] = u32::from_le_bytes([padded[0], padded[1], padded[2], padded[3]]);
p[5] = u32::from_le_bytes([padded[4], padded[5], padded[6], padded[7]]);
padded.fill(0);
padded[..persona.len()].copy_from_slice(persona);
p[6] = u32::from_le_bytes([padded[0], padded[1], padded[2], padded[3]]);
p[7] = u32::from_le_bytes([padded[4], padded[5], padded[6], padded[7]]);
p[0] ^= IV[0];
p[1] ^= IV[1];
p[2] ^= IV[2];
p[3] ^= IV[3];
p[4] ^= IV[4];
p[5] ^= IV[5];
p[6] ^= IV[6];
p[7] ^= IV[7];
Ok(Self {
b: [0u8; 64],
h: p,
t: 0,
c: 0,
outlen: outlen as u8,
})
}
#[allow(clippy::cast_possible_truncation)]
pub fn new(outlen: usize, salt: &[u8], persona: &[u8], key: &[u8]) -> Result<Self, Error> {
let mut ctx = Self::new_with_params(outlen, salt, persona, key.len())?;
if !key.is_empty() {
ctx.update(key);
ctx.c = 64;
}
Ok(ctx)
}
pub fn new_with_defaults(outlen: usize, key: &[u8]) -> Result<Self, Error> {
Self::new(outlen, &[], &[], key)
}
pub fn finalize_with_flag(&mut self, out: &mut [u8], f1: u32) -> Result<(), Error> {
if out.len() != self.outlen as usize {
return Err(Error::InvalidInput);
}
self.t += self.c as u64;
while self.c < 64 {
self.b[self.c as usize] = 0;
self.c += 1;
}
self.compress(!0, f1);
for i in 0..self.outlen as usize {
out[i] = ((self.h[i >> 2] >> (8 * (i & 3))) & 0xff) as u8;
}
Ok(())
}
#[inline]
pub fn finalize(&mut self, out: &mut [u8]) -> Result<(), Error> {
self.finalize_with_flag(out, 0)
}
#[allow(clippy::too_many_lines)]
fn compress(&mut self, f0: u32, f1: u32) {
const SIGMA: [[u8; 16]; 10] = [
[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],
];
fn quarter_round(v: &mut [u32; 16], rd: u32, rb: u32, m: [u32; 4]) {
v[0] = v[0].wrapping_add(v[4]).wrapping_add(m[0]);
v[1] = v[1].wrapping_add(v[5]).wrapping_add(m[1]);
v[2] = v[2].wrapping_add(v[6]).wrapping_add(m[2]);
v[3] = v[3].wrapping_add(v[7]).wrapping_add(m[3]);
v[12] = (v[12] ^ v[0]).rotate_right(rd);
v[13] = (v[13] ^ v[1]).rotate_right(rd);
v[14] = (v[14] ^ v[2]).rotate_right(rd);
v[15] = (v[15] ^ v[3]).rotate_right(rd);
v[8] = v[8].wrapping_add(v[12]);
v[9] = v[9].wrapping_add(v[13]);
v[10] = v[10].wrapping_add(v[14]);
v[11] = v[11].wrapping_add(v[15]);
v[4] = (v[4] ^ v[8]).rotate_right(rb);
v[5] = (v[5] ^ v[9]).rotate_right(rb);
v[6] = (v[6] ^ v[10]).rotate_right(rb);
v[7] = (v[7] ^ v[11]).rotate_right(rb);
}
fn round(v: &mut [u32; 16], m: &[u32; 16], s: &[u8; 16]) {
quarter_round(
v,
16,
12,
[
m[s[0] as usize],
m[s[2] as usize],
m[s[4] as usize],
m[s[6] as usize],
],
);
quarter_round(
v,
8,
7,
[
m[s[1] as usize],
m[s[3] as usize],
m[s[5] as usize],
m[s[7] as usize],
],
);
(v[4], v[5], v[6], v[7]) = (v[5], v[6], v[7], v[4]);
(v[8], v[9], v[10], v[11]) = (v[10], v[11], v[8], v[9]);
(v[12], v[13], v[14], v[15]) = (v[15], v[12], v[13], v[14]);
quarter_round(
v,
16,
12,
[
m[s[8] as usize],
m[s[10] as usize],
m[s[12] as usize],
m[s[14] as usize],
],
);
quarter_round(
v,
8,
7,
[
m[s[9] as usize],
m[s[11] as usize],
m[s[13] as usize],
m[s[15] as usize],
],
);
(v[5], v[6], v[7], v[4]) = (v[4], v[5], v[6], v[7]);
(v[10], v[11], v[8], v[9]) = (v[8], v[9], v[10], v[11]);
(v[15], v[12], v[13], v[14]) = (v[12], v[13], v[14], v[15]);
}
let mut m = [0u32; 16];
for (v, chunk) in m.iter_mut().zip(self.b.chunks_exact(4)) {
*v = u32::from_le_bytes(chunk.try_into().unwrap());
}
let h = &mut self.h;
let t0 = (self.t & 0xffff_ffff) as u32;
let t1 = (self.t >> 32) as u32;
let mut v = [
h[0],
h[1],
h[2],
h[3],
h[4],
h[5],
h[6],
h[7],
IV[0],
IV[1],
IV[2],
IV[3],
IV[4] ^ t0,
IV[5] ^ t1,
IV[6] ^ f0,
IV[7] ^ f1,
];
for i in 0..SIGMA.len() {
round(&mut v, &m, &SIGMA[i]);
}
for i in 0..h.len() {
h[i] ^= v[i] ^ v[i + 8];
}
}
pub fn update(&mut self, data: &[u8]) {
for v in data {
if self.c == 64 {
self.t += self.c as u64;
self.compress(0, 0);
self.c = 0;
}
self.b[self.c as usize] = *v;
self.c += 1;
}
}
}
#[allow(clippy::cast_possible_truncation)]
pub fn blake2s(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<(), Error> {
let mut ctx = Blake2s::new(out.len(), &[], &[], key)?;
ctx.update(data);
ctx.finalize(out)
}
#[cfg(test)]
mod tests {
use super::{blake2s, Blake2s};
const GRAND_HASH: [u8; 32] = [
0x6A, 0x41, 0x1F, 0x08, 0xCE, 0x25, 0xAD, 0xCD, 0xFB, 0x02, 0xAB, 0xA6, 0x41, 0x45, 0x1C,
0xEC, 0x53, 0xC5, 0x98, 0xB2, 0x4F, 0x4F, 0xC7, 0x87, 0xFB, 0xDC, 0x88, 0x79, 0x7F, 0x4C,
0x1D, 0xFE,
];
const DIGEST_LENGTHS: [usize; 4] = [16, 20, 28, 32];
const INPUT_LENGTHS: [usize; 6] = [0, 3, 64, 65, 255, 1024];
fn selftest_seq(out: &mut [u8], seed: u32) {
let mut t: u32;
let mut a = 0xDEAD_4BADu32.overflowing_mul(seed).0;
let mut b = 1u32;
for i in 0..out.len() {
t = a.overflowing_add(b).0;
a = b;
b = t;
out[i] = ((t >> 24) & 0xff) as u8;
}
}
#[test]
fn selftest() {
let mut ctx = Blake2s::new_with_defaults(32, &[]).unwrap();
let mut input = [0u8; 1024];
let mut digest = [0u8; 32];
let mut key = [0u8; 32];
for i in 0..4 {
let outlen = DIGEST_LENGTHS[i];
for j in 0..6 {
let inlen = INPUT_LENGTHS[j];
selftest_seq(&mut input[..inlen], inlen as u32);
blake2s(&mut digest[..outlen], &[], &input[..inlen]).unwrap();
ctx.update(&digest[..outlen]);
selftest_seq(&mut key[..outlen], outlen as u32);
blake2s(&mut digest[..outlen], &key[..outlen], &input[..inlen]).unwrap();
ctx.update(&digest[..outlen]);
}
}
ctx.finalize(&mut digest).unwrap();
assert_eq!(digest, GRAND_HASH);
}
#[test]
fn test_blake2s256_unkeyed_abc() {
let mut result = [0u8; 32];
blake2s(&mut result, &[], b"abc").unwrap();
assert_eq!(
result,
[
0x50, 0x8c, 0x5e, 0x8c, 0x32, 0x7c, 0x14, 0xe2, 0xe1, 0xa7, 0x2b, 0xa3, 0x4e, 0xeb,
0x45, 0x2f, 0x37, 0x45, 0x8b, 0x20, 0x9e, 0xd6, 0x3a, 0x29, 0x4d, 0x99, 0x9b, 0x4c,
0x86, 0x67, 0x59, 0x82
]
);
}
}