diff --git a/hashers/src/ascon/mod.rs b/hashers/src/ascon/mod.rs index be2f7f7b..c56dff47 100644 --- a/hashers/src/ascon/mod.rs +++ b/hashers/src/ascon/mod.rs @@ -4,9 +4,8 @@ pub mod mac; const DEBUG: bool = false; fn padded_bytes_64(bytes: &[u8]) -> u64 { - if bytes.len() > 8 { - panic!("input block was too large") - } else if bytes.len() == 8 { + assert!(bytes.len() <= 8); + if bytes.len() == 8 { u64::from_be_bytes(bytes.try_into().unwrap()) } else { let mut word_bytes: [u8; 8] = [0; 8]; @@ -19,9 +18,8 @@ fn padded_bytes_64(bytes: &[u8]) -> u64 { } fn unpadded_bytes_64(bytes: &[u8]) -> u64 { - if bytes.len() > 8 { - panic!("input block was too large") - } else if bytes.len() == 8 { + assert!(bytes.len() <= 8); + if bytes.len() == 8 { u64::from_be_bytes(bytes.try_into().unwrap()) } else { let mut word_bytes: [u8; 8] = [0; 8]; @@ -33,9 +31,8 @@ fn unpadded_bytes_64(bytes: &[u8]) -> u64 { } fn unpadded_bytes_128(bytes: &[u8]) -> [u64; 2] { - if bytes.len() > 16 { - panic!("input block was too large") - } else if bytes.len() == 16 { + assert!(bytes.len() <= 16); + if bytes.len() == 16 { [ u64::from_be_bytes(bytes[0..8].try_into().unwrap()), u64::from_be_bytes(bytes[8..16].try_into().unwrap()), @@ -67,9 +64,8 @@ fn unpadded_bytes_128(bytes: &[u8]) -> [u64; 2] { // } fn padded_bytes_256(bytes: &[u8]) -> [u64; 4] { - if bytes.len() > 32 { - panic!("input block was too large") - } else if bytes.len() == 32 { + assert!(bytes.len() <= 32); + if bytes.len() == 32 { [ u64::from_be_bytes(bytes[0..8].try_into().unwrap()), u64::from_be_bytes(bytes[8..16].try_into().unwrap()), @@ -103,9 +99,8 @@ fn padded_bytes_256(bytes: &[u8]) -> [u64; 4] { } fn padded_bytes_320(bytes: &[u8]) -> [u64; 5] { - if bytes.len() > 40 { - panic!("input block was too large") - } else if bytes.len() == 40 { + assert!(bytes.len() <= 40); + if bytes.len() == 40 { [ u64::from_be_bytes(bytes[0..8].try_into().unwrap()), u64::from_be_bytes(bytes[8..16].try_into().unwrap()), diff --git a/hashers/src/blake/blake256.rs b/hashers/src/blake/blake256.rs index 31d6f968..abddf476 100644 --- a/hashers/src/blake/blake256.rs +++ b/hashers/src/blake/blake256.rs @@ -3,8 +3,6 @@ use utils::byte_formatting::{fill_u32s_be, ByteFormat}; use crate::{errors::HasherError, traits::ClassicHasher}; -use super::SIGMA; - // https://eprint.iacr.org/2012/351.pdf // Constants for compression function, beginning digits of pi @@ -118,25 +116,8 @@ impl Blake256 { work[14] = C[6] ^ (counter >> 32) as u32; // Upper bits work[15] = C[7] ^ (counter >> 32) as u32; - // At this point the working vector is correct, I have triple checked - // println!("work: {:08x?}\n", work); - for i in 0..14 { - let s = SIGMA[i % 10]; - - let a = [0, 1, 2, 3, 0, 1, 2, 3]; - let b = [4, 5, 6, 7, 5, 6, 7, 4]; - let c = [8, 9, 10, 11, 10, 11, 8, 9]; - let d = [12, 13, 14, 15, 15, 12, 13, 14]; - - // Apply the mixing function eight times, xoring the constants with the chunks of message - for j in 0..8 { - let x = chunk[s[2 * j]] ^ C[s[2 * j + 1]]; - let y = chunk[s[2 * j + 1]] ^ C[s[2 * j]]; - Self::mix(&mut work, a[j], b[j], c[j], d[j], x, y); - } + crate::blake_compress!(&mut work, chunk, [16, 12, 8, 7], C, 14); - // println!("work {}:\n{:08x?}\n", i + 1, work); - } for i in 0..8 { state[i] ^= salt[i % 4] ^ work[i] ^ work[i + 8]; } diff --git a/hashers/src/blake/blake2b.rs b/hashers/src/blake/blake2b.rs index 7d5e4c67..332ec40a 100644 --- a/hashers/src/blake/blake2b.rs +++ b/hashers/src/blake/blake2b.rs @@ -3,8 +3,6 @@ use utils::byte_formatting::ByteFormat; use crate::traits::ClassicHasher; -use super::SIGMA; - // https://eprint.iacr.org/2012/351.pdf #[derive(Debug, Clone)] @@ -62,20 +60,6 @@ impl Blake2b { self } - pub 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); - } - // https://datatracker.ietf.org/doc/html/rfc7693.html#appendix-A pub fn compress(state: &mut [u64; 8], chunk: &[u64; 16], bytes_taken: u128, last_chunk: bool) { // println!("Original Chunk:\n{chunk:016x?}\n"); @@ -94,21 +78,8 @@ impl Blake2b { if last_chunk { work[14] ^= u64::MAX; } - // println!("Working Vector Before Compression:\n{work:016x?}\n"); - for i in 0..12 { - let s = SIGMA[i % 10]; - - Self::mix(&mut work, 0, 4, 8, 12, chunk[s[0]], chunk[s[1]]); - Self::mix(&mut work, 1, 5, 9, 13, chunk[s[2]], chunk[s[3]]); - Self::mix(&mut work, 2, 6, 10, 14, chunk[s[4]], chunk[s[5]]); - Self::mix(&mut work, 3, 7, 11, 15, chunk[s[6]], chunk[s[7]]); - - Self::mix(&mut work, 0, 5, 10, 15, chunk[s[8]], chunk[s[9]]); - Self::mix(&mut work, 1, 6, 11, 12, chunk[s[10]], chunk[s[11]]); - Self::mix(&mut work, 2, 7, 8, 13, chunk[s[12]], chunk[s[13]]); - Self::mix(&mut work, 3, 4, 9, 14, chunk[s[14]], chunk[s[15]]); - // println!("Working Vector at [{i}]:\n{work:016x?}\n"); - } + + crate::blake_compress!(&mut work, chunk, [32, 24, 16, 63], 12); for i in 0..8 { state[i] ^= work[i]; diff --git a/hashers/src/blake/blake2s.rs b/hashers/src/blake/blake2s.rs index 2077cd05..27e9af13 100644 --- a/hashers/src/blake/blake2s.rs +++ b/hashers/src/blake/blake2s.rs @@ -3,8 +3,6 @@ use utils::byte_formatting::ByteFormat; use crate::traits::ClassicHasher; -use super::SIGMA; - // https://eprint.iacr.org/2012/351.pdf #[derive(Debug, Clone)] @@ -56,20 +54,6 @@ impl Blake2s { self } - pub fn mix(v: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize, x: u32, y: u32) { - v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); - 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(12); - - v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); - v[d] = (v[d] ^ v[a]).rotate_right(8); - - v[c] = v[c].wrapping_add(v[d]); - v[b] = (v[b] ^ v[c]).rotate_right(7); - } - // https://datatracker.ietf.org/doc/html/rfc7693.html#appendix-A pub fn compress(state: &mut [u32; 8], chunk: &[u32; 16], bytes_taken: u64, last_chunk: bool) { // println!("Original Chunk:\n{chunk:016x?}\n"); @@ -88,21 +72,8 @@ impl Blake2s { if last_chunk { work[14] ^= u32::MAX; } - // println!("Working Vector Before Compression:\n{work:016x?}\n"); - for i in 0..10 { - let s = SIGMA[i]; - - Self::mix(&mut work, 0, 4, 8, 12, chunk[s[0]], chunk[s[1]]); - Self::mix(&mut work, 1, 5, 9, 13, chunk[s[2]], chunk[s[3]]); - Self::mix(&mut work, 2, 6, 10, 14, chunk[s[4]], chunk[s[5]]); - Self::mix(&mut work, 3, 7, 11, 15, chunk[s[6]], chunk[s[7]]); - - Self::mix(&mut work, 0, 5, 10, 15, chunk[s[8]], chunk[s[9]]); - Self::mix(&mut work, 1, 6, 11, 12, chunk[s[10]], chunk[s[11]]); - Self::mix(&mut work, 2, 7, 8, 13, chunk[s[12]], chunk[s[13]]); - Self::mix(&mut work, 3, 4, 9, 14, chunk[s[14]], chunk[s[15]]); - // println!("Working Vector at [{i}]:\n{work:016x?}\n"); - } + + crate::blake_compress!(&mut work, chunk, [16, 12, 8, 7], 10); for i in 0..8 { state[i] ^= work[i]; @@ -178,19 +149,16 @@ impl ClassicHasher for Blake2s { crate::hash_bytes_from_string! {} } -#[cfg(test)] -mod blake2s_tests { - use super::*; - - #[test] - fn test_empty() { - let mut hasher = Blake2s::default(); - hasher.input_format = ByteFormat::Utf8; - hasher.output_format = ByteFormat::Hex; - hasher.hash_len = 32; - assert_eq!( - "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9", - hasher.hash_bytes_from_string("").unwrap() - ); - } -} +crate::basic_hash_tests!( + Blake2s::default().hash_len(32), empty_hash_len_32, "", + "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"; + Blake2s::default().hash_len(32).input(ByteFormat::Hex), hash_8_len_32, "0001020304050607", + "c7e887b546623635e93e0495598f1726821996c2377705b93a1f636f872bfa2d"; + Blake2s::default() + .input(ByteFormat::Hex) + .hash_len(32) + .key(ByteFormat::Hex.text_to_bytes("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap()), + keyed_hash_len_32, + "000102030405060708090a0b0c0d0e0f", + "19ba234f0a4f38637d1839f9d9f76ad91c8522307143c97d5f93f69274cec9a7"; +); diff --git a/hashers/src/blake/blake512.rs b/hashers/src/blake/blake512.rs index 2c1f51ae..d5b8b138 100644 --- a/hashers/src/blake/blake512.rs +++ b/hashers/src/blake/blake512.rs @@ -3,8 +3,6 @@ use utils::byte_formatting::{fill_u64s_be, ByteFormat}; use crate::{errors::HasherError, traits::ClassicHasher}; -use super::SIGMA; - // https://eprint.iacr.org/2012/351.pdf // Constants for compression function, beginning digits of pi @@ -144,25 +142,8 @@ impl Blake512 { work[14] = C[6] ^ (counter >> 64) as u64; // Upper bits work[15] = C[7] ^ (counter >> 64) as u64; - // At this point the working vector is correct, I have triple checked - // println!("work: {:016x?}\n", work); - for i in 0..16 { - let s = SIGMA[i % 10]; - - let a = [0, 1, 2, 3, 0, 1, 2, 3]; - let b = [4, 5, 6, 7, 5, 6, 7, 4]; - let c = [8, 9, 10, 11, 10, 11, 8, 9]; - let d = [12, 13, 14, 15, 15, 12, 13, 14]; - - // Apply the mixing function eight times, xoring the constants with the chunks of message - for j in 0..8 { - let x = chunk[s[2 * j]] ^ C[s[2 * j + 1]]; - let y = chunk[s[2 * j + 1]] ^ C[s[2 * j]]; - Self::mix(&mut work, a[j], b[j], c[j], d[j], x, y); - } + crate::blake_compress!(&mut work, chunk, [32, 25, 16, 11], C, 16); - // println!("work {}:\n{:016x?}\n", i + 1, work); - } for i in 0..8 { state[i] ^= salt[i % 4] ^ work[i] ^ work[i + 8]; } diff --git a/hashers/src/blake/mod.rs b/hashers/src/blake/mod.rs index 591e5688..26914947 100644 --- a/hashers/src/blake/mod.rs +++ b/hashers/src/blake/mod.rs @@ -20,3 +20,85 @@ const SIGMA: [[usize; 16]; 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], ]; + +// These are the constants for how BLAKE and BLAKE2 mix along columns then rows +const A: [usize; 8] = [0, 1, 2, 3, 0, 1, 2, 3]; +const B: [usize; 8] = [4, 5, 6, 7, 5, 6, 7, 4]; +const C: [usize; 8] = [8, 9, 10, 11, 10, 11, 8, 9]; +const D: [usize; 8] = [12, 13, 14, 15, 15, 12, 13, 14]; + +// This is the mixing function (quarter round) used by BLAKE and BLAKE2 written in a type agnostic form +// information in $v (the working vector) is spread across four of its indexes ($a, $b, $c, $d) which +// are chosen to represent columns and rows of $v when it is viewed as a 4x4 array +#[macro_export] +macro_rules! blake_mix { + ($v: expr, $a: expr, $b: expr, $c: expr, $d: expr, $x: expr, $y: expr, $r: expr) => { + $v[$a] = $v[$a].wrapping_add($v[$b]).wrapping_add($x); + $v[$d] = ($v[$d] ^ $v[$a]).rotate_right($r[0]); + + $v[$c] = $v[$c].wrapping_add($v[$d]); + $v[$b] = ($v[$b] ^ $v[$c]).rotate_right($r[1]); + + $v[$a] = $v[$a].wrapping_add($v[$b]).wrapping_add($y); + $v[$d] = ($v[$d] ^ $v[$a]).rotate_right($r[2]); + + $v[$c] = $v[$c].wrapping_add($v[$d]); + $v[$b] = ($v[$b] ^ $v[$c]).rotate_right($r[3]); + }; +} + +// The double round in type agnosic form. +// Applies the quarter round eight times. The second variant of the +// macro XORs in values from a constant as in the original BLAKE +#[macro_export] +macro_rules! blake_double_round { + ($v: expr, $chunk: ident, $r: expr, $s: ident) => { + for j in 0..8 { + let x = $chunk[$s[2 * j]]; + let y = $chunk[$s[2 * j + 1]]; + crate::blake_mix!( + $v, + super::A[j], + super::B[j], + super::C[j], + super::D[j], + x, + y, + $r + ); + } + }; + ($v: expr, $chunk: ident, $r: expr, $s: ident, $c: ident) => { + for j in 0..8 { + let x = $chunk[$s[2 * j]] ^ $c[$s[2 * j + 1]]; + let y = $chunk[$s[2 * j + 1]] ^ $c[$s[2 * j]]; + crate::blake_mix!( + $v, + super::A[j], + super::B[j], + super::C[j], + super::D[j], + x, + y, + $r + ); + } + }; +} + +// A sequence of compression rounds +#[macro_export] +macro_rules! blake_compress { + ($v: expr, $chunk: ident, $r: expr, $n: literal) => { + for i in 0..$n { + let s = super::SIGMA[i % 10]; + crate::blake_double_round!($v, $chunk, $r, s); + } + }; + ($v: expr, $chunk: ident, $r: expr, $c: ident, $n: literal) => { + for i in 0..$n { + let s = super::SIGMA[i % 10]; + crate::blake_double_round!($v, $chunk, $r, s, $c); + } + }; +}