Skip to main content

spongefish/
codecs.rs

1//! Maps for encoding prover messages and decoding verifier messages.
2
3/// Marker trait for types that have encoding and decoding maps.
4///
5/// A type is a [`Codec`] if it implements [`Encoding`], [`Decoding`],
6/// [`NargSerialize`][crate::io::NargSerialize], and [`NargDeserialize`][crate::io::NargDeserialize]
7///
8/// # Derive Macros
9///
10/// With the `derive` feature enabled:
11///
12/// ```
13/// # #[cfg(feature = "derive")]
14/// # {
15/// use spongefish::Codec;
16///
17/// #[derive(Codec)]
18/// struct MyStruct {
19///     field1: u32,
20///     field2: u32,
21///     #[spongefish(skip)]  // Skip this field (uses Default)
22///     cached: Option<String>,
23/// }
24/// # }
25/// ```
26///
27/// Equivalent to deriving `Encoding`, `Decoding`, and `NargDeserialize`. Fields marked with
28/// `#[spongefish(skip)]` are initialized via `Default`.
29pub trait Codec<T = [u8]>:
30    crate::NargDeserialize + crate::NargSerialize + Encoding<T> + Decoding<T>
31where
32    T: ?Sized,
33{
34}
35
36/// Interface for turning a type into a duplex sponge input.
37///
38/// [`Encoding<T>`] defines an encoding into a type `T`.
39/// By default `T = [u8]` in order to serve encoding for byte-oriented hash functions.
40///
41/// # Safety
42///
43/// [`spongefish`][`crate`] assumes that prover and verifier will know the length of all the prover messages.
44/// [`Encoding`] must be **prefix-free**: the output of [`Encoding::encode`] is never a prefix of any other
45/// instance of the same type.
46///
47/// More information on the theoretical requirements is in [[CO25], Theorem 6.2].
48///
49/// # Blanket implementations
50///
51/// # Encoding conventions
52///
53/// For byte sequences, encoding must be the identity function.
54/// Strings are encoded as their little-endian `u32` byte length followed by their UTF-8 bytes.
55/// Integers are encoded via []
56///
57/// [CO25]: https://eprint.iacr.org/2025/536.pdf
58pub trait Encoding<T = [u8]>
59where
60    T: ?Sized,
61{
62    /// The function encoding prover messages into inputs to be absorbed by the duplex sponge.
63    ///
64    /// This map must be injective. The computation of the pre-image of this map will affect the extraction time.
65    fn encode(&self) -> impl AsRef<T>;
66}
67
68/// The interface for all types that can be turned into verifier messages.
69pub trait Decoding<T = [u8]>
70where
71    T: ?Sized,
72{
73    /// The output type (and length) expected by the duplex sponge.
74    ///
75    /// # Example
76    ///
77    /// ```
78    /// # use spongefish::{Decoding, ByteArray};
79    /// let repr: ByteArray<4> = Default::default();
80    /// assert_eq!(repr.as_ref(), &[0u8; 4]);
81    /// ```
82    type Repr: Default + AsMut<T>;
83
84    ///  The distribution-preserving map, that re-maps a squeezed output [`Decoding::Repr`] into a verifier message.
85    ///
86    /// This map is not exactly a decoding function (e.g., it can be onto). What is demanded from this function is that
87    /// it preserves the uniform distribution: if [`Decoding::Repr`] is distributed uniformly at random, the also the output of [`decode`][Decoding::decode] is so.
88    fn decode(buf: Self::Repr) -> Self;
89}
90
91impl<U, T> Encoding<U> for &T
92where
93    U: ?Sized,
94    T: Encoding<U> + ?Sized,
95{
96    fn encode(&self) -> impl AsRef<U> {
97        (*self).encode()
98    }
99}
100
101impl<U: Clone, T: Encoding<[U]>, const N: usize> Encoding<[U]> for [T; N] {
102    fn encode(&self) -> impl AsRef<[U]> {
103        let mut output = alloc::vec::Vec::new();
104        for element in self {
105            output.extend_from_slice(element.encode().as_ref());
106        }
107        output
108    }
109}
110
111macro_rules! impl_int_encoding {
112    ($type: ty) => {
113        impl Encoding<[u8]> for $type {
114            fn encode(&self) -> impl AsRef<[u8]> {
115                self.to_le_bytes()
116            }
117        }
118    };
119}
120
121macro_rules! impl_int_decoding {
122    ($type: ty) => {
123        impl Decoding<[u8]> for $type {
124            type Repr = ByteArray<{ core::mem::size_of::<$type>() }>;
125
126            fn decode(buf: Self::Repr) -> Self {
127                <$type>::from_le_bytes(Decoding::decode(buf))
128            }
129        }
130    };
131}
132
133impl_int_encoding!(u8);
134impl_int_decoding!(u8);
135impl_int_encoding!(u16);
136impl_int_decoding!(u16);
137impl_int_encoding!(u32);
138impl_int_decoding!(u32);
139impl_int_encoding!(u64);
140impl_int_decoding!(u64);
141impl_int_encoding!(u128);
142impl_int_decoding!(u128);
143
144#[derive(Debug, Clone)]
145pub struct ByteArray<const N: usize>([u8; N]);
146
147impl<const N: usize> Default for ByteArray<N> {
148    fn default() -> Self {
149        Self([0; N])
150    }
151}
152impl<const N: usize> AsRef<[u8; N]> for ByteArray<N> {
153    fn as_ref(&self) -> &[u8; N] {
154        &self.0
155    }
156}
157
158impl<const N: usize> AsMut<[u8]> for ByteArray<N> {
159    fn as_mut(&mut self) -> &mut [u8] {
160        self.0.as_mut()
161    }
162}
163
164impl<const N: usize> Decoding<[u8]> for [u8; N] {
165    type Repr = ByteArray<N>;
166
167    fn decode(buf: Self::Repr) -> Self {
168        buf.0
169    }
170}
171
172/// Handy for serializing byte strings.
173///
174/// # Safety
175///
176/// This implementation is the identity map on `[u8]`.
177/// > **Warning:**
178/// > It is the responsibility of the caller to ensure that the byte string length is fixed by
179/// > the surrounding protocol and that any value encoded this way is prefix-free. Otherwise,
180/// > distinct prover messages may become ambiguous in the transcript.
181impl Encoding<[u8]> for [u8] {
182    fn encode(&self) -> impl AsRef<[u8]> {
183        self
184    }
185}
186
187/// Handy for serializing UTF-8 strings.
188///
189/// Strings are encoded as their little-endian `u32` byte length followed by their UTF-8 bytes.
190/// This makes the byte-oriented encoding prefix-free.
191impl Encoding<[u8]> for str {
192    fn encode(&self) -> impl AsRef<[u8]> {
193        let len: u32 = self
194            .len()
195            .try_into()
196            .expect("string encoding requires length to fit in u32");
197        let mut out = alloc::vec::Vec::new();
198        out.extend_from_slice(&len.to_le_bytes());
199        out.extend_from_slice(self.as_bytes());
200        out
201    }
202}
203
204impl<U: Clone, T: Encoding<[U]>> Encoding<[U]> for alloc::vec::Vec<T> {
205    fn encode(&self) -> impl AsRef<[U]> {
206        let mut out = alloc::vec::Vec::new();
207        for x in self {
208            out.extend_from_slice(x.encode().as_ref());
209        }
210        out
211    }
212}
213
214impl<A, B> Encoding<[u8]> for (A, B)
215where
216    A: Encoding<[u8]>,
217    B: Encoding<[u8]>,
218{
219    fn encode(&self) -> impl AsRef<[u8]> {
220        let mut output = alloc::vec::Vec::new();
221        output.extend_from_slice(self.0.encode().as_ref());
222        output.extend_from_slice(self.1.encode().as_ref());
223        output
224    }
225}
226
227impl<A, B, C> Encoding<[u8]> for (A, B, C)
228where
229    A: Encoding<[u8]>,
230    B: Encoding<[u8]>,
231    C: Encoding<[u8]>,
232{
233    fn encode(&self) -> impl AsRef<[u8]> {
234        let mut output = alloc::vec::Vec::new();
235        output.extend_from_slice(self.0.encode().as_ref());
236        output.extend_from_slice(self.1.encode().as_ref());
237        output.extend_from_slice(self.2.encode().as_ref());
238        output
239    }
240}
241
242/// Blanket implementation of [`Codec`] for all traits implementing
243/// [`NargSerialize`][`crate::NargSerialize`],
244/// [`NargDeserialize`][`crate::NargSerialize`],
245/// [`Encoding`], and [`Decoding`]
246impl<T, E> Codec<T> for E
247where
248    T: ?Sized,
249    E: crate::NargDeserialize + crate::NargSerialize + Encoding<T> + Decoding<T>,
250{
251}