Skip to main content

fancy_garbling/garble/
evaluator.rs

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