Skip to main content

fancy_garbling/garble/
evaluator.rs

1use super::security_warning::warn_proj;
2use crate::{
3    AllWire, ArithmeticWire, FancyArithmetic, FancyBinary, FancyProj, HasModulus, WireMod2,
4    check_binary,
5    fancy::Fancy,
6    garble::binary_and::BinaryWireLabel,
7    hash_wires,
8    util::{output_tweak, tweak, tweak2},
9    wire::WireLabel,
10};
11use swanky_channel::Channel;
12use swanky_error::ErrorKind;
13use vectoreyes::U8x16;
14
15/// Streaming evaluator using a callback to receive ciphertexts as needed.
16///
17/// Evaluates a garbled circuit on the fly, using messages containing ciphertexts and
18/// wires. Parallelizable.
19pub struct Evaluator<Wire> {
20    one: Wire,
21    current_gate: usize,
22    current_output: usize,
23}
24
25impl<Wire: WireLabel> Evaluator<Wire> {
26    /// Create a new [`Evaluator`].
27    pub fn new(channel: &mut Channel) -> swanky_error::Result<Self> {
28        // Receive the constant one wirelabel from the garbler. This is used to
29        // make negation free.
30        let one = channel.read::<U8x16>()?;
31        Ok(Evaluator {
32            one: Wire::from_repr(one, 2),
33            current_gate: 0,
34            current_output: 0,
35        })
36    }
37
38    /// The current non-free gate index of the garbling computation.
39    fn current_gate(&mut self) -> usize {
40        let current = self.current_gate;
41        self.current_gate += 1;
42        current
43    }
44
45    /// The current output index of the garbling computation.
46    fn current_output(&mut self) -> usize {
47        let current = self.current_output;
48        self.current_output += 1;
49        current
50    }
51}
52
53impl<W: BinaryWireLabel> FancyBinary for Evaluator<W> {
54    /// Negate is a noop for the evaluator
55    fn negate(&mut self, x: &Self::Item) -> Self::Item {
56        *x + self.one
57    }
58
59    fn xor(&mut self, x: &Self::Item, y: &Self::Item) -> Self::Item {
60        *x + *y
61    }
62
63    fn and(
64        &mut self,
65        A: &Self::Item,
66        B: &Self::Item,
67        channel: &mut Channel,
68    ) -> swanky_error::Result<Self::Item> {
69        let gate_num = self.current_gate();
70        let gate0 = channel.read()?;
71        let gate1 = channel.read()?;
72        Ok(W::evaluate_and_gate(gate_num, A, B, &gate0, &gate1))
73    }
74}
75
76impl FancyBinary for Evaluator<AllWire> {
77    /// Overriding `negate` to be a noop: entirely handled on garbler's end
78    fn negate(&mut self, x: &Self::Item) -> Self::Item {
79        check_binary!(x);
80
81        x.clone() + self.one.clone()
82    }
83
84    fn xor(&mut self, x: &Self::Item, y: &Self::Item) -> Self::Item {
85        check_binary!(x);
86        check_binary!(y);
87
88        self.add(x, y)
89    }
90
91    fn and(
92        &mut self,
93        x: &Self::Item,
94        y: &Self::Item,
95        channel: &mut Channel,
96    ) -> swanky_error::Result<Self::Item> {
97        if let (AllWire::Mod2(A), AllWire::Mod2(B)) = (x, y) {
98            let gate_num = self.current_gate();
99            let gate0 = channel.read()?;
100            let gate1 = channel.read()?;
101            return Ok(AllWire::Mod2(WireMod2::evaluate_and_gate(
102                gate_num, A, B, &gate0, &gate1,
103            )));
104        }
105
106        // If we got here, one of the wires isn't binary
107        check_binary!(x);
108        check_binary!(y);
109
110        // Shouldn't be reachable, unless the wire has modulus 2 but is not AllWire::Mod2()
111        unreachable!()
112    }
113}
114
115impl<Wire: WireLabel + ArithmeticWire> FancyArithmetic for Evaluator<Wire> {
116    fn add(&mut self, x: &Wire, y: &Wire) -> Wire {
117        assert_eq!(x.modulus(), y.modulus());
118        x.clone() + y.clone()
119    }
120
121    fn sub(&mut self, x: &Wire, y: &Wire) -> Wire {
122        assert_eq!(x.modulus(), y.modulus());
123        x.clone() - y.clone()
124    }
125
126    fn cmul(&mut self, x: &Wire, c: u16) -> Wire {
127        x.clone() * c
128    }
129
130    fn mul(&mut self, A: &Wire, B: &Wire, channel: &mut Channel) -> swanky_error::Result<Wire> {
131        if A.modulus() < B.modulus() {
132            return self.mul(B, A, channel);
133        }
134        let q = A.modulus();
135        let qb = B.modulus();
136        let unequal = q != qb;
137        let ngates = q as usize + qb as usize - 2 + unequal as usize;
138        let mut gate = Vec::with_capacity(ngates);
139        {
140            for _ in 0..ngates {
141                let block = channel.read::<U8x16>()?;
142                gate.push(block);
143            }
144        }
145        let gate_num = self.current_gate();
146        let g = tweak2(gate_num as u64, 0);
147
148        let [hashA, hashB] = hash_wires([A, B], g);
149
150        // garbler's half gate
151        let L = if A.color() == 0 {
152            Wire::hash_to_mod(hashA, q)
153        } else {
154            let ct_left = gate[A.color() as usize - 1];
155            Wire::from_repr(ct_left ^ hashA, q)
156        };
157
158        // evaluator's half gate
159        let R = if B.color() == 0 {
160            Wire::hash_to_mod(hashB, q)
161        } else {
162            let ct_right = gate[(q + B.color()) as usize - 2];
163            Wire::from_repr(ct_right ^ hashB, q)
164        };
165
166        // hack for unequal mods
167        // TODO: Batch this with original hash if unequal.
168        let new_b_color = if unequal {
169            let minitable = *gate.last().unwrap();
170            let ct = u128::from(minitable) >> (B.color() * 16);
171            let pt = u128::from(B.hash(tweak2(gate_num as u64, 1))) ^ ct;
172            pt as u16
173        } else {
174            B.color()
175        };
176
177        let res = L + R + A.clone() * new_b_color;
178        Ok(res)
179    }
180}
181
182impl<Wire: WireLabel + ArithmeticWire> FancyProj for Evaluator<Wire> {
183    fn proj(
184        &mut self,
185        x: &Wire,
186        q: u16,
187        _: Option<Vec<u16>>,
188        channel: &mut Channel,
189    ) -> swanky_error::Result<Wire> {
190        warn_proj();
191        let ngates = (x.modulus() - 1) as usize;
192        let mut gate = Vec::with_capacity(ngates);
193        for _ in 0..ngates {
194            let block = channel.read::<U8x16>()?;
195            gate.push(block);
196        }
197        let t = tweak(self.current_gate());
198        if x.color() == 0 {
199            Ok(x.hashback(t, q))
200        } else {
201            let ct = gate[x.color() as usize - 1];
202            Ok(Wire::from_repr(ct ^ x.hash(t), q))
203        }
204    }
205}
206
207impl<Wire: WireLabel> Fancy for Evaluator<Wire> {
208    type Item = Wire;
209
210    fn encode_many(
211        &mut self,
212        _values: &[u16],
213        _moduli: &[u16],
214        _: &mut Channel,
215    ) -> swanky_error::Result<Vec<Self::Item>> {
216        unimplemented!("Evaluator cannot encode values")
217    }
218
219    fn receive_many(
220        &mut self,
221        moduli: &[u16],
222        channel: &mut Channel,
223    ) -> swanky_error::Result<Vec<Self::Item>> {
224        moduli
225            .iter()
226            .map(|q| {
227                let block = channel.read()?;
228                Ok(Wire::from_repr(block, *q))
229            })
230            .collect()
231    }
232
233    fn constant(&mut self, _: u16, q: u16, channel: &mut Channel) -> swanky_error::Result<Wire> {
234        Ok(Wire::from_repr(channel.read()?, q))
235    }
236
237    fn output(&mut self, x: &Wire, channel: &mut Channel) -> swanky_error::Result<Option<u16>> {
238        let q = x.modulus();
239        let i = self.current_output();
240
241        // Receive the output ciphertext from the garbler
242        let mut ct = Vec::with_capacity(q as usize);
243        for _ in 0..q {
244            let block = channel.read()?;
245            ct.push(block);
246        }
247
248        // Attempt to brute force x using the output ciphertext
249        let mut decoded = None;
250        for k in 0..q {
251            let hashed_wire = x.hash(output_tweak(i, k));
252            if hashed_wire == ct[k as usize] {
253                decoded = Some(k);
254                break;
255            }
256        }
257
258        if let Some(output) = decoded {
259            Ok(Some(output))
260        } else {
261            swanky_error::bail!(ErrorKind::OtherError, "Decoding failed");
262        }
263    }
264}