Skip to main content

fancy_garbling/
classic.rs

1//! Provides objects and functions for statically garbling and evaluating a
2//! circuit without streaming.
3
4use crate::{
5    WireLabel,
6    garble::{Evaluator, Garbler},
7    util::output_tweak,
8};
9use fancy_traits::{Circuit, CircuitInputMapper, CircuitOutputMapper, FancyOutput};
10use rand::CryptoRng;
11use swanky_channel::Channel;
12use swanky_error::ErrorKind;
13use swanky_field_binary::F2;
14use vectoreyes::U8x16;
15
16/// A garbled circuit.
17///
18/// A garbled circuit at its core is just a vector of garbled rows and constant
19/// wirelabels.
20#[derive(Debug)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct GarbledCircuit {
23    blocks: Vec<U8x16>,
24}
25
26impl GarbledCircuit {
27    /// Create a [`GarbledCircuit`] from a vector of garbled rows and constant
28    /// wirelabels.
29    pub fn new(blocks: Vec<U8x16>) -> Self {
30        GarbledCircuit { blocks }
31    }
32
33    /// The number of garbled rows and constant wires in the garbled circuit.
34    pub fn size(&self) -> usize {
35        self.blocks.len()
36    }
37
38    /// Garble a circuit.
39    ///
40    /// This outputs three things:
41    /// 1. An [`Encoder`] for encoding inputs to valid input wirelabels.
42    /// 2. The garbled circuit itself.
43    /// 3. An [`OutputMapping`] mapping for mapping output wirelabels to their
44    ///    associated underlying values.
45    pub fn garble<
46        Wire: WireLabel<F2>,
47        C: CircuitInputMapper<Garbler<RNG, Wire>> + CircuitOutputMapper<Garbler<RNG, Wire>>,
48        RNG: CryptoRng,
49    >(
50        circuit: &C,
51        rng: RNG,
52    ) -> swanky_error::Result<(Encoder<Wire>, Self, OutputMapping)> {
53        let mut garbler = Garbler::new(rng);
54
55        // Produce zero wirelabels for the inputs.
56        let inputs = (0..circuit.ninputs())
57            .map(|_| garbler.encode_zero())
58            .collect::<Vec<_>>();
59
60        let mut channel = GarbledChannel::new_writer(None);
61        let (en, output_mapping) = Channel::with(&mut channel, |channel| {
62            // First, garble the circuit, outputting the zero wirelabels
63            // associated with the output.
64            let zeros = circuit.execute(&mut garbler, circuit.map(inputs.clone()), channel)?;
65            let zeros = C::flatten(zeros);
66            // Next, map the zero output wirelabels to the set of valid outputs.
67            // This is needed for evaluators that don't use the output
68            // mapping provided as output; in that case, we need the channel to
69            // contain that mapping, which is what the below does.
70            garbler.outputs(&zeros, channel)?;
71
72            let en = Encoder::new(inputs, garbler.delta());
73            let output_mapping = OutputMapping::new(&zeros, garbler.delta());
74
75            Ok((en, output_mapping))
76        })?;
77        let gc = GarbledCircuit::new(channel.finish_writing());
78        Ok((en, gc, output_mapping))
79    }
80
81    /// Evaluate the garbled circuit on the provided inputs, mapping the output
82    /// wirelabels to their associated values.
83    pub fn eval<Wire: WireLabel<F2>, C: CircuitOutputMapper<Evaluator<Wire>>>(
84        &self,
85        circuit: &C,
86        inputs: C::Input,
87        output_mapping: &OutputMapping,
88    ) -> swanky_error::Result<Vec<F2>> {
89        let wirelabels = self.eval_to_wirelabels(circuit, inputs)?;
90        output_mapping.to_outputs(&C::flatten(wirelabels))
91    }
92
93    /// Evaluate the garbled circuit on the provided inputs, returning the
94    /// output wirelabels.
95    pub fn eval_to_wirelabels<Wire: WireLabel<F2>, C: Circuit<Evaluator<Wire>>>(
96        &self,
97        circuit: &C,
98        inputs: C::Input,
99    ) -> swanky_error::Result<C::Output> {
100        let mut evaluator = Evaluator::new();
101        Channel::with(GarbledChannel::from(self), |channel| {
102            circuit.execute(&mut evaluator, inputs, channel)
103        })
104    }
105}
106
107/// Encoder for input wirelabels.
108#[derive(Debug)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110pub struct Encoder<Wire> {
111    inputs: Vec<Wire>,
112    delta: Wire,
113}
114
115impl<Wire: WireLabel<F2>> Encoder<Wire> {
116    /// Make a new [`Encoder`] from a vector of zero wirelabels and the `Δ`
117    /// wirelabel.
118    pub fn new(inputs: Vec<Wire>, delta: Wire) -> Self {
119        Encoder { inputs, delta }
120    }
121
122    /// Encode input values into their associated wirelabels.
123    ///
124    /// # Panics
125    /// This panics if `inputs.len()` does not equal the expected number of
126    /// garbler inputs.
127    pub fn encode_inputs(&self, inputs: &[F2]) -> Vec<Wire> {
128        assert_eq!(inputs.len(), self.inputs.len());
129        self.inputs
130            .iter()
131            .zip(inputs)
132            .map(|(zero, x)| *zero + self.delta * (*x).into())
133            .collect()
134    }
135}
136
137/// A mapping of output wirelabels to their associated underlying values.
138#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
139pub struct OutputMapping(Vec<[U8x16; 2]>);
140
141impl OutputMapping {
142    /// Construct a new [`OutputMapping`] from a slice of zero wirelabels and
143    /// the `Δ` wirelabel.
144    pub fn new<Wire: WireLabel<F2>>(zeros: &[Wire], delta: Wire) -> Self {
145        let outputs = zeros
146            .iter()
147            .enumerate()
148            .map(|(i, zero)| {
149                core::array::from_fn(|j| {
150                    (*zero + delta * (j as u16)).hash(output_tweak(i, j as u16))
151                })
152            })
153            .collect::<Vec<_>>();
154        Self(outputs)
155    }
156
157    /// Map output wirelabels to their underlying values.
158    ///
159    /// # Errors
160    /// This returns an error if it is unable to find a valid mapping for a
161    /// given output wirelabel.
162    pub fn to_outputs<Wire: WireLabel<F2>>(
163        &self,
164        wirelabels: &[Wire],
165    ) -> swanky_error::Result<Vec<F2>> {
166        wirelabels
167            .iter()
168            .enumerate()
169            .map(|(i, wirelabel)| {
170                let mut decoded = None;
171                for k in 0..2 {
172                    let hashed = wirelabel.hash(output_tweak(i, k));
173                    if hashed == self.0[i][k as usize] {
174                        decoded = Some(k);
175                        break;
176                    }
177                }
178                if let Some(output) = decoded {
179                    Ok(F2::from(output != 0))
180                } else {
181                    swanky_error::bail!(ErrorKind::OtherError, "Decoding failed");
182                }
183            })
184            .collect()
185    }
186}
187
188/// Type for writing and reading a garbled circuit from memory.
189///
190/// A [`GarbledChannel`] provides a way to use the [`Channel`] interface to
191/// write a garbled circuit to memory, alongside the ability to read it from
192/// memory for evaluation.
193///
194/// A [`GarbledChannel`] can be instantiated in one of two ways: either by
195/// calling [`GarbledChannel::new_writer`] to store the garbled circuit in
196/// memory, or [`GarbledChannel::from`] on an existing [`GarbledCircuit`] to
197/// evaluate the garbled circuit.
198///
199/// Note that a [`GarbledChannel`] cannot be both a writer and a reader. For
200/// example, calling [`GarbledChannel::finish_writing`] on a [`GarbledChannel`]
201/// reader results in a panic.
202pub struct GarbledChannel {
203    reader: Option<GarbledReader>,
204    writer: Option<GarbledWriter>,
205}
206
207impl GarbledChannel {
208    /// Construct a new [`GarbledChannel`] for writing a garbled circuit.
209    pub fn new_writer(ngates: Option<usize>) -> Self {
210        Self {
211            reader: None,
212            writer: Some(GarbledWriter::new(ngates)),
213        }
214    }
215
216    /// Consume the [`GarbledChannel`], outputting the resulting garbled circuit.
217    ///
218    /// # Panics
219    /// Panics if there is no valid writer for the [`GarbledChannel`].
220    pub fn finish_writing(self) -> Vec<U8x16> {
221        self.writer.unwrap().finish()
222    }
223}
224
225impl std::io::Read for GarbledChannel {
226    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
227        let reader = self.reader.as_mut().unwrap();
228        reader.read(buf)
229    }
230}
231
232impl std::io::Write for GarbledChannel {
233    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
234        let writer = self.writer.as_mut().unwrap();
235        writer.write(buf)
236    }
237
238    fn flush(&mut self) -> std::io::Result<()> {
239        let writer = self.writer.as_mut().unwrap();
240        writer.flush()
241    }
242}
243
244impl From<&GarbledCircuit> for GarbledChannel {
245    fn from(value: &GarbledCircuit) -> Self {
246        Self {
247            reader: Some(GarbledReader::new(&value.blocks)),
248            writer: None,
249        }
250    }
251}
252
253/// Implementation of the `Read` trait for use by the `Evaluator`.
254#[derive(Debug)]
255struct GarbledReader {
256    blocks: Vec<U8x16>,
257    index: usize,
258}
259
260impl GarbledReader {
261    fn new(blocks: &[U8x16]) -> Self {
262        Self {
263            blocks: blocks.to_vec(),
264            index: 0,
265        }
266    }
267}
268
269impl std::io::Read for GarbledReader {
270    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
271        assert_eq!(buf.len() % 16, 0);
272        let start = self.index;
273        for data in buf.chunks_mut(16) {
274            let block: [u8; 16] = self.blocks[self.index].into();
275            for (a, b) in data.iter_mut().zip(block.iter()) {
276                *a = *b;
277            }
278            self.index += 1;
279            if self.index == self.blocks.len() {
280                // We've read all that we can from the vector of `Block`s, so
281                // return the length of bytes that we've read to satisfy the
282                // `read` API.
283                return Ok(16 * (self.index - start));
284            }
285        }
286        Ok(buf.len())
287    }
288}
289
290/// Implementation of the `Write` trait for use by `Garbler`.
291#[derive(Debug)]
292struct GarbledWriter {
293    blocks: Vec<U8x16>,
294}
295
296impl GarbledWriter {
297    /// Make a new [`GarbledWriter`].
298    fn new(ngates: Option<usize>) -> Self {
299        let blocks = if let Some(n) = ngates {
300            Vec::with_capacity(2 * n)
301        } else {
302            Vec::new()
303        };
304        Self { blocks }
305    }
306
307    /// Consume the [`GarbledWriter`], outputting the resulting garbled circuit.
308    fn finish(self) -> Vec<U8x16> {
309        self.blocks
310    }
311}
312
313impl std::io::Write for GarbledWriter {
314    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
315        for item in buf.chunks(16) {
316            let bytes: [u8; 16] = match item.try_into() {
317                Ok(bytes) => bytes,
318                Err(_) => {
319                    return Err(std::io::Error::new(
320                        std::io::ErrorKind::InvalidData,
321                        "unable to map bytes to block",
322                    ));
323                }
324            };
325            self.blocks.push(bytes.into());
326        }
327        Ok(buf.len())
328    }
329    fn flush(&mut self) -> std::io::Result<()> {
330        Ok(())
331    }
332}