Skip to main content

risc0_zkp/core/hash/
mod.rs

1// Copyright 2026 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Traits to configure which cryptographic primitives the ZKP uses
16
17pub mod blake2b;
18pub mod poseidon2;
19#[cfg(feature = "prove")]
20pub mod poseidon_254;
21pub mod sha;
22
23use alloc::{boxed::Box, rc::Rc, string::String};
24
25use risc0_core::field::{baby_bear::BabyBear, Field};
26
27use super::digest::Digest;
28
29/// A trait that sets the hashes and encodings used by the ZKP.
30pub trait HashFn<F: Field>: Send + Sync {
31    /// Generate a hash from a pair of [Digest].
32    fn hash_pair(&self, a: &Digest, b: &Digest) -> Box<Digest>;
33
34    /// Generate a hash from a slice of field elements.  This may be unpadded so
35    /// this is only safe to used when the size is known.
36    fn hash_elem_slice(&self, slice: &[F::Elem]) -> Box<Digest>;
37
38    /// Generate a hash from a slice of extension field element.  This may be
39    /// unpadded so this is only safe to used when the size is known.
40    fn hash_ext_elem_slice(&self, slice: &[F::ExtElem]) -> Box<Digest>;
41
42    /// Checks whether the given [Digest] is valid for this hash function.
43    ///
44    /// Poseidon2 considers a Digest invalid is any of the words in the digest are not reduced Baby
45    /// Bear elements.
46    // NOTE: The HashFn API requires the caller to be diligent about calling is_digest_valid before
47    // calling hash_pair to avoid a panic (as Poseidon2 and Poseidon over BN254 will do). This
48    // could be embedded in the type signature instead if Digest was an associated type. However,
49    // this would be a major change and would make using `dyn` more complicated. If that change is
50    // implemented, this function should be removed.
51    fn is_digest_valid(&self, digest: &Digest) -> bool {
52        #![expect(unused_variables)]
53        true
54    }
55}
56
57/// A trait that sets the PRNG used by Fiat-Shamir.  We allow specialization at
58/// this level rather than at RngCore because some hashes such as Poseidon have
59/// elements distributed uniformly over the field natively.
60pub trait Rng<F: Field> {
61    /// Mix in randomness from a Fiat-Shamir commitment.
62    fn mix(&mut self, val: &Digest);
63
64    /// Get a cryptographically uniform set of bits, as the low order bits of a
65    /// u32
66    fn random_bits(&mut self, bits: usize) -> u32;
67
68    /// Get a cryptographically uniform field element
69    fn random_elem(&mut self) -> F::Elem;
70
71    /// Get a cryptographically uniform extension field element
72    fn random_ext_elem(&mut self) -> F::ExtElem;
73}
74
75/// Responsible for constructing new Rngs.
76pub trait RngFactory<F: Field> {
77    /// Construct a new Rng
78    fn new_rng(&self) -> Box<dyn Rng<F>>;
79}
80
81/// Make it easy compute both hash related traits from a single source
82pub struct HashSuite<F: Field> {
83    /// The name of this HashSuite.
84    pub name: String,
85
86    /// Define the hash used by the HashSuite
87    pub hashfn: Rc<dyn HashFn<F>>,
88
89    /// Define an RNG factory
90    pub rng: Rc<dyn RngFactory<F>>,
91}
92
93impl<F: Field> Clone for HashSuite<F> {
94    fn clone(&self) -> Self {
95        Self {
96            name: self.name.clone(),
97            hashfn: self.hashfn.clone(),
98            rng: self.rng.clone(),
99        }
100    }
101}
102
103/// Construct a supported hash function given its name. Returns None is the name does not
104/// correspond to a supported hash function.
105pub fn hash_suite_from_name(name: impl AsRef<str>) -> Option<HashSuite<BabyBear>> {
106    match name.as_ref() {
107        "sha-256" => Some(sha::Sha256HashSuite::new_suite()),
108        "poseidon2" => Some(poseidon2::Poseidon2HashSuite::new_suite()),
109        "blake2b" => Some(blake2b::Blake2bCpuHashSuite::new_suite()),
110        #[cfg(feature = "prove")]
111        "poseidon_254" => Some(poseidon_254::Poseidon254HashSuite::new_suite()),
112        _ => None,
113    }
114}