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