Skip to main content

fancy_garbling/garble/
garbler.rs

1use crate::{BinaryWireLabel, WireLabel, util::output_tweak};
2use fancy_traits::{Fancy, FancyBinary, FancyConstant, FancyEncode, FancyOutput};
3use rand::CryptoRng;
4use swanky_channel::Channel;
5use swanky_field_binary::F2;
6use vectoreyes::U8x16;
7
8/// Streams garbled circuit ciphertexts through a callback.
9pub struct Garbler<RNG, Wire> {
10    // Zero wirelabel used for binary negation.
11    zero: Wire,
12    delta: Wire,
13    current_output: usize,
14    current_gate: usize,
15    rng: RNG,
16}
17
18impl<RNG: CryptoRng, Wire: WireLabel<F2>> Garbler<RNG, Wire> {
19    /// Create a new [`Garbler`].
20    pub fn new(mut rng: RNG) -> Self {
21        let delta = Wire::rand_delta(&mut rng);
22        // We fix the constant `1` value to `1`, and derive the zero wirelabel
23        // as that value XORed with `Δ`.
24        let one = Wire::from_repr(U8x16::from(1u128));
25        let zero = delta + one;
26        Garbler {
27            zero,
28            delta,
29            current_gate: 0,
30            current_output: 0,
31            rng,
32        }
33    }
34
35    /// The current non-free gate index of the garbling computation
36    fn current_gate(&mut self) -> usize {
37        let current = self.current_gate;
38        self.current_gate += 1;
39        current
40    }
41
42    /// The `Δ` wirelabel.
43    pub fn delta(&mut self) -> Wire {
44        self.delta
45    }
46
47    /// The current output index of the garbling computation.
48    fn current_output(&mut self) -> usize {
49        let current = self.current_output;
50        self.current_output += 1;
51        current
52    }
53
54    /// Output a fresh zero wirelabel associated with the provided modulus.
55    pub fn encode_zero(&mut self) -> Wire {
56        Wire::rand(&mut self.rng)
57    }
58}
59
60impl<RNG: CryptoRng, W: BinaryWireLabel> FancyBinary for Garbler<RNG, W> {
61    fn and(
62        &mut self,
63        A: &Self::Item,
64        B: &Self::Item,
65        channel: &mut Channel,
66    ) -> swanky_error::Result<Self::Item> {
67        let gate_num = self.current_gate();
68        let (gate0, gate1, C) = W::garble_and_gate(gate_num, A, B, &self.delta);
69        channel.write(&gate0)?;
70        channel.write(&gate1)?;
71        Ok(C)
72    }
73
74    fn xor(&mut self, x: &Self::Item, y: &Self::Item) -> Self::Item {
75        *x + *y
76    }
77
78    /// We can negate by having garbler xor wire with Delta
79    ///
80    /// Since we treat all garbler wires as zero,
81    /// xoring with delta conceptually negates the value of the wire
82    fn negate(&mut self, x: &Self::Item) -> Self::Item {
83        self.zero + *x
84    }
85}
86
87impl<RNG: CryptoRng, Wire: WireLabel<F2>> Fancy for Garbler<RNG, Wire> {
88    type Item = Wire;
89}
90
91impl<RNG: CryptoRng, Wire: WireLabel<F2>> FancyConstant<F2> for Garbler<RNG, Wire> {
92    fn constant(&mut self, x: F2) -> Self::Item {
93        if x.into() {
94            // `self.zero` corresponds to the zero wirelabel associated with the
95            // "one" wirelabel set to `F128b::ONE`.
96            self.zero
97        } else {
98            // Otherwise, the garbler uses the "null" wirelabel to represent zero.
99            Default::default()
100        }
101    }
102}
103
104impl<RNG: CryptoRng, Wire: WireLabel<F2>> FancyEncode<F2> for Garbler<RNG, Wire> {
105    fn encode_many(
106        &mut self,
107        values: &[F2],
108        channel: &mut Channel,
109    ) -> swanky_error::Result<Vec<Self::Item>> {
110        let mut zeros = Vec::with_capacity(values.len());
111        for x in values.iter() {
112            let zero = self.encode_zero();
113            let encoded = zero + self.delta * (*x).into();
114            channel.write(&encoded.to_repr())?;
115            zeros.push(zero);
116        }
117        Ok(zeros)
118    }
119
120    fn receive_many(&mut self, _: usize, _: &mut Channel) -> swanky_error::Result<Vec<Self::Item>> {
121        unimplemented!("Garbler cannot receive values")
122    }
123}
124
125impl<RNG: CryptoRng, Wire: WireLabel<F2>> FancyOutput<F2> for Garbler<RNG, Wire> {
126    fn output(&mut self, X: &Wire, channel: &mut Channel) -> swanky_error::Result<Option<F2>> {
127        let i = self.current_output();
128        for k in 0..2 {
129            let block = (*X + self.delta() * k).hash(output_tweak(i, k));
130            channel.write(&block)?;
131        }
132        Ok(None)
133    }
134}