Hash: use optimized rotation constants.

Also use random constants for the initial hash state.  Changed my
mind about using zero initialization.  It doesn't make a practical
difference, but helps esnure that the mixing is randomized.
This commit is contained in:
Nathan Vegdahl 2022-09-03 20:59:10 -07:00
parent 776438e866
commit f8ba027c38

View File

@ -33,7 +33,12 @@ pub struct LedHash256 {
impl LedHash256 { impl LedHash256 {
pub fn new() -> LedHash256 { pub fn new() -> LedHash256 {
LedHash256 { LedHash256 {
state: [0, 0, 0, 0], state: [
0xe2b8d3b67882709f,
0x045e21ec46bcea22,
0x51ea37fa96fbae67,
0xf5d94991b6b9b944,
],
buf: [0; BLOCK_SIZE], buf: [0; BLOCK_SIZE],
buf_length: 0, buf_length: 0,
message_length: 0, message_length: 0,
@ -141,28 +146,16 @@ fn add_data_to_state(state: &mut [u64; 4], data: &[u8]) {
/// Mixes the passed hash state. /// Mixes the passed hash state.
/// ///
/// Inspired by Skein 1.3, and using its MIX function and rotation /// Inspired by Skein 1.3's MIX function and permutation approach.
/// constants.
/// ///
/// Each round actually applies the MIX function twice, with a different /// 6 rounds is enough for each bit to have a reasonable chance of
/// word permutation such that each word affects every other word once /// affecting most other bits. 9 rounds is enough for full diffusion.
/// per round.
///
/// 3 rounds is enough for each bit to have a reasonable chance of
/// affecting most other bits: one input bit flip on average causes about
/// 110 output bit flips. 5 rounds is enough for full diffusion.
#[inline(always)] #[inline(always)]
fn mix_state(state: &mut [u64; 4], rounds: usize) { fn mix_state(state: &mut [u64; 4], rounds: usize) {
// Rotation constants from Skein-256-256 v1.3. const ROTATIONS: &[[u32; 2]] = &[[31, 25], [5, 48], [20, 34], [21, 57], [11, 41], [18, 33]];
const ROTATIONS: &[[u32; 4]] = &[
[14, 16, 52, 57],
[23, 40, 5, 37],
[25, 33, 46, 12],
[58, 22, 32, 32],
];
debug_assert!(rounds % 2 == 0); debug_assert!(rounds % 2 == 0);
for round in 0..(rounds / 2) { for round in 0..rounds {
let rot = ROTATIONS[round % ROTATIONS.len()]; let rot = ROTATIONS[round % ROTATIONS.len()];
// MIX function. // MIX function.
@ -171,14 +164,7 @@ fn mix_state(state: &mut [u64; 4], rounds: usize) {
state[1] = state[1].wrapping_add(state[3]); state[1] = state[1].wrapping_add(state[3]);
state[3] = state[3].rotate_left(rot[1]) ^ state[1]; state[3] = state[3].rotate_left(rot[1]) ^ state[1];
// We change the indices we use below, as if we state.swap(2, 3);
// did a [0 1 2 3] -> [0 1 3 2] permutation.
// MIX function.
state[0] = state[0].wrapping_add(state[3]);
state[3] = state[3].rotate_left(rot[2]) ^ state[0];
state[1] = state[1].wrapping_add(state[2]);
state[2] = state[2].rotate_left(rot[3]) ^ state[1];
} }
} }
@ -194,41 +180,41 @@ mod test {
#[test] #[test]
fn hash_empty() { fn hash_empty() {
let correct_digest = "0000000000000000000000000000000000000000000000000000000000000000"; let correct_digest = "e0d4e0a2608a8741e349fa1ea0263fedbd65f66dfbcbcecd77a334c809424cb6";
assert_eq!(digest_to_string(&hash(&[])), correct_digest); assert_eq!(digest_to_string(&hash(&[])), correct_digest);
} }
#[test] #[test]
fn hash_zero() { fn hash_zero() {
let correct_digest = "c3da6dcfc0b3b293ddd210be8922b36d401ab8bac6ee0717d3f4e6f5b6a91183"; let correct_digest = "6e5f483d20443bb6e70c300b0a5aa64ce36d346793ea62d53a198d8ae48f60f3";
assert_eq!(digest_to_string(&hash(&[0u8])), correct_digest); assert_eq!(digest_to_string(&hash(&[0u8])), correct_digest);
} }
#[test] #[test]
fn hash_string_01() { fn hash_string_01() {
let s = "0123456789"; let s = "0123456789";
let correct_digest = "07ee14811136870400e801bcf2790f5c560d2d76fcc8bf83e4c33facb024ff3f"; let correct_digest = "f12f795967313e9a0e822edaa307c3d7b7d19ce38773955e4ba25aa0bce17f56";
assert_eq!(digest_to_string(&hash(s.as_bytes())), correct_digest); assert_eq!(digest_to_string(&hash(s.as_bytes())), correct_digest);
} }
#[test] #[test]
fn hash_string_02() { fn hash_string_02() {
let s = "abcdefghijklmnopqrstuvwxyz"; let s = "abcdefghijklmnopqrstuvwxyz";
let correct_digest = "a8bf2da4476da5cdf0020bf0ffb462b3baa7fd4799de05e4fbe01df4c0a659dc"; let correct_digest = "8f578c05439217eeac0dc46d7df2805f91ffad995468eca8cf9f8eb954f7da18";
assert_eq!(digest_to_string(&hash(s.as_bytes())), correct_digest); assert_eq!(digest_to_string(&hash(s.as_bytes())), correct_digest);
} }
#[test] #[test]
fn hash_string_03() { fn hash_string_03() {
let s = "The quick brown fox jumps over the lazy dog."; let s = "The quick brown fox jumps over the lazy dog.";
let correct_digest = "535b546e3d9e0b591b14fda86906967ecf5525b593898bbcd42ed88e12ce697f"; let correct_digest = "0be19c6dc03f6800743e41c70f0ee0c2d75bad674f8496fea75a18af280e6100";
assert_eq!(digest_to_string(&hash(s.as_bytes())), correct_digest); assert_eq!(digest_to_string(&hash(s.as_bytes())), correct_digest);
} }
#[test] #[test]
fn hash_string_04() { fn hash_string_04() {
let s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; let s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
let correct_digest = "ccb76a0eaba46d8d53e536649e96048af3d15def148134dc963117dd485e6761"; let correct_digest = "6faf47daac8a767a1d7ed6da36cbe50616a1b83ae9b2802564c948a4283f7833";
assert_eq!(digest_to_string(&hash(s.as_bytes())), correct_digest); assert_eq!(digest_to_string(&hash(s.as_bytes())), correct_digest);
} }
@ -241,7 +227,7 @@ mod test {
let test_string4 = "cup"; let test_string4 = "cup";
let test_string5 = let test_string5 =
"idatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; "idatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
let correct_digest = "ccb76a0eaba46d8d53e536649e96048af3d15def148134dc963117dd485e6761"; let correct_digest = "6faf47daac8a767a1d7ed6da36cbe50616a1b83ae9b2802564c948a4283f7833";
let mut hasher = LedHash256::new(); let mut hasher = LedHash256::new();
hasher.update(test_string1.as_bytes()); hasher.update(test_string1.as_bytes());
@ -266,15 +252,15 @@ mod test {
assert_eq!( assert_eq!(
digest_to_string(&hash(len_0)), digest_to_string(&hash(len_0)),
"0000000000000000000000000000000000000000000000000000000000000000", "e0d4e0a2608a8741e349fa1ea0263fedbd65f66dfbcbcecd77a334c809424cb6",
); );
assert_eq!( assert_eq!(
digest_to_string(&hash(len_1)), digest_to_string(&hash(len_1)),
"c3da6dcfc0b3b293ddd210be8922b36d401ab8bac6ee0717d3f4e6f5b6a91183", "6e5f483d20443bb6e70c300b0a5aa64ce36d346793ea62d53a198d8ae48f60f3",
); );
assert_eq!( assert_eq!(
digest_to_string(&hash(len_2)), digest_to_string(&hash(len_2)),
"68b6ac9a09eeb50bfc0ac9c851c59773789350c1e085b1d7566a8a3563fbda20", "ffaf1c6954edb55a7ac10c16b6f309c8e1cc7b5c29e4fd4992eb0f416b965ee0",
); );
} }
} }