1use crate::circuit::{BinaryCircuit, BinaryGate};
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 + n2 {
84 circ.gates.push(BinaryGate::Input { id: i });
85 circ.input_refs.push(i);
86 }
87 circ.gates.push(BinaryGate::Constant { val: 1 });
91 circ.const_refs.push(n1 + n2);
92 for i in 0..n3 {
94 circ.output_refs.push(nwires - n3 + i);
95 }
96 for line in reader.lines() {
97 let line = line.wrap_err(ErrorKind::OtherError, "Failed to read line")?;
98 match line.chars().next() {
99 Some('1') => {
100 let cap = regex2captures(&re1, &line)?;
101 let yref = cap2int(&cap, 1)?;
102 let out = cap2int(&cap, 2)?;
103 circ.gates.push(BinaryGate::Inv {
104 xref: yref,
105 out: Some(out),
106 })
107 }
108 Some('2') => {
109 let cap = regex2captures(&re2, &line)?;
110 let xref = cap2int(&cap, 1)?;
111 let yref = cap2int(&cap, 2)?;
112 let out = cap2int(&cap, 3)?;
113 let typ = cap2typ(&cap, 4)?;
114 let gate = match typ {
115 GateType::AndGate => {
116 let gate = BinaryGate::And {
117 xref,
118 yref,
119 id,
120 out: Some(out),
121 };
122 id += 1;
123 gate
124 }
125 GateType::XorGate => BinaryGate::Xor {
126 xref,
127 yref,
128 out: Some(out),
129 },
130 };
131 circ.gates.push(gate);
132 }
133 None => break,
134 _ => {
135 swanky_error::bail!(ErrorKind::OtherError, "Invalid wire value");
136 }
137 }
138 }
139 Ok(circ)
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use crate::{
146 WireMod2, circuit::BinaryCircuit as Circuit, classic::GarbledCircuit, dummy::Dummy,
147 };
148 use swanky_rng::SwankyRng;
149
150 #[test]
151 fn test_parser() {
152 let circ = Circuit::parse(std::io::Cursor::<&'static [u8]>::new(include_bytes!(
153 "../circuits/AES-non-expanded.txt"
154 )))
155 .unwrap();
156 let input = [0u16; 256];
157 let output = Dummy::eval(&circ, &input).unwrap();
158 assert_eq!(
159 output.iter().map(|i| i.to_string()).collect::<String>(),
160 "01100110111010010100101111010100111011111000101000101100001110111000100001001100111110100101100111001010001101000010101100101110"
161 );
162 let mut input = vec![0u16; 128];
163 input.extend([1u16; 128]);
164 let output = Dummy::eval(&circ, &input).unwrap();
165 assert_eq!(
166 output.iter().map(|i| i.to_string()).collect::<String>(),
167 "10100001111101100010010110001100100001110111110101011111110011011000100101100100010010000100010100111000101111111100100100101100"
168 );
169 let mut input = [0u16; 256];
170 for key_part in input[128..].iter_mut().take(8) {
171 *key_part = 1;
172 }
173 let output = Dummy::eval(&circ, &input).unwrap();
174 assert_eq!(
175 output.iter().map(|i| i.to_string()).collect::<String>(),
176 "10110001110101110101100000100101011010110010100011111101100001010000101011010100100101000100001000001000110011110001000101010101"
177 );
178 let mut input = vec![0u16; 256];
179 input[128 + 7] = 1;
180 let output = Dummy::eval(&circ, &input).unwrap();
181 assert_eq!(
182 output.iter().map(|i| i.to_string()).collect::<String>(),
183 "11011100000011101101100001011101111110010110000100011010101110110111001001001001110011011101000101101000110001010100011001111110"
184 );
185 }
186
187 #[test]
188 fn test_gc_eval() {
189 let circ = Circuit::parse(std::io::Cursor::<&'static [u8]>::new(include_bytes!(
190 "../circuits/AES-non-expanded.txt"
191 )))
192 .unwrap();
193 let (encoder, gc, _) =
194 GarbledCircuit::garble::<WireMod2, _, _>(&circ, SwankyRng::new()).unwrap();
195 let inputs = encoder.encode_inputs(&vec![0u16; 256]);
196 gc.eval_to_wirelabels(&circ, &inputs).unwrap();
197 }
198}