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