Skip to main content

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}