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