diff --git a/plonky2/src/hash/keccak.rs b/plonky2/src/hash/keccak.rs index 7bb2f133..84275091 100644 --- a/plonky2/src/hash/keccak.rs +++ b/plonky2/src/hash/keccak.rs @@ -1,3 +1,5 @@ +use std::mem::size_of; + use keccak_hash::keccak; use crate::hash::hash_types::{BytesHash, RichField}; @@ -5,37 +7,49 @@ use crate::hash::hashing::{PlonkyPermutation, SPONGE_WIDTH}; use crate::plonk::config::Hasher; use crate::util::serialization::Buffer; -/// Keccak-256 permutation used in the challenger. +/// Keccak-256 pseudo-permutation (not necessarily one-to-one) used in the challenger. /// A state `input: [F; 12]` is sent to the field representation of `H(input) || H(H(input)) || H(H(H(input)))` /// where `H` is the Keccak-256 hash. pub struct KeccakPermutation; impl PlonkyPermutation for KeccakPermutation { fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] { - // Fill a byte array with the little-endian representation of the field array. - let mut buffer = [0u8; SPONGE_WIDTH * std::mem::size_of::()]; - for i in 0..SPONGE_WIDTH { - buffer[i * std::mem::size_of::()..(i + 1) * std::mem::size_of::()] - .copy_from_slice(&input[i].to_canonical_u64().to_le_bytes()); + // Use rejection sampling so that if one of the `u64` values in the output is larger than + // the field order, we increment the nonce and start again. + 'rejection_sampling: for nonce in 0u64.. { + // Fill a byte array with the little-endian representation of the field array. + let mut buffer = [0u8; (SPONGE_WIDTH + 1) * size_of::()]; + for i in 0..SPONGE_WIDTH { + buffer[i * size_of::()..(i + 1) * size_of::()] + .copy_from_slice(&input[i].to_canonical_u64().to_le_bytes()); + } + // Add the nonce at the end of the buffer. + buffer[SPONGE_WIDTH * size_of::()..].copy_from_slice(&nonce.to_le_bytes()); + // Concatenate `H(input), H(H(input)), H(H(H(input)))`. + let permutated_input_bytes = { + let mut ans = [0u8; 96]; + ans[0..32].copy_from_slice(&keccak(buffer).0); + ans[32..64].copy_from_slice(&keccak(keccak(buffer).0).0); + ans[64..96].copy_from_slice(&keccak(keccak(keccak(buffer).0).0).0); + ans + }; + // Write the hashed byte array to a field array. + let mut permutated_input = [F::ZERO; SPONGE_WIDTH]; + for i in 0..SPONGE_WIDTH { + let perm_u64 = u64::from_le_bytes( + permutated_input_bytes[i * size_of::()..(i + 1) * size_of::()] + .try_into() + .unwrap(), + ); + if perm_u64 >= F::ORDER { + // If a value is larger than the field order, we break and start again with a new nonce. + continue 'rejection_sampling; + } else { + permutated_input[i] = F::from_canonical_u64(perm_u64); + } + } + return permutated_input; } - // Concatenate `H(input), H(H(input)), H(H(H(input)))`. - let permutated_input_bytes = { - let mut ans = [0u8; 96]; - ans[0..32].copy_from_slice(&keccak(buffer).0); - ans[32..64].copy_from_slice(&keccak(keccak(buffer).0).0); - ans[64..96].copy_from_slice(&keccak(keccak(keccak(buffer).0).0).0); - ans - }; - // Write the hashed byte array to a field array. - let mut permutated_input = [F::ZERO; SPONGE_WIDTH]; - for i in 0..SPONGE_WIDTH { - permutated_input[i] = F::from_noncanonical_u64(u64::from_le_bytes( - permutated_input_bytes - [i * std::mem::size_of::()..(i + 1) * std::mem::size_of::()] - .try_into() - .unwrap(), - )); - } - permutated_input + panic!("Improbable.") } }