Skip to main content

fancy_garbling/wire/
mod2.rs

1use crate::{BinaryWireLabel, WireLabel, util::tweak2, wire::hash_wires};
2use fancy_traits::HasModulus;
3use rand::{CryptoRng, RngExt};
4use subtle::ConditionallySelectable;
5use vectoreyes::{SimdBase, U8x16};
6
7impl HasModulus for WireMod2 {
8    fn modulus(&self) -> u16 {
9        2
10    }
11}
12
13/// Representation of a `mod-2` wire.
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[derive(Debug, Clone, Copy, PartialEq, Default)]
16pub struct WireMod2 {
17    /// A 128-bit value.
18    pub(crate) val: U8x16,
19}
20
21impl core::ops::Add for WireMod2 {
22    type Output = Self;
23
24    #[allow(clippy::suspicious_arithmetic_impl)]
25    fn add(self, rhs: Self) -> Self::Output {
26        Self {
27            val: self.val ^ rhs.val,
28        }
29    }
30}
31
32impl core::ops::AddAssign for WireMod2 {
33    #[allow(clippy::suspicious_op_assign_impl)]
34    fn add_assign(&mut self, rhs: Self) {
35        self.val ^= rhs.val;
36    }
37}
38
39impl core::ops::Sub for WireMod2 {
40    type Output = Self;
41
42    fn sub(self, rhs: Self) -> Self::Output {
43        self + -rhs
44    }
45}
46
47impl core::ops::SubAssign for WireMod2 {
48    fn sub_assign(&mut self, rhs: Self) {
49        *self = *self - rhs;
50    }
51}
52
53impl core::ops::Neg for WireMod2 {
54    type Output = Self;
55
56    fn neg(self) -> Self::Output {
57        // Do nothing. Additive inverse is a no-op for mod 2.
58        self
59    }
60}
61
62impl core::ops::Mul<u16> for WireMod2 {
63    type Output = Self;
64
65    fn mul(self, rhs: u16) -> Self::Output {
66        if rhs & 1 == 0 {
67            Self {
68                val: Default::default(),
69            }
70        } else {
71            self
72        }
73    }
74}
75
76impl core::ops::MulAssign<u16> for WireMod2 {
77    fn mul_assign(&mut self, rhs: u16) {
78        if rhs & 1 == 0 {
79            self.val = Default::default();
80        }
81    }
82}
83
84impl ConditionallySelectable for WireMod2 {
85    fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
86        WireMod2::from_repr(
87            U8x16::conditional_select(&a.to_repr(), &b.to_repr(), choice),
88            2,
89        )
90    }
91}
92
93impl WireLabel for WireMod2 {
94    fn rand_delta<R: CryptoRng>(rng: &mut R, q: u16) -> Self {
95        if q != 2 {
96            panic!("[WireMod2::rand_delta] Expected modulo 2. Got {}", q);
97        }
98        let mut w = Self::rand(rng, q);
99        w.val |= U8x16::set_lo(1);
100        w
101    }
102
103    fn to_repr(&self) -> U8x16 {
104        // This function converts a [`WireMod2`] into its [`U8x16`] representation.
105        // Since the value of a [`WireMod2`] is a 128b value, its directly returned
106        // as a [`U8x16`].
107        self.val
108    }
109
110    fn color(&self) -> u16 {
111        // This extracts the least-significant bit of the U8x16.
112        (self.val.extract::<0>() & 1) as u16
113    }
114
115    fn from_repr(inp: U8x16, q: u16) -> Self {
116        // This function converts a Block into its WireLabel representation
117        // by just setting the value of WireMod2 to the Block (i.e. the
118        // wire's 128b value).
119        if q != 2 {
120            panic!("[WireMod2::from_block] Expected modulo 2. Got {}", q);
121        }
122        Self { val: inp }
123    }
124
125    fn rand<R: CryptoRng>(rng: &mut R, q: u16) -> Self {
126        if q != 2 {
127            panic!("[WireMod2::rand] Expected modulo 2. Got {}", q);
128        }
129
130        Self { val: rng.random() }
131    }
132
133    fn hash_to_mod(hash: U8x16, q: u16) -> Self {
134        if q != 2 {
135            panic!("[WireMod2::hash_to_mod] Expected modulo 2. Got {}", q);
136        }
137        Self::from_repr(hash, q)
138    }
139}
140
141impl BinaryWireLabel for WireMod2 {
142    fn garble_and_gate(gate_num: usize, A: &Self, B: &Self, delta: &Self) -> (U8x16, U8x16, Self) {
143        let q = A.modulus();
144        let D = delta;
145
146        let r = B.color(); // secret value known only to the garbler (ev knows r+b)
147
148        let g = tweak2(gate_num as u64, 0);
149
150        // X = H(A+aD) + arD such that a + A.color == 0
151        let alpha = A.color(); // alpha = -A.color
152        let X1 = *A + *D * alpha;
153
154        // Y = H(B + bD) + (b + r)A such that b + B.color == 0
155        let beta = (q - B.color()) % q;
156        let Y1 = *B + *D * beta;
157
158        let AD = *A + *D;
159        let BD = *B + *D;
160
161        // idx is always boolean for binary gates, so it can be represented as a `u8`
162        let a_selector = (A.color() as u8).into();
163        let b_selector = (B.color() as u8).into();
164
165        let B = Self::conditional_select(&BD, B, b_selector);
166        let newA = Self::conditional_select(&AD, A, a_selector);
167        let idx = u8::conditional_select(&(r as u8), &0u8, a_selector);
168
169        let [hashA, hashB, hashX, hashY] = hash_wires([&newA, &B, &X1, &Y1], g);
170
171        let X = Self::hash_to_mod(hashX, q) + *D * (alpha * r % q);
172        let Y = Self::hash_to_mod(hashY, q);
173
174        let gate0 =
175            hashA ^ U8x16::conditional_select(&X.to_repr(), &(X + *D).to_repr(), idx.into());
176        let gate1 = hashB ^ (Y + *A).to_repr();
177
178        (gate0, gate1, X + Y)
179    }
180
181    fn evaluate_and_gate(
182        gate_num: usize,
183        A: &Self,
184        B: &Self,
185        gate0: &U8x16,
186        gate1: &U8x16,
187    ) -> Self {
188        let g = tweak2(gate_num as u64, 0);
189
190        let [hashA, hashB] = hash_wires([A, B], g);
191
192        // garbler's half gate
193        let L = Self::from_repr(
194            U8x16::conditional_select(&hashA, &(hashA ^ *gate0), (A.color() as u8).into()),
195            2,
196        );
197
198        // evaluator's half gate
199        let R = Self::from_repr(
200            U8x16::conditional_select(&hashB, &(hashB ^ *gate1), (B.color() as u8).into()),
201            2,
202        );
203
204        L + R + *A * B.color()
205    }
206}
207
208#[cfg(test)]
209mod tests {
210    #[cfg(feature = "serde")]
211    #[test]
212    fn test_serialize_mod2() {
213        use crate::{WireLabel, WireMod2};
214        use rand::rng;
215
216        let mut rng = rng();
217        let w = WireMod2::rand(&mut rng, 2);
218        let serialized = serde_json::to_string(&w).unwrap();
219
220        let deserialized: WireMod2 = serde_json::from_str(&serialized).unwrap();
221
222        assert_eq!(w, deserialized);
223    }
224}