acvm/pwg/blackbox/
aes128.rs

1use acir::{
2    AcirField,
3    circuit::opcodes::FunctionInput,
4    native_types::{Witness, WitnessMap},
5};
6use acvm_blackbox_solver::aes128_encrypt;
7use itertools::Itertools;
8
9use crate::{OpcodeResolutionError, pwg::insert_value};
10
11use super::utils::{to_u8_array, to_u8_vec};
12
13pub(super) fn solve_aes128_encryption_opcode<F: AcirField>(
14    initial_witness: &mut WitnessMap<F>,
15    inputs: &[FunctionInput<F>],
16    iv: &[FunctionInput<F>; 16],
17    key: &[FunctionInput<F>; 16],
18    outputs: &[Witness],
19) -> Result<(), OpcodeResolutionError<F>> {
20    let ciphertext = execute_aes128_encryption_opcode(initial_witness, inputs, iv, key)?;
21
22    // Write witness assignments
23    for (output_witness, value) in outputs.iter().zip_eq(ciphertext) {
24        insert_value(output_witness, F::from(u128::from(value)), initial_witness)?;
25    }
26
27    Ok(())
28}
29
30pub(crate) fn execute_aes128_encryption_opcode<F: AcirField>(
31    initial_witness: &WitnessMap<F>,
32    inputs: &[FunctionInput<F>],
33    iv: &[FunctionInput<F>; 16],
34    key: &[FunctionInput<F>; 16],
35) -> Result<Vec<u8>, OpcodeResolutionError<F>> {
36    let scalars = to_u8_vec(initial_witness, inputs)?;
37
38    let iv = to_u8_array(initial_witness, iv)?;
39    let key = to_u8_array(initial_witness, key)?;
40
41    let ciphertext = aes128_encrypt(&scalars, iv, key)?;
42
43    Ok(ciphertext)
44}
45
46#[cfg(test)]
47mod tests {
48    use crate::pwg::blackbox::solve_aes128_encryption_opcode;
49    use acir::{
50        FieldElement,
51        circuit::opcodes::FunctionInput,
52        native_types::{Witness, WitnessMap},
53    };
54    use std::collections::BTreeMap;
55
56    #[test]
57    #[allow(clippy::needless_range_loop)]
58    fn test_aes() {
59        // Test vector is coming from Barretenberg (cf. aes128.test.cpp)
60        // cspell:disable (hex literals below trigger false positives)
61        let mut initial_witness = WitnessMap::from(BTreeMap::from_iter([
62            // Key { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }
63            (Witness(1), FieldElement::from(0x2bu128)),
64            (Witness(2), FieldElement::from(0x7eu128)),
65            (Witness(3), FieldElement::from(0x15u128)),
66            (Witness(4), FieldElement::from(0x16u128)),
67            (Witness(5), FieldElement::from(0x28u128)),
68            (Witness(6), FieldElement::from(0xaeu128)),
69            (Witness(7), FieldElement::from(0xd2u128)),
70            (Witness(8), FieldElement::from(0xa6u128)),
71            (Witness(9), FieldElement::from(0xabu128)),
72            (Witness(10), FieldElement::from(0xf7u128)),
73            (Witness(11), FieldElement::from(0x15u128)),
74            (Witness(12), FieldElement::from(0x88u128)),
75            (Witness(13), FieldElement::from(0x09u128)),
76            (Witness(14), FieldElement::from(0xcfu128)),
77            (Witness(15), FieldElement::from(0x4fu128)),
78            (Witness(16), FieldElement::from(0x3cu128)),
79            // IV {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }
80            (Witness(17), FieldElement::from(0x00u128)),
81            (Witness(18), FieldElement::from(0x01u128)),
82            (Witness(19), FieldElement::from(0x02u128)),
83            (Witness(20), FieldElement::from(0x03u128)),
84            (Witness(21), FieldElement::from(0x04u128)),
85            (Witness(22), FieldElement::from(0x05u128)),
86            (Witness(23), FieldElement::from(0x06u128)),
87            (Witness(24), FieldElement::from(0x07u128)),
88            (Witness(25), FieldElement::from(0x08u128)),
89            (Witness(26), FieldElement::from(0x09u128)),
90            (Witness(27), FieldElement::from(0x0au128)),
91            (Witness(28), FieldElement::from(0x0bu128)),
92            (Witness(29), FieldElement::from(0x0cu128)),
93            (Witness(30), FieldElement::from(0x0du128)),
94            (Witness(31), FieldElement::from(0x0eu128)),
95            (Witness(32), FieldElement::from(0x0fu128)),
96            // Input { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
97            //        0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
98            //        0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
99            //        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 };
100            (Witness(33), FieldElement::from(0x6bu128)),
101            (Witness(34), FieldElement::from(0xc1u128)),
102            (Witness(35), FieldElement::from(0xbeu128)),
103            (Witness(36), FieldElement::from(0xe2u128)),
104            (Witness(37), FieldElement::from(0x2eu128)),
105            (Witness(38), FieldElement::from(0x40u128)),
106            (Witness(39), FieldElement::from(0x9fu128)),
107            (Witness(40), FieldElement::from(0x96u128)),
108            (Witness(41), FieldElement::from(0xe9u128)),
109            (Witness(42), FieldElement::from(0x3du128)),
110            (Witness(43), FieldElement::from(0x7eu128)),
111            (Witness(44), FieldElement::from(0x11u128)),
112            (Witness(45), FieldElement::from(0x73u128)),
113            (Witness(46), FieldElement::from(0x93u128)),
114            (Witness(47), FieldElement::from(0x17u128)),
115            (Witness(48), FieldElement::from(0x2au128)),
116            (Witness(49), FieldElement::from(0xaeu128)),
117            (Witness(50), FieldElement::from(0x2du128)),
118            (Witness(51), FieldElement::from(0x8au128)),
119            (Witness(52), FieldElement::from(0x57u128)),
120            (Witness(53), FieldElement::from(0x1eu128)),
121            (Witness(54), FieldElement::from(0x03u128)),
122            (Witness(55), FieldElement::from(0xacu128)),
123            (Witness(56), FieldElement::from(0x9cu128)),
124            (Witness(57), FieldElement::from(0x9eu128)),
125            (Witness(58), FieldElement::from(0xb7u128)),
126            (Witness(59), FieldElement::from(0x6fu128)),
127            (Witness(60), FieldElement::from(0xacu128)),
128            (Witness(61), FieldElement::from(0x45u128)),
129            (Witness(62), FieldElement::from(0xafu128)),
130            (Witness(63), FieldElement::from(0x8eu128)),
131            (Witness(64), FieldElement::from(0x51u128)),
132            (Witness(65), FieldElement::from(0x30u128)),
133            (Witness(66), FieldElement::from(0xc8u128)),
134            (Witness(67), FieldElement::from(0x1cu128)),
135            (Witness(68), FieldElement::from(0x46u128)),
136            (Witness(69), FieldElement::from(0xa3u128)),
137            (Witness(70), FieldElement::from(0x5cu128)),
138            (Witness(71), FieldElement::from(0xe4u128)),
139            (Witness(72), FieldElement::from(0x11u128)),
140            (Witness(73), FieldElement::from(0xe5u128)),
141            (Witness(74), FieldElement::from(0xfbu128)),
142            (Witness(75), FieldElement::from(0xc1u128)),
143            (Witness(76), FieldElement::from(0x19u128)),
144            (Witness(77), FieldElement::from(0x1au128)),
145            (Witness(78), FieldElement::from(0x0au128)),
146            (Witness(79), FieldElement::from(0x52u128)),
147            (Witness(80), FieldElement::from(0xefu128)),
148            (Witness(81), FieldElement::from(0xf6u128)),
149            (Witness(82), FieldElement::from(0x9fu128)),
150            (Witness(83), FieldElement::from(0x24u128)),
151            (Witness(84), FieldElement::from(0x45u128)),
152            (Witness(85), FieldElement::from(0xdfu128)),
153            (Witness(86), FieldElement::from(0x4fu128)),
154            (Witness(87), FieldElement::from(0x9bu128)),
155            (Witness(88), FieldElement::from(0x17u128)),
156            (Witness(89), FieldElement::from(0xadu128)),
157            (Witness(90), FieldElement::from(0x2bu128)),
158            (Witness(91), FieldElement::from(0x41u128)),
159            (Witness(92), FieldElement::from(0x7bu128)),
160            (Witness(93), FieldElement::from(0xe6u128)),
161            (Witness(94), FieldElement::from(0x6cu128)),
162            (Witness(95), FieldElement::from(0x37u128)),
163            (Witness(96), FieldElement::from(0x10u128)),
164        ]));
165        // cspell:enable
166        const INPUT_LENGTH: usize = 64;
167
168        let mut inputs = [FunctionInput::Witness(Witness(0)); 64];
169        for i in 0..INPUT_LENGTH {
170            inputs[i] = FunctionInput::Witness(Witness(33 + i as u32));
171        }
172        let mut iv = [FunctionInput::Witness(Witness(0)); 16];
173        for i in 0..16 {
174            iv[i] = FunctionInput::Witness(Witness(17 + i as u32));
175        }
176        let mut key = [FunctionInput::Witness(Witness(0)); 16];
177        for i in 0..16 {
178            key[i] = FunctionInput::Witness(Witness(1 + i as u32));
179        }
180        // With auto padding disabled, output length equals input length
181        const OUTPUT_LENGTH: usize = INPUT_LENGTH;
182        let mut outputs = vec![];
183        for i in 97..97 + OUTPUT_LENGTH {
184            outputs.push(Witness(i as u32));
185        }
186
187        solve_aes128_encryption_opcode(&mut initial_witness, &inputs, &iv, &key, &outputs).unwrap();
188        // Expected output from NIST test vectors (without padding block)
189        // See: https://github.com/keepsimple1/libaes/blob/e45afaa1e9f248375e797a52eaf40eeb0ba8515a/tests/aes.rs#L14
190        let expected_output: [u128; OUTPUT_LENGTH] = [
191            0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9,
192            0x19, 0x7d, 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a,
193            0x91, 0x76, 0x78, 0xb2, 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16,
194            0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09,
195            0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7,
196        ];
197        let expected_output = expected_output.map(FieldElement::from);
198        let expected_output: Vec<&FieldElement> = expected_output.iter().collect();
199        for i in 0..OUTPUT_LENGTH {
200            assert_eq!(initial_witness[&Witness(97 + i as u32)], *expected_output[i]);
201        }
202    }
203}