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}