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:
The "rounds" for loop. Solved by a macro that transforms the for loop into a while loop. We could just manually make it a while loop too, probably simpler. The other for loops are unrolled, so nothing more needed. keccakf is now const fn.
The Permutation trait. A trait function cannot be const. Not sure how to solve; we could get rid of the trait entirely (since there's only one permutation).
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
}
```
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:
keccakf
is nowconst fn
.Permutation
trait. A trait function cannot beconst
. Not sure how to solve; we could get rid of the trait entirely (since there's only one permutation).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