Skip to main content

fancy_garbling/circuits/
aes.rs

1//! AES circuits.
2
3use crate::{
4    FancyBinary,
5    circuit::{BinaryCircuit, Circuit, CircuitInputMapper},
6};
7use std::io::Cursor;
8use swanky_channel::Channel;
9use swanky_error::Result;
10
11/// Circuit for AES-128 without key expansion.
12///
13/// For an input `(key, block)`, output `AES-128(key, block)`.
14pub struct AesNonExpanded(BinaryCircuit);
15
16impl AesNonExpanded {
17    /// Create a new [`AesNonExpanded`] circuit.
18    ///
19    /// # Performance Note!
20    /// This involves parsing a Bristol Format file, and thus is not cheap! Hence,
21    /// it is best to reuse this circuit if possible versus calling
22    /// [`AesNonExpanded::new`] every time this circuit is needed.
23    pub fn new() -> Self {
24        let circuit = BinaryCircuit::parse_bristol_format(Cursor::<&'static [u8]>::new(
25            include_bytes!("../../circuits/bristol-format/AES-non-expanded.txt"),
26        ))
27        .expect("`AES-non-expanded.txt` file should always parse correctly");
28        Self(circuit)
29    }
30}
31
32impl Default for AesNonExpanded {
33    fn default() -> Self {
34        Self::new()
35    }
36}
37
38impl<F: FancyBinary> Circuit<F> for AesNonExpanded {
39    type Input = ([F::Item; 128], [F::Item; 128]);
40    type Output = [F::Item; 128];
41
42    fn execute(
43        &self,
44        backend: &mut F,
45        inputs: Self::Input,
46        channel: &mut Channel,
47    ) -> Result<Self::Output> {
48        // Confusingly, the AES Bristol Format file takes the block _first_, and
49        // the key _second_. Since the more conventional approach is to take
50        // (key, block), we swap the values here before feeding them into the
51        // `BinaryCircuit` for evaluation, leaving the interface using the
52        // conventional approach.
53        let mut combined = inputs.1.to_vec();
54        combined.extend_from_slice(&inputs.0);
55        let output = self.0.execute(backend, combined, channel)?;
56        Ok(output
57            .try_into()
58            .expect("AES output should always be 128 elements"))
59    }
60}
61
62impl<F: FancyBinary> CircuitInputMapper<F> for AesNonExpanded {
63    fn map(&self, inputs: Vec<<F as crate::Fancy>::Item>) -> Self::Input {
64        assert_eq!(inputs.len(), 256);
65        let (key, block) = inputs.split_at(128);
66        (
67            key.to_vec().try_into().unwrap(),
68            block.to_vec().try_into().unwrap(),
69        )
70    }
71
72    fn ninputs(&self) -> usize {
73        256
74    }
75
76    fn modulus(&self, _: usize) -> u16 {
77        2
78    }
79}
80
81#[cfg(test)]
82mod test {
83    use super::*;
84
85    #[test]
86    fn aes_non_expanded() {
87        use crate::dummy::{Dummy, DummyVal};
88
89        let aes = AesNonExpanded::new();
90
91        let key = [DummyVal::new(0, 2); 128];
92        let block = [DummyVal::new(0, 2); 128];
93        let output = Dummy::eval(&aes, (key, block)).unwrap();
94        assert_eq!(
95            output
96                .iter()
97                .map(|i| i.val().to_string())
98                .collect::<String>(),
99            "01100110111010010100101111010100111011111000101000101100001110111000100001001100111110100101100111001010001101000010101100101110"
100        );
101
102        let key = [DummyVal::new(1, 2); 128];
103        let block = [DummyVal::new(0, 2); 128];
104        let output = Dummy::eval(&aes, (key, block)).unwrap();
105        assert_eq!(
106            output
107                .iter()
108                .map(|i| i.val().to_string())
109                .collect::<String>(),
110            "10100001111101100010010110001100100001110111110101011111110011011000100101100100010010000100010100111000101111111100100100101100"
111        );
112
113        let mut key = [DummyVal::new(0, 2); 128];
114        for key_part in key.iter_mut().take(8) {
115            *key_part = DummyVal::new(1, 2);
116        }
117        let block = [DummyVal::new(0, 2); 128];
118        let output = Dummy::eval(&aes, (key, block)).unwrap();
119        assert_eq!(
120            output
121                .iter()
122                .map(|i| i.val().to_string())
123                .collect::<String>(),
124            "10110001110101110101100000100101011010110010100011111101100001010000101011010100100101000100001000001000110011110001000101010101"
125        );
126
127        let mut key = [DummyVal::new(0, 2); 128];
128        key[7] = DummyVal::new(1, 2);
129        let block = [DummyVal::new(0, 2); 128];
130        let output = Dummy::eval(&aes, (key, block)).unwrap();
131        assert_eq!(
132            output
133                .iter()
134                .map(|i| i.val().to_string())
135                .collect::<String>(),
136            "11011100000011101101100001011101111110010110000100011010101110110111001001001001110011011101000101101000110001010100011001111110"
137        );
138    }
139
140    #[test]
141    fn aes_non_expanded_gc_eval() {
142        use crate::{WireMod2, classic::GarbledCircuit};
143        use swanky_rng::SwankyRng;
144
145        let aes = AesNonExpanded::new();
146
147        let (encoder, gc, _) =
148            GarbledCircuit::garble::<WireMod2, _, _>(&aes, SwankyRng::new()).unwrap();
149        let inputs = encoder.encode_inputs(&vec![0u16; 256]);
150        let key = inputs[..128].try_into().unwrap();
151        let block = inputs[128..].try_into().unwrap();
152        gc.eval_to_wirelabels(&aes, (key, block)).unwrap();
153    }
154}