mirror of
https://github.com/BLAKE3-team/BLAKE3
synced 2024-09-25 07:50:42 +02:00
037de38bfe
This version uses const generics, which bumps our minimum supported compiler version to 1.51.
199 lines
5.3 KiB
Rust
199 lines
5.3 KiB
Rust
use crate::{
|
|
counter_high, counter_low, CVBytes, CVWords, IncrementCounter, BLOCK_LEN, IV, MSG_SCHEDULE,
|
|
OUT_LEN,
|
|
};
|
|
use arrayref::{array_mut_ref, array_ref};
|
|
|
|
#[inline(always)]
|
|
fn g(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize, x: u32, y: u32) {
|
|
state[a] = state[a].wrapping_add(state[b]).wrapping_add(x);
|
|
state[d] = (state[d] ^ state[a]).rotate_right(16);
|
|
state[c] = state[c].wrapping_add(state[d]);
|
|
state[b] = (state[b] ^ state[c]).rotate_right(12);
|
|
state[a] = state[a].wrapping_add(state[b]).wrapping_add(y);
|
|
state[d] = (state[d] ^ state[a]).rotate_right(8);
|
|
state[c] = state[c].wrapping_add(state[d]);
|
|
state[b] = (state[b] ^ state[c]).rotate_right(7);
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn round(state: &mut [u32; 16], msg: &[u32; 16], round: usize) {
|
|
// Select the message schedule based on the round.
|
|
let schedule = MSG_SCHEDULE[round];
|
|
|
|
// Mix the columns.
|
|
g(state, 0, 4, 8, 12, msg[schedule[0]], msg[schedule[1]]);
|
|
g(state, 1, 5, 9, 13, msg[schedule[2]], msg[schedule[3]]);
|
|
g(state, 2, 6, 10, 14, msg[schedule[4]], msg[schedule[5]]);
|
|
g(state, 3, 7, 11, 15, msg[schedule[6]], msg[schedule[7]]);
|
|
|
|
// Mix the diagonals.
|
|
g(state, 0, 5, 10, 15, msg[schedule[8]], msg[schedule[9]]);
|
|
g(state, 1, 6, 11, 12, msg[schedule[10]], msg[schedule[11]]);
|
|
g(state, 2, 7, 8, 13, msg[schedule[12]], msg[schedule[13]]);
|
|
g(state, 3, 4, 9, 14, msg[schedule[14]], msg[schedule[15]]);
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn compress_pre(
|
|
cv: &CVWords,
|
|
block: &[u8; BLOCK_LEN],
|
|
block_len: u8,
|
|
counter: u64,
|
|
flags: u8,
|
|
) -> [u32; 16] {
|
|
let block_words = crate::platform::words_from_le_bytes_64(block);
|
|
|
|
let mut state = [
|
|
cv[0],
|
|
cv[1],
|
|
cv[2],
|
|
cv[3],
|
|
cv[4],
|
|
cv[5],
|
|
cv[6],
|
|
cv[7],
|
|
IV[0],
|
|
IV[1],
|
|
IV[2],
|
|
IV[3],
|
|
counter_low(counter),
|
|
counter_high(counter),
|
|
block_len as u32,
|
|
flags as u32,
|
|
];
|
|
|
|
round(&mut state, &block_words, 0);
|
|
round(&mut state, &block_words, 1);
|
|
round(&mut state, &block_words, 2);
|
|
round(&mut state, &block_words, 3);
|
|
round(&mut state, &block_words, 4);
|
|
round(&mut state, &block_words, 5);
|
|
round(&mut state, &block_words, 6);
|
|
|
|
state
|
|
}
|
|
|
|
pub fn compress_in_place(
|
|
cv: &mut CVWords,
|
|
block: &[u8; BLOCK_LEN],
|
|
block_len: u8,
|
|
counter: u64,
|
|
flags: u8,
|
|
) {
|
|
let state = compress_pre(cv, block, block_len, counter, flags);
|
|
|
|
cv[0] = state[0] ^ state[8];
|
|
cv[1] = state[1] ^ state[9];
|
|
cv[2] = state[2] ^ state[10];
|
|
cv[3] = state[3] ^ state[11];
|
|
cv[4] = state[4] ^ state[12];
|
|
cv[5] = state[5] ^ state[13];
|
|
cv[6] = state[6] ^ state[14];
|
|
cv[7] = state[7] ^ state[15];
|
|
}
|
|
|
|
pub fn compress_xof(
|
|
cv: &CVWords,
|
|
block: &[u8; BLOCK_LEN],
|
|
block_len: u8,
|
|
counter: u64,
|
|
flags: u8,
|
|
) -> [u8; 64] {
|
|
let mut state = compress_pre(cv, block, block_len, counter, flags);
|
|
state[0] ^= state[8];
|
|
state[1] ^= state[9];
|
|
state[2] ^= state[10];
|
|
state[3] ^= state[11];
|
|
state[4] ^= state[12];
|
|
state[5] ^= state[13];
|
|
state[6] ^= state[14];
|
|
state[7] ^= state[15];
|
|
state[8] ^= cv[0];
|
|
state[9] ^= cv[1];
|
|
state[10] ^= cv[2];
|
|
state[11] ^= cv[3];
|
|
state[12] ^= cv[4];
|
|
state[13] ^= cv[5];
|
|
state[14] ^= cv[6];
|
|
state[15] ^= cv[7];
|
|
crate::platform::le_bytes_from_words_64(&state)
|
|
}
|
|
|
|
pub fn hash1<const N: usize>(
|
|
input: &[u8; N],
|
|
key: &CVWords,
|
|
counter: u64,
|
|
flags: u8,
|
|
flags_start: u8,
|
|
flags_end: u8,
|
|
out: &mut CVBytes,
|
|
) {
|
|
debug_assert_eq!(N % BLOCK_LEN, 0, "uneven blocks");
|
|
let mut cv = *key;
|
|
let mut block_flags = flags | flags_start;
|
|
let mut slice = &input[..];
|
|
while slice.len() >= BLOCK_LEN {
|
|
if slice.len() == BLOCK_LEN {
|
|
block_flags |= flags_end;
|
|
}
|
|
compress_in_place(
|
|
&mut cv,
|
|
array_ref!(slice, 0, BLOCK_LEN),
|
|
BLOCK_LEN as u8,
|
|
counter,
|
|
block_flags,
|
|
);
|
|
block_flags = flags;
|
|
slice = &slice[BLOCK_LEN..];
|
|
}
|
|
*out = crate::platform::le_bytes_from_words_32(&cv);
|
|
}
|
|
|
|
pub fn hash_many<const N: usize>(
|
|
inputs: &[&[u8; N]],
|
|
key: &CVWords,
|
|
mut counter: u64,
|
|
increment_counter: IncrementCounter,
|
|
flags: u8,
|
|
flags_start: u8,
|
|
flags_end: u8,
|
|
out: &mut [u8],
|
|
) {
|
|
debug_assert!(out.len() >= inputs.len() * OUT_LEN, "out too short");
|
|
for (&input, output) in inputs.iter().zip(out.chunks_exact_mut(OUT_LEN)) {
|
|
hash1(
|
|
input,
|
|
key,
|
|
counter,
|
|
flags,
|
|
flags_start,
|
|
flags_end,
|
|
array_mut_ref!(output, 0, OUT_LEN),
|
|
);
|
|
if increment_counter.yes() {
|
|
counter += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub mod test {
|
|
use super::*;
|
|
|
|
// This is basically testing the portable implementation against itself,
|
|
// but it also checks that compress_in_place and compress_xof are
|
|
// consistent. And there are tests against the reference implementation and
|
|
// against hardcoded test vectors elsewhere.
|
|
#[test]
|
|
fn test_compress() {
|
|
crate::test::test_compress_fn(compress_in_place, compress_xof);
|
|
}
|
|
|
|
// Ditto.
|
|
#[test]
|
|
fn test_hash_many() {
|
|
crate::test::test_hash_many_fn(hash_many, hash_many);
|
|
}
|
|
}
|