1use crate::circuit::{BinaryCircuit, BinaryGate, CircuitRef, CircuitType};
5use regex::{Captures, Regex};
6use std::str::FromStr;
7use swanky_error::{ErrorKind, Result, WrapErr, swanky_error};
8
9enum GateType {
10 AndGate,
11 XorGate,
12}
13
14fn cap2int(cap: &Captures, idx: usize) -> Result<usize> {
15 let s = cap
16 .get(idx)
17 .ok_or_else(|| swanky_error!(ErrorKind::OtherError, "Failed to match index '{idx}'"))?;
18 FromStr::from_str(s.as_str())
19 .wrap_err(ErrorKind::OtherError, "Failed to convert value to string")
20}
21
22fn cap2typ(cap: &Captures, idx: usize) -> Result<GateType> {
23 let s = cap
24 .get(idx)
25 .ok_or_else(|| swanky_error!(ErrorKind::OtherError, "Failed to match index '{idx}'"))?;
26 let s = s.as_str();
27 match s {
28 "AND" => Ok(GateType::AndGate),
29 "XOR" => Ok(GateType::XorGate),
30 s => swanky_error::bail!(ErrorKind::OtherError, "Unknown gate type '{s}'"),
31 }
32}
33
34fn regex2captures<'t>(re: &Regex, line: &'t str) -> Result<Captures<'t>> {
35 re.captures(line)
36 .ok_or_else(|| swanky_error!(ErrorKind::OtherError, "Failed to find match for regex"))
37}
38
39impl BinaryCircuit {
40 pub fn parse(mut reader: impl std::io::BufRead) -> Result<Self> {
45 let mut line = String::new();
47 reader
48 .read_line(&mut line)
49 .wrap_err(ErrorKind::OtherError, "Failed to read line")?;
50 let re = Regex::new(r"(\d+)\s+(\d+)").expect("regex should be valid");
51 let cap = regex2captures(&re, &line)?;
52 let ngates = cap2int(&cap, 1)?;
53 let nwires = cap2int(&cap, 2)?;
54
55 let mut line = String::new();
57 reader
58 .read_line(&mut line)
59 .wrap_err(ErrorKind::OtherError, "Failed to read line")?;
60 let re = Regex::new(r"(\d+)\s+(\d+)\s+(\d+)").expect("regex should be valid");
61 let cap = regex2captures(&re, &line)?;
62 let n1 = cap2int(&cap, 1)?; let n2 = cap2int(&cap, 2)?; let n3 = cap2int(&cap, 3)?; let mut line = String::new();
68 reader
69 .read_line(&mut line)
70 .wrap_err(ErrorKind::OtherError, "Failed to read line")?;
71 #[allow(clippy::trivial_regex)]
72 let re = Regex::new(r"\n").expect("regex should be valid");
73 let _ = regex2captures(&re, &line)?;
74
75 let mut circ = Self::new(Some(ngates));
76
77 let re1 = Regex::new(r"1 1 (\d+) (\d+) INV").expect("regex should be valid");
78 let re2 = Regex::new(r"2 1 (\d+) (\d+) (\d+) ((AND|XOR))").expect("regex should be valid");
79
80 let mut id = 0;
81
82 for i in 0..n1 {
84 circ.gates.push(BinaryGate::GarblerInput { id: i });
85 circ.garbler_input_refs
86 .push(CircuitRef { ix: i, modulus: 2 });
87 }
88 for i in 0..n2 {
90 circ.gates.push(BinaryGate::EvaluatorInput { id: i });
91 circ.evaluator_input_refs.push(CircuitRef {
92 ix: n1 + i,
93 modulus: 2,
94 });
95 }
96 circ.gates.push(BinaryGate::Constant { val: 1 });
100 let oneref = CircuitRef {
101 ix: n1 + n2,
102 modulus: 2,
103 };
104 circ.const_refs.push(oneref);
105 for i in 0..n3 {
107 circ.output_refs.push(CircuitRef {
108 ix: nwires - n3 + i,
109 modulus: 2,
110 });
111 }
112 for line in reader.lines() {
113 let line = line.wrap_err(ErrorKind::OtherError, "Failed to read line")?;
114 match line.chars().next() {
115 Some('1') => {
116 let cap = regex2captures(&re1, &line)?;
117 let yref = cap2int(&cap, 1)?;
118 let out = cap2int(&cap, 2)?;
119 let yref = CircuitRef {
120 ix: yref,
121 modulus: 2,
122 };
123 circ.gates.push(BinaryGate::Inv {
124 xref: yref,
125 out: Some(out),
126 })
127 }
128 Some('2') => {
129 let cap = regex2captures(&re2, &line)?;
130 let xref = cap2int(&cap, 1)?;
131 let yref = cap2int(&cap, 2)?;
132 let out = cap2int(&cap, 3)?;
133 let typ = cap2typ(&cap, 4)?;
134 let xref = CircuitRef {
135 ix: xref,
136 modulus: 2,
137 };
138 let yref = CircuitRef {
139 ix: yref,
140 modulus: 2,
141 };
142 let gate = match typ {
143 GateType::AndGate => {
144 let gate = BinaryGate::And {
145 xref,
146 yref,
147 id,
148 out: Some(out),
149 };
150 id += 1;
151 gate
152 }
153 GateType::XorGate => BinaryGate::Xor {
154 xref,
155 yref,
156 out: Some(out),
157 },
158 };
159 circ.gates.push(gate);
160 }
161 None => break,
162 _ => {
163 swanky_error::bail!(ErrorKind::OtherError, "Invalid wire value");
164 }
165 }
166 }
167 Ok(circ)
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use swanky_rng::SwankyRng;
174
175 use crate::{
176 WireMod2,
177 circuit::{BinaryCircuit as Circuit, eval_plain},
178 classic::GarbledCircuit,
179 };
180
181 #[test]
182 fn test_parser() {
183 let circ = Circuit::parse(std::io::Cursor::<&'static [u8]>::new(include_bytes!(
184 "../circuits/AES-non-expanded.txt"
185 )))
186 .unwrap();
187 let key = vec![0u16; 128];
188 let pt = vec![0u16; 128];
189 let output = eval_plain(&circ, &pt, &key).unwrap();
190 assert_eq!(
191 output.iter().map(|i| i.to_string()).collect::<String>(),
192 "01100110111010010100101111010100111011111000101000101100001110111000100001001100111110100101100111001010001101000010101100101110"
193 );
194 let key = vec![1u16; 128];
195 let pt = vec![0u16; 128];
196 let output = eval_plain(&circ, &pt, &key).unwrap();
197 assert_eq!(
198 output.iter().map(|i| i.to_string()).collect::<String>(),
199 "10100001111101100010010110001100100001110111110101011111110011011000100101100100010010000100010100111000101111111100100100101100"
200 );
201 let mut key = vec![0u16; 128];
202 for key_part in key.iter_mut().take(8) {
203 *key_part = 1;
204 }
205 let pt = vec![0u16; 128];
206 let output = eval_plain(&circ, &pt, &key).unwrap();
207 assert_eq!(
208 output.iter().map(|i| i.to_string()).collect::<String>(),
209 "10110001110101110101100000100101011010110010100011111101100001010000101011010100100101000100001000001000110011110001000101010101"
210 );
211 let mut key = vec![0u16; 128];
212 key[7] = 1;
213 let pt = vec![0u16; 128];
214 let output = eval_plain(&circ, &pt, &key).unwrap();
215 assert_eq!(
216 output.iter().map(|i| i.to_string()).collect::<String>(),
217 "11011100000011101101100001011101111110010110000100011010101110110111001001001001110011011101000101101000110001010100011001111110"
218 );
219 }
220
221 #[test]
222 fn test_gc_eval() {
223 let circ = Circuit::parse(std::io::Cursor::<&'static [u8]>::new(include_bytes!(
224 "../circuits/AES-non-expanded.txt"
225 )))
226 .unwrap();
227 let (en, gc, _) =
228 GarbledCircuit::garble::<WireMod2, _, _>(&circ, SwankyRng::new()).unwrap();
229 let gb = en.encode_garbler_inputs(&vec![0u16; 128]);
230 let ev = en.encode_evaluator_inputs(&vec![0u16; 128]);
231 gc.eval(&circ, &gb, &ev).unwrap();
232 }
233}