Skip to main content

fancy_garbling/
fancy.rs

1//! Traits for representing specific kinds of garbled circuit computations.
2//!
3//! The core trait of this module is [`Fancy`], which represents the basic set
4//! of operations possible by a garbled circuit. There are also extension
5//! traits, in particular [`FancyBinary`] and [`FancyArithmetic`] that further
6//! extend the core [`Fancy`] trait to provide binary and arithmetic operations,
7//! respectively.
8
9use swanky_channel::Channel;
10
11mod binary;
12mod bundle;
13mod crt;
14pub use binary::{BinaryBundle, BinaryGadgets};
15pub use bundle::{
16    ArithmeticBundleGadgets, ArithmeticProjBundleGadgets, BinaryBundleGadgets, Bundle,
17    BundleGadgets,
18};
19pub use crt::{CrtBundle, CrtGadgets, CrtProjGadgets};
20
21/// An object that has a modulus.
22pub trait HasModulus {
23    /// The modulus of the wire.
24    fn modulus(&self) -> u16;
25}
26
27/// The `Fancy` trait provides the basic set of operations possible in a garbled
28/// circuit.
29///
30/// The trait contains an associated type, [`Fancy::Item`], which defines the
31/// underlying wirelabel representation. The trait then defines several methods
32/// for:
33/// 1. Encoding a value into a wirelabel ([`Fancy::encode`] and
34///    [`Fancy::encode_many`]).
35/// 2. Receiving a wirelabel for an unknown value ([`Fancy::receive`] and
36///    [`Fancy::receive_many`]).
37/// 3. Creating a wirelabel for a fixed (public) constant value
38///    ([`Fancy::constant`]).
39/// 4. Outputting a wirelabel as its underlying value ([`Fancy::output`] and
40///    [`Fancy::outputs`]).
41///
42/// This trait can be further extended to support binary, arithmetic, and/or
43/// projections by using the [`FancyBinary`], [`FancyArithmetic`], or
44/// [`FancyProj`] extension traits, respectively.
45pub trait Fancy {
46    /// The underlying wirelabel representation of this [`Fancy`] object.
47    type Item: Clone + HasModulus;
48
49    /// Encode many wirelabels for known values.
50    ///
51    /// When writing a garbler, the return value must correspond to the zero
52    /// wire label.
53    fn encode_many(
54        &mut self,
55        values: &[u16],
56        moduli: &[u16],
57        channel: &mut Channel,
58    ) -> swanky_error::Result<Vec<Self::Item>>;
59
60    /// Receive many wirelabels for unknown values.
61    fn receive_many(
62        &mut self,
63        moduli: &[u16],
64        channel: &mut Channel,
65    ) -> swanky_error::Result<Vec<Self::Item>>;
66
67    /// Encode a constant `x` with modulus `q`.
68    fn constant(
69        &mut self,
70        x: u16,
71        q: u16,
72        channel: &mut Channel,
73    ) -> swanky_error::Result<Self::Item>;
74
75    /// Output the value associated with wirelabel `x`.
76    ///
77    /// Some [`Fancy`] implementers don't actually *return* output, but they
78    /// need to be involved in the process, so they can return `None`.
79    fn output(
80        &mut self,
81        x: &Self::Item,
82        channel: &mut Channel,
83    ) -> swanky_error::Result<Option<u16>>;
84
85    /// Output the values associated with a slice of wirelabels.
86    ///
87    /// Some [`Fancy`] implementers don't actually *return* output, but they
88    /// need to be involved in the process, so they can return `None`.
89    fn outputs(
90        &mut self,
91        xs: &[Self::Item],
92        channel: &mut Channel,
93    ) -> swanky_error::Result<Option<Vec<u16>>> {
94        let mut zs = Vec::with_capacity(xs.len());
95        for x in xs.iter() {
96            zs.push(self.output(x, channel)?);
97        }
98        Ok(zs.into_iter().collect())
99    }
100
101    /// Encode a wirelabel for a known value.
102    ///
103    /// When writing a garbler, the return value must correspond to the zero
104    /// wire label.
105    fn encode(
106        &mut self,
107        value: u16,
108        modulus: u16,
109        channel: &mut Channel,
110    ) -> swanky_error::Result<Self::Item> {
111        let mut xs = self.encode_many(&[value], &[modulus], channel)?;
112        Ok(xs.remove(0))
113    }
114
115    /// Receive a wirelabel for an unknown value.
116    fn receive(&mut self, modulus: u16, channel: &mut Channel) -> swanky_error::Result<Self::Item> {
117        let mut xs = self.receive_many(&[modulus], channel)?;
118        Ok(xs.remove(0))
119    }
120}
121
122/// Extension trait for [`Fancy`] that provides binary operations.
123pub trait FancyBinary: Fancy {
124    /// Binary XOR.
125    fn xor(&mut self, x: &Self::Item, y: &Self::Item) -> Self::Item;
126
127    /// Binary AND.
128    fn and(
129        &mut self,
130        x: &Self::Item,
131        y: &Self::Item,
132        channel: &mut Channel,
133    ) -> swanky_error::Result<Self::Item>;
134
135    /// Binary negation.
136    fn negate(&mut self, x: &Self::Item) -> Self::Item;
137
138    /// Binary OR.
139    fn or(
140        &mut self,
141        x: &Self::Item,
142        y: &Self::Item,
143        channel: &mut Channel,
144    ) -> swanky_error::Result<Self::Item> {
145        let notx = self.negate(x);
146        let noty = self.negate(y);
147        let z = self.and(&notx, &noty, channel)?;
148        Ok(self.negate(&z))
149    }
150
151    /// Binary adder. Returns the result and the carry.
152    fn adder(
153        &mut self,
154        x: &Self::Item,
155        y: &Self::Item,
156        carry_in: Option<&Self::Item>,
157        channel: &mut Channel,
158    ) -> swanky_error::Result<(Self::Item, Self::Item)> {
159        if let Some(c) = carry_in {
160            let z1 = self.xor(x, y);
161            let z2 = self.xor(&z1, c);
162            let z3 = self.xor(x, c);
163            let z4 = self.and(&z1, &z3, channel)?;
164            let carry = self.xor(&z4, x);
165            Ok((z2, carry))
166        } else {
167            let z = self.xor(x, y);
168            let carry = self.and(x, y, channel)?;
169            Ok((z, carry))
170        }
171    }
172    /// Return 1 if all wirelabels equal 1.
173    ///
174    /// # Panics
175    /// Panics if `args` is empty.
176    fn and_many(
177        &mut self,
178        args: &[Self::Item],
179        channel: &mut Channel,
180    ) -> swanky_error::Result<Self::Item> {
181        assert!(!args.is_empty(), "`args` cannot be empty");
182        args.iter()
183            .skip(1)
184            .try_fold(args[0].clone(), |acc, x| self.and(&acc, x, channel))
185    }
186
187    /// Return 1 if any wirelabel equals 1.
188    ///
189    /// # Panics
190    /// Panics if `args` is empty.
191    fn or_many(
192        &mut self,
193        args: &[Self::Item],
194        channel: &mut Channel,
195    ) -> swanky_error::Result<Self::Item> {
196        assert!(!args.is_empty(), "`args` cannot be empty");
197        args.iter()
198            .skip(1)
199            .try_fold(args[0].clone(), |acc, x| self.or(&acc, x, channel))
200    }
201
202    /// XOR many wirelabels together.
203    ///
204    /// # Panics
205    /// Panics if `args.len() < 2`.
206    fn xor_many(&mut self, args: &[Self::Item]) -> Self::Item {
207        assert!(args.len() >= 2, "`args.len()` must be two or more");
208        args.iter()
209            .skip(1)
210            .fold(args[0].clone(), |acc, x| self.xor(&acc, x))
211    }
212
213    /// If `x = 0` return the constant `b1`, otherwise return `b2`.
214    fn mux_constant_bits(
215        &mut self,
216        x: &Self::Item,
217        b1: bool,
218        b2: bool,
219        channel: &mut Channel,
220    ) -> swanky_error::Result<Self::Item> {
221        match (b1, b2) {
222            (false, true) => Ok(x.clone()),
223            (true, false) => Ok(self.negate(x)),
224            (false, false) => self.constant(0, 2, channel),
225            (true, true) => self.constant(1, 2, channel),
226        }
227    }
228
229    /// If `b = 0` return `x`, otherwise return `y`.
230    fn mux(
231        &mut self,
232        b: &Self::Item,
233        x: &Self::Item,
234        y: &Self::Item,
235        channel: &mut Channel,
236    ) -> swanky_error::Result<Self::Item> {
237        let xor = self.xor(x, y);
238        let and = self.and(b, &xor, channel)?;
239        Ok(self.xor(&and, x))
240    }
241}
242
243/// Extension trait for [`Fancy`] that provides arithmetic operations.
244pub trait FancyArithmetic: Fancy {
245    /// Add `x` and `y`.
246    ///
247    /// # Panics
248    /// This panics if `x` and `y` do not have equal moduli.
249    fn add(&mut self, x: &Self::Item, y: &Self::Item) -> Self::Item;
250
251    /// Subtract `x` and `y`.
252    ///
253    /// # Panics
254    /// This panics if `x` and `y` do not have equal moduli.
255    fn sub(&mut self, x: &Self::Item, y: &Self::Item) -> Self::Item;
256
257    /// Multiply `x` with the constant `c`.
258    fn cmul(&mut self, x: &Self::Item, c: u16) -> Self::Item;
259
260    /// Multiply `x` and `y`.
261    fn mul(
262        &mut self,
263        x: &Self::Item,
264        y: &Self::Item,
265        channel: &mut Channel,
266    ) -> swanky_error::Result<Self::Item>;
267
268    /// Sum up a slice of wires.
269    ///
270    /// # Panics
271    /// Panics if `args.len() < 2`.
272    fn add_many(&mut self, args: &[Self::Item]) -> Self::Item {
273        assert!(args.len() >= 2, "`args.len()` must be two or more");
274        let mut z = args[0].clone();
275        for x in args.iter().skip(1) {
276            z = self.add(&z, x);
277        }
278        z
279    }
280}
281
282/// Extension trait for [`Fancy`] that provides a projection gate, alongside
283/// methods that utilize projection gates.
284///
285/// # Security Warning
286/// In its current form, using projection gates in arithmetic garbling is
287/// **insecure**.
288pub trait FancyProj: Fancy {
289    /// Project `x` according to the truth table `tt`. Resulting wire has modulus `q`.
290    ///
291    /// Optional `tt` is useful for hiding the gate from the evaluator.
292    ///
293    /// # Panics
294    /// This may panic in certain implementations if `tt` is `None` when it
295    /// should be `Some`. In addition, it may panic if `tt` is improperly
296    /// formed: either the length of `tt` is smaller than `x`s modulus, or the
297    /// values in `tt` are larger than `q`.
298    fn proj(
299        &mut self,
300        x: &Self::Item,
301        q: u16,
302        tt: Option<Vec<u16>>,
303        channel: &mut Channel,
304    ) -> swanky_error::Result<Self::Item>;
305
306    /// Change the modulus of `x` to `to_modulus` using a projection gate.
307    fn mod_change(
308        &mut self,
309        x: &Self::Item,
310        to_modulus: u16,
311        channel: &mut Channel,
312    ) -> swanky_error::Result<Self::Item> {
313        let from_modulus = x.modulus();
314        if from_modulus == to_modulus {
315            return Ok(x.clone());
316        }
317        let tab = (0..from_modulus)
318            .map(|x| x % to_modulus)
319            .collect::<Vec<_>>();
320        self.proj(x, to_modulus, Some(tab), channel)
321    }
322}
323
324macro_rules! check_binary {
325    ($x:ident) => {
326        assert_eq!($x.modulus(), 2);
327    };
328}
329
330pub(crate) use check_binary;