debris / tiny-keccak

An implementation of Keccak derived functions specified in FIPS-202, SP800-185 and KangarooTwelve
Creative Commons Zero v1.0 Universal
193 stars 49 forks source link

`const fn` hashing #50

Open rubdos opened 3 years ago

rubdos commented 3 years ago

Hi!

I'm using Shake in a setting where all input data is known at compile-time, a case where the hash value could be computed at compile-time too.

I feel that Rust has everything needed for it, so I've been toying with const fn for all the things.

Main hurdles:

Would a const implementation be welcome? If so, do you have an idea to get around the trait problem?

My WIP const implementation ```diff+rust diff --git a/src/lib.rs b/src/lib.rs index 9329fd3..a46aa79 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,6 +38,7 @@ //! [`@oleganza`]: https://github.com/oleganza //! [`CC0`]: https://github.com/debris/tiny-keccak/blob/master/LICENSE +#![feature(const_mut_refs)] #![no_std] #![deny(missing_docs)] @@ -51,71 +52,83 @@ const PI: [usize; 24] = [ const WORDS: usize = 25; +macro_rules! const_for { + (for $i:ident in $begin:tt..$end:tt { $($body:tt)* }) => { + let mut $i = $begin; + while $i < $end { + $($body)* + $i += 1; + } + }; +} + macro_rules! keccak_function { ($doc: expr, $name: ident, $rounds: expr, $rc: expr) => { #[doc = $doc] #[allow(unused_assignments)] #[allow(non_upper_case_globals)] - pub fn $name(a: &mut [u64; $crate::WORDS]) { + pub const fn $name(a: &mut [u64; $crate::WORDS]) { use crunchy::unroll; - for i in 0..$rounds { - let mut array: [u64; 5] = [0; 5]; - - // Theta - unroll! { - for x in 0..5 { - unroll! { - for y_count in 0..5 { - let y = y_count * 5; - array[x] ^= a[x + y]; + const_for! { + for i in 0..$rounds { + let mut array: [u64; 5] = [0; 5]; + + // Theta + unroll! { + for x in 0..5 { + unroll! { + for y_count in 0..5 { + let y = y_count * 5; + array[x] ^= a[x + y]; + } } } } - } - unroll! { - for x in 0..5 { - unroll! { - for y_count in 0..5 { - let y = y_count * 5; - a[y + x] ^= array[(x + 4) % 5] ^ array[(x + 1) % 5].rotate_left(1); + unroll! { + for x in 0..5 { + unroll! { + for y_count in 0..5 { + let y = y_count * 5; + a[y + x] ^= array[(x + 4) % 5] ^ array[(x + 1) % 5].rotate_left(1); + } } } } - } - // Rho and pi - let mut last = a[1]; - unroll! { - for x in 0..24 { - array[0] = a[$crate::PI[x]]; - a[$crate::PI[x]] = last.rotate_left($crate::RHO[x]); - last = array[0]; + // Rho and pi + let mut last = a[1]; + unroll! { + for x in 0..24 { + array[0] = a[$crate::PI[x]]; + a[$crate::PI[x]] = last.rotate_left($crate::RHO[x]); + last = array[0]; + } } - } - // Chi - unroll! { - for y_step in 0..5 { - let y = y_step * 5; + // Chi + unroll! { + for y_step in 0..5 { + let y = y_step * 5; - unroll! { - for x in 0..5 { - array[x] = a[y + x]; + unroll! { + for x in 0..5 { + array[x] = a[y + x]; + } } - } - unroll! { - for x in 0..5 { - a[y + x] = array[x] ^ ((!array[(x + 1) % 5]) & (array[(x + 2) % 5])); + unroll! { + for x in 0..5 { + a[y + x] = array[x] ^ ((!array[(x + 1) % 5]) & (array[(x + 2) % 5])); + } } } - } - }; + }; - // Iota - a[0] ^= $rc[i]; + // Iota + a[0] ^= $rc[i]; + } } } } @@ -301,7 +314,7 @@ fn right_encode(len: usize) -> EncodedLen { struct Buffer([u64; WORDS]); impl Buffer { - fn words(&mut self) -> &mut [u64; WORDS] { + const fn words(&mut self) -> &mut [u64; WORDS] { &mut self.0 } ```

Best regards,

Ruben