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}