spongefish/narg_prover.rs
1use alloc::vec::Vec;
2use core::fmt;
3
4use rand::{CryptoRng, Rng, RngCore, SeedableRng};
5
6use crate::{Decoding, DuplexSpongeInterface, Encoding, NargSerialize, StdHash};
7
8type StdRng = rand::rngs::StdRng;
9
10/// [`ProverState`] is the prover state in the non-interactive transformation.
11///
12/// It provides the **secret coins** of the prover for zero-knowledge, and
13/// the hash function state for the verifier's **public coins**.
14///
15/// The internal random number generator is instantiated with [`sha3::Shake128`],
16/// seeded via [`rand::rngs::StdRng`].
17///
18/// # Safety
19///
20/// Leaking [`ProverState`] is equivalent to leaking the prover's private coins, and therefore zero-knowledge.
21/// [`ProverState`] does not implement [`Clone`] or [`Copy`] to prevent accidental state-restoration attacks.
22pub struct ProverState<H = StdHash, R = StdRng>
23where
24 H: DuplexSpongeInterface,
25 R: RngCore + CryptoRng,
26{
27 /// The randomness state of the prover.
28 pub(crate) private_rng: ReseedableRng<R>,
29 /// The public coins for the protocol.
30 ///
31 /// # Safety
32 ///
33 /// Copying this object will break the soundness guarantees installed at the [`ProverState`] level.
34 #[cfg(feature = "yolocrypto")]
35 pub duplex_sponge_state: H,
36 #[cfg(not(feature = "yolocrypto"))]
37 pub(crate) duplex_sponge_state: H,
38 /// The argument string as it gets written throughout the execution of the prover.
39 pub(crate) narg_string: Vec<u8>,
40}
41
42/// A cryptographically-secure random number generator that is bound to the proof string.
43///
44/// For most public-coin protocols it is *vital* not to have two different verifier messages for the same prover message.
45/// For this reason, we construct an RNG that absorbs whatever the verifier absorbs, and that in addition
46/// is seeded by a cryptographic random number generator (by default, [`rand::rngs::OsRng`]).
47///
48/// Every time a challenge is being generated, the private prover sponge is ratcheted, so that it can't be inverted and the randomness recovered.
49#[derive(Default)]
50pub struct ReseedableRng<R: RngCore + CryptoRng> {
51 /// The duplex sponge that is used to generate the prover's private random coins.
52 pub(crate) duplex_sponge: StdHash,
53 /// The cryptographic random number generator that seeds the sponge.
54 pub(crate) csrng: R,
55}
56
57impl<R: RngCore + CryptoRng> From<R> for ReseedableRng<R> {
58 fn from(mut csrng: R) -> Self {
59 let mut duplex_sponge = StdHash::default();
60 let seed: [u8; 32] = csrng.gen::<[u8; 32]>();
61 duplex_sponge.absorb(&seed);
62 Self {
63 duplex_sponge,
64 csrng,
65 }
66 }
67}
68
69impl ReseedableRng<StdRng> {
70 /// Creates a reseedable RNG backed by `StdRng`.
71 pub fn new() -> Self {
72 let csrng = StdRng::from_entropy();
73 csrng.into()
74 }
75}
76
77impl<R: RngCore + CryptoRng> RngCore for ReseedableRng<R> {
78 fn next_u32(&mut self) -> u32 {
79 let mut buf = [0u8; 4];
80 self.fill_bytes(buf.as_mut());
81 u32::from_le_bytes(buf)
82 }
83
84 fn next_u64(&mut self) -> u64 {
85 let mut buf = [0u8; 8];
86 self.fill_bytes(buf.as_mut());
87 u64::from_le_bytes(buf)
88 }
89
90 fn fill_bytes(&mut self, dest: &mut [u8]) {
91 // fill `dest` with the output of the sponge
92 self.duplex_sponge.squeeze(dest);
93 // xxx. for extra safety we can imagine ratcheting here so that
94 // the state of the sponge can't be reverted after
95 // erase the state from the sponge so that it can't be reverted
96 // self.duplex_sponge.ratchet();
97 }
98
99 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
100 self.duplex_sponge.squeeze(dest);
101 Ok(())
102 }
103}
104
105impl<R: RngCore + CryptoRng> ReseedableRng<R> {
106 /// Reseeds the internal sponge with the provided bytes.
107 pub fn reseed_with(&mut self, value: &[u8]) {
108 self.duplex_sponge.ratchet();
109 self.duplex_sponge.absorb(value);
110 self.duplex_sponge.ratchet();
111 }
112
113 /// Reseeds the internal sponge with fresh entropy from the CSRNG.
114 pub fn reseed(&mut self) {
115 let seed = self.csrng.gen::<[u8; 32]>();
116 self.reseed_with(&seed);
117 }
118}
119
120impl<R: RngCore + CryptoRng> CryptoRng for ReseedableRng<R> {}
121
122impl<H, R> fmt::Debug for ProverState<H, R>
123where
124 H: DuplexSpongeInterface,
125 R: RngCore + CryptoRng,
126{
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 write!(f, "ProverState<{}>", core::any::type_name::<H>())
129 }
130}
131
132impl<H, R> ProverState<H, R>
133where
134 H: DuplexSpongeInterface,
135 R: RngCore + CryptoRng,
136{
137 /// Returns the reseedable RNG bound to this transcript.
138 pub const fn rng(&mut self) -> &mut ReseedableRng<R> {
139 &mut self.private_rng
140 }
141
142 /// Returns the current serialized NARG string.
143 #[inline]
144 pub const fn narg_string(&self) -> &[u8] {
145 self.narg_string.as_slice()
146 }
147
148 /// Input a public message to the Fiat--Shamir transformation.
149 ///
150 /// A public message in this context is a message that is shared among prover and verifier
151 /// outside of the NARG, and is to be included in the Fiat--Shamir transformation but not in
152 /// the final NARG string.
153 ///
154 /// ```
155 /// use spongefish::ProverState;
156 ///
157 /// let mut prover_state = spongefish::domain_separator!("examples"; "ProverState::public_message")
158 /// .instance(&0u32)
159 /// .std_prover();
160 /// prover_state.public_message(&123u32);
161 /// assert_eq!(prover_state.narg_string(), b"");
162 /// ```
163 pub fn public_message<T: Encoding<[H::U]> + ?Sized>(&mut self, message: &T) {
164 self.duplex_sponge_state.absorb(message.encode().as_ref());
165 }
166
167 /// Input a prover message of type `T` into the Fiat--Shamir transformation.
168 ///
169 /// `T` must implement [`Encoding<[H::U]>`][`Encoding`] to be encoded in the domain of the
170 /// duplex sponge, and [`NargSerialize`] to be serialized into the NARG string.
171 ///
172 /// ```
173 /// use spongefish::ProverState;
174 ///
175 /// let mut prover_state = spongefish::domain_separator!("examples"; "ProverState::prover_message")
176 /// .instance(&0u32)
177 /// .std_prover();
178 /// prover_state.prover_message(&42u32);
179 /// let expected = 42u32.to_le_bytes();
180 /// assert_eq!(prover_state.narg_string(), expected.as_slice());
181 /// ```
182 pub fn prover_message<T: Encoding<[H::U]> + NargSerialize + ?Sized>(&mut self, message: &T) {
183 self.duplex_sponge_state.absorb(message.encode().as_ref());
184 message.serialize_into_narg(&mut self.narg_string);
185 }
186
187 /// Returns a verifier message `T` that is uniformly distributed.
188 ///
189 /// `T` must implement [`Decoding<[H::U]>`][`Decoding`].
190 pub fn verifier_message<T: Decoding<[H::U]>>(&mut self) -> T {
191 let mut buf = T::Repr::default();
192 self.duplex_sponge_state.squeeze(buf.as_mut());
193 T::decode(buf)
194 }
195
196 /// Alias for [`narg_string`][ProverState::narg_string].
197 #[deprecated(note = "Please use ProverState::narg_string instead.")]
198 #[inline]
199 pub const fn transcript(&self) -> &[u8] {
200 self.narg_string()
201 }
202
203 /// Alias for [`verifier_message`][`ProverState::verifier_message`].
204 #[deprecated(note = "Please use ProverState::verifier_message instead.")]
205 pub fn challenge<T: Decoding<[H::U]>>(&mut self) -> T {
206 self.verifier_message()
207 }
208
209 /// Input to the Fiat--Shamir transformation an array of public messages.
210 pub fn public_messages<T: Encoding<[H::U]>>(&mut self, messages: &[T]) {
211 for message in messages {
212 self.public_message(message);
213 }
214 }
215
216 /// Input to the Fiat--Shamir transformation an iterator of public messages.
217 pub fn public_messages_iter<J>(&mut self, messages: J)
218 where
219 J: IntoIterator,
220 J::Item: Encoding<[H::U]>,
221 {
222 messages
223 .into_iter()
224 .for_each(|message| self.public_message(&message));
225 }
226
227 /// Absorbs a list of prover messages at once.
228 pub fn prover_messages<T: Encoding<[H::U]> + NargSerialize>(&mut self, messages: &[T]) {
229 for message in messages {
230 self.prover_message(message);
231 }
232 }
233
234 /// Absorbs an iterator of prover messages.
235 pub fn prover_messages_iter<J>(&mut self, messages: J)
236 where
237 J: IntoIterator,
238 J::Item: Encoding<[H::U]> + NargSerialize,
239 {
240 messages
241 .into_iter()
242 .for_each(|message| self.prover_message(&message));
243 }
244
245 /// Returns a fixed-length array of uniformly-distributed verifier messages `[T; N]`.
246 pub fn verifier_messages<T: Decoding<[H::U]>, const N: usize>(&mut self) -> [T; N] {
247 core::array::from_fn(|_| self.verifier_message())
248 }
249
250 /// Returns a vector of uniformly-distributed verifier messages `[T; N]`.
251 pub fn verifier_messages_vec<T: Decoding<[H::U]>>(&mut self, len: usize) -> Vec<T> {
252 (0..len).map(|_| self.verifier_message()).collect()
253 }
254}
255
256/// Creates a new [`ProverState`] seeded using [`rand::SeedableRng::from_entropy`].
257///
258/// [`Default`] provides alternative initialization methods than the one via
259/// [`DomainSeparator`][`crate::DomainSeparator`].
260/// [`ProverState::default`] is only available with the `yolocrypto` feature and its support in
261/// future releases is not guaranteed.
262#[cfg(feature = "yolocrypto")]
263impl<H: DuplexSpongeInterface + Default, R: RngCore + CryptoRng + SeedableRng> Default
264 for ProverState<H, R>
265{
266 fn default() -> Self {
267 Self {
268 duplex_sponge_state: H::default(),
269 private_rng: R::from_entropy().into(),
270 narg_string: Vec::new(),
271 }
272 }
273}
274
275/// Creates a new [`ProverState`] using the given duplex sponge interface.
276impl<H: DuplexSpongeInterface, R: RngCore + CryptoRng + SeedableRng> From<H> for ProverState<H, R> {
277 fn from(value: H) -> Self {
278 Self {
279 duplex_sponge_state: value,
280 private_rng: R::from_entropy().into(),
281 narg_string: Vec::new(),
282 }
283 }
284}