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 crate::{
165 WireMod2,
166 circuit::{BinaryCircuit as Circuit, eval_plain},
167 classic::garble,
168 };
169
170 #[test]
171 fn test_parser() {
172 let circ = Circuit::parse(std::io::Cursor::<&'static [u8]>::new(include_bytes!(
173 "../circuits/AES-non-expanded.txt"
174 )))
175 .unwrap();
176 let key = vec![0u16; 128];
177 let pt = vec![0u16; 128];
178 let output = eval_plain(&circ, &pt, &key).unwrap();
179 assert_eq!(
180 output.iter().map(|i| i.to_string()).collect::<String>(),
181 "01100110111010010100101111010100111011111000101000101100001110111000100001001100111110100101100111001010001101000010101100101110"
182 );
183 let key = vec![1u16; 128];
184 let pt = vec![0u16; 128];
185 let output = eval_plain(&circ, &pt, &key).unwrap();
186 assert_eq!(
187 output.iter().map(|i| i.to_string()).collect::<String>(),
188 "10100001111101100010010110001100100001110111110101011111110011011000100101100100010010000100010100111000101111111100100100101100"
189 );
190 let mut key = vec![0u16; 128];
191 for i in 0..8 {
192 key[i] = 1;
193 }
194 let pt = vec![0u16; 128];
195 let output = eval_plain(&circ, &pt, &key).unwrap();
196 assert_eq!(
197 output.iter().map(|i| i.to_string()).collect::<String>(),
198 "10110001110101110101100000100101011010110010100011111101100001010000101011010100100101000100001000001000110011110001000101010101"
199 );
200 let mut key = vec![0u16; 128];
201 key[7] = 1;
202 let pt = vec![0u16; 128];
203 let output = eval_plain(&circ, &pt, &key).unwrap();
204 assert_eq!(
205 output.iter().map(|i| i.to_string()).collect::<String>(),
206 "11011100000011101101100001011101111110010110000100011010101110110111001001001001110011011101000101101000110001010100011001111110"
207 );
208 }
209
210 #[test]
211 fn test_gc_eval() {
212 let circ = Circuit::parse(std::io::Cursor::<&'static [u8]>::new(include_bytes!(
213 "../circuits/AES-non-expanded.txt"
214 )))
215 .unwrap();
216 let (en, gc) = garble::<WireMod2, _>(&circ).unwrap();
217 let gb = en.encode_garbler_inputs(&vec![0u16; 128]);
218 let ev = en.encode_evaluator_inputs(&vec![0u16; 128]);
219 gc.eval(&circ, &gb, &ev).unwrap();
220 }
221}