arnaucube
arnaucube/plonky2-crypto-gadgetsPublic Self-hosted repository
plonky2-crypto-gadgets/src/circuit.rs
src/circuit.rsRaw
  1use std::{array, sync::Arc};
  2
  3use plonky2::{
  4    field::{extension::Extendable, goldilocks_field::GoldilocksField as F, types::Field},
  5    hash::hash_types::RichField,
  6    iop::{
  7        target::{BoolTarget, Target},
  8        witness::{PartialWitness, WitnessWrite},
  9    },
 10    plonk::circuit_builder::CircuitBuilder,
 11};
 12
 13use crate::{
 14    constants::{RCON, SBOX},
 15    native::{rot_word, shift_rows, State},
 16    D,
 17};
 18
 19#[derive(Debug, Copy, Clone)]
 20pub struct StateTarget(pub [[ByteArrayTarget; 4]; 4]);
 21
 22// TODO: Normalise terminology!
 23pub type ByteArrayTarget = [BoolTarget; 8];
 24
 25pub trait CircuitBuilderAESState<F: RichField + Extendable<D>, const D: usize> {
 26    /// Adds state target.
 27    fn add_virtual_state(&mut self) -> StateTarget;
 28
 29    /// AES cipher as in spec.
 30    fn aes_cipher<const NR: usize>(
 31        &mut self,
 32        sbox_lut_idx: usize,
 33        mix_matrix: [[ByteArrayTarget; 4]; 4],
 34        s: StateTarget,
 35
 36        w: [[ByteArrayTarget; 4]; 4 * (NR + 1)],
 37    ) -> StateTarget;
 38
 39    /// Applies sub_bytes routine to state.
 40    fn state_sub_bytes(&mut self, sbox_lut_idx: usize, s: StateTarget) -> StateTarget;
 41
 42    /// SubWord
 43    fn state_sub_word(
 44        &mut self,
 45        sbox_lut_idx: usize,
 46        word: [ByteArrayTarget; 4],
 47    ) -> [ByteArrayTarget; 4];
 48
 49    /// MixColumns
 50    fn state_mix_columns(
 51        &mut self,
 52        mix_matrix: [[ByteArrayTarget; 4]; 4],
 53        s: StateTarget,
 54    ) -> StateTarget;
 55
 56    /// AddRoundKey
 57    fn state_add_round_key(
 58        &mut self,
 59        round_key: &[[ByteArrayTarget; 4]],
 60        s: StateTarget,
 61    ) -> StateTarget;
 62
 63    /// KeyExpansion
 64    fn key_expansion<const NK: usize, const NR: usize>(
 65        &mut self,
 66        sbox_lut_idx: usize,
 67        key: [ByteArrayTarget; 4 * NK],
 68    ) -> [[ByteArrayTarget; 4]; 4 * (NR + 1)];
 69
 70    /// GF(2^8) addition
 71    fn gf_2_8_add(&mut self, x: Target, y: Target) -> Target;
 72
 73    /// GF(2^8) addition actinb on bit arrays
 74    fn gf_2_8_add_bits(
 75        &mut self,
 76        x_bits: [BoolTarget; 8],
 77        y_bits: [BoolTarget; 8],
 78    ) -> [BoolTarget; 8];
 79
 80    /// GF(2^8) multiplication
 81    fn gf_2_8_mul(&mut self, x: Target, y: Target) -> Target;
 82
 83    /// GF(2^8) multiplication acting on bit arrays
 84    fn gf_2_8_mul_bits(&mut self, x: [BoolTarget; 8], y: [BoolTarget; 8]) -> [BoolTarget; 8];
 85
 86    /// Bytearray inner product.
 87    fn bytearray_ip_bits<const N: usize>(
 88        &mut self,
 89        x: [[BoolTarget; 8]; N],
 90        y: [[BoolTarget; 8]; N],
 91    ) -> [BoolTarget; 8];
 92
 93    /// Bytearray matrix application.
 94    fn bytearray_matrix_apply_bits<const M: usize, const N: usize>(
 95        &mut self,
 96        a: [[ByteArrayTarget; N]; M],
 97        x: [ByteArrayTarget; N],
 98    ) -> [ByteArrayTarget; M];
 99}
100
101impl CircuitBuilderAESState<F, D> for CircuitBuilder<F, D> {
102    fn add_virtual_state(&mut self) -> StateTarget {
103        StateTarget(array::from_fn(|_| {
104            array::from_fn(|_| array::from_fn(|_| self.add_virtual_bool_target_safe()))
105        }))
106    }
107
108    fn aes_cipher<const NR: usize>(
109        &mut self,
110        sbox_lut_idx: usize,
111        mix_matrix: [[ByteArrayTarget; 4]; 4],
112        s: StateTarget,
113        w: [[ByteArrayTarget; 4]; 4 * (NR + 1)],
114    ) -> StateTarget {
115        let mut s = s;
116        s = self.state_add_round_key(&w[0..4], s);
117        (1..NR).for_each(|i| {
118            s = self.state_sub_bytes(sbox_lut_idx, s);
119            s = StateTarget(shift_rows(s.0));
120            s = self.state_mix_columns(mix_matrix, s);
121            s = self.state_add_round_key(&w[4 * i..4 * (i + 1)], s);
122        });
123        s = self.state_sub_bytes(sbox_lut_idx, s);
124        s = StateTarget(shift_rows(s.0));
125        self.state_add_round_key(&w[4 * NR..4 * (NR + 1)], s)
126    }
127
128    fn state_sub_bytes(&mut self, sbox_lut_idx: usize, s: StateTarget) -> StateTarget {
129        StateTarget(array::from_fn(|i| {
130            self.state_sub_word(sbox_lut_idx, s.0[i])
131        }))
132    }
133
134    fn state_sub_word(
135        &mut self,
136        sbox_lut_idx: usize,
137        word: [ByteArrayTarget; 4],
138    ) -> [ByteArrayTarget; 4] {
139        array::from_fn(|i| {
140            let byte_target = target_from_bitarray(self, &word[i]);
141            let out_target = self.add_lookup_from_index(byte_target, sbox_lut_idx);
142            bitarray_from_bytetarget(self, out_target)
143        })
144    }
145
146    fn state_mix_columns(
147        &mut self,
148        mix_matrix: [[ByteArrayTarget; 4]; 4],
149        s: StateTarget,
150    ) -> StateTarget {
151        let cols: [_; 4] = array::from_fn(|i| array::from_fn(|j| s.0[j][i]));
152        let out_cols: [_; 4] =
153            array::from_fn(|i| self.bytearray_matrix_apply_bits(mix_matrix, cols[i]));
154        StateTarget(array::from_fn(|i| array::from_fn(|j| out_cols[j][i])))
155    }
156
157    fn state_add_round_key(
158        &mut self,
159        round_key: &[[ByteArrayTarget; 4]],
160        s: StateTarget,
161    ) -> StateTarget {
162        StateTarget(array::from_fn(|i| {
163            array::from_fn(|j| self.gf_2_8_add_bits(s.0[i][j], round_key[j][i]))
164        }))
165    }
166
167    fn key_expansion<const NK: usize, const NR: usize>(
168        &mut self,
169        sbox_lut_idx: usize,
170        key: [ByteArrayTarget; 4 * NK],
171    ) -> [[ByteArrayTarget; 4]; 4 * (NR + 1)] {
172        let rcon: [ByteArrayTarget; 11] = array::from_fn(|i| {
173            let rcon_bits = le_bits_from_byte(RCON[i]);
174            array::from_fn(|j| self.constant_bool(rcon_bits[j]))
175        });
176
177        let key: [[_; 4]; NK] = array::from_fn(|i| array::from_fn(|j| key[4 * i + j]));
178
179        let key_additions = (NK..4 * (NR + 1))
180            .scan(key.to_vec(), |st, i| {
181                let offset = if i % NK == 0 {
182                    let term = self.state_sub_word(sbox_lut_idx, rot_word(st[i - 1]));
183                    array::from_fn(|j| {
184                        if j == 0 {
185                            self.gf_2_8_add_bits(term[j], rcon[i / NK])
186                        } else {
187                            term[j]
188                        }
189                    })
190                } else if (NK > 6) && (i % NK == 4) {
191                    self.state_sub_word(sbox_lut_idx, st[i - 1])
192                } else {
193                    st[i - 1]
194                };
195
196                let cur_val = array::from_fn(|j| self.gf_2_8_add_bits(st[i - NK][j], offset[j]));
197                st.push(cur_val);
198                Some(cur_val)
199            })
200            .collect::<Vec<_>>();
201        std::array::from_fn(|i| {
202            if i < NK {
203                key[i]
204            } else {
205                key_additions[i - NK]
206            }
207        })
208    }
209
210    fn gf_2_8_add(&mut self, x: Target, y: Target) -> Target {
211        let x_bits = bitarray_from_bytetarget(self, x);
212        let y_bits = bitarray_from_bytetarget(self, y);
213
214        let sum_bits = self.gf_2_8_add_bits(x_bits, y_bits);
215        target_from_bitarray(self, &sum_bits)
216    }
217
218    fn gf_2_8_add_bits(
219        &mut self,
220        x_bits: [BoolTarget; 8],
221        y_bits: [BoolTarget; 8],
222    ) -> [BoolTarget; 8] {
223        let sum_bits = std::iter::zip(x_bits, y_bits)
224            .map(|(a, b)| xor(self, a, b))
225            .collect::<Vec<_>>();
226
227        array::from_fn(|i| sum_bits[i])
228    }
229
230    fn gf_2_8_mul(&mut self, x: Target, y: Target) -> Target {
231        let x_bits = bitarray_from_bytetarget(self, x);
232        let y_bits = bitarray_from_bytetarget(self, y);
233
234        let prod_bits = self.gf_2_8_mul_bits(x_bits, y_bits);
235        target_from_bitarray(self, &prod_bits)
236    }
237
238    fn gf_2_8_mul_bits(
239        &mut self,
240        x_bits: [BoolTarget; 8],
241        y_bits: [BoolTarget; 8],
242    ) -> [BoolTarget; 8] {
243        let zero_bits = vec![self._false(); 8];
244        assert!(x_bits.len() == 8 && y_bits.len() == 8);
245
246        let x_times = |builder: &mut CircuitBuilder<_, _>, y: &[BoolTarget]| -> [_; 8] {
247            let zero = builder._false();
248            let one = builder._true();
249
250            let potential_offset = [one, one, zero, one, one, zero, zero, zero];
251            let y_shifted: [_; 8] = array::from_fn(|i| if i == 0 { zero } else { y[i - 1] });
252
253            let offset: [_; 8] = array::from_fn(|i| builder.and(y[7], potential_offset[i]));
254            array::from_fn(|i| xor(builder, y_shifted[i], offset[i]))
255        };
256
257        let powers_times_x = (0..8)
258            .scan(x_bits.to_vec(), |st, i| {
259                if i > 0 {
260                    *st = x_times(self, st).to_vec();
261                }
262                Some(st.clone())
263            })
264            .collect::<Vec<_>>();
265
266        let prod_bits = std::iter::zip(y_bits, powers_times_x)
267            .map(|(c, o)| o.into_iter().map(|b| self.and(c, b)).collect::<Vec<_>>())
268            .collect::<Vec<_>>()
269            .into_iter()
270            .fold(zero_bits, |acc, term| {
271                std::iter::zip(acc, term)
272                    .map(|(a, b)| xor(self, a, b))
273                    .collect::<Vec<_>>()
274            });
275
276        array::from_fn(|i| prod_bits[i])
277    }
278
279    fn bytearray_ip_bits<const N: usize>(
280        &mut self,
281        x: [[BoolTarget; 8]; N],
282        y: [[BoolTarget; 8]; N],
283    ) -> [BoolTarget; 8] {
284        let zero = self.zero();
285        let zero_bits = bitarray_from_bytetarget(self, zero);
286
287        std::iter::zip(x, y).fold(zero_bits, |acc, (a, b)| {
288            let prod = self.gf_2_8_mul_bits(a, b);
289            self.gf_2_8_add_bits(acc, prod)
290        })
291    }
292
293    fn bytearray_matrix_apply_bits<const M: usize, const N: usize>(
294        &mut self,
295        a: [[ByteArrayTarget; N]; M],
296        x: [ByteArrayTarget; N],
297    ) -> [ByteArrayTarget; M] {
298        std::array::from_fn(|i| self.bytearray_ip_bits(a[i], x))
299    }
300}
301
302pub trait PartialWitnessByteArray {
303    fn set_byte_array_target(&mut self, target: ByteArrayTarget, value: u8) -> anyhow::Result<()>;
304}
305
306impl<F: Field> PartialWitnessByteArray for PartialWitness<F> {
307    fn set_byte_array_target(&mut self, target: ByteArrayTarget, value: u8) -> anyhow::Result<()> {
308        let value_bits = le_bits_from_byte(value);
309        std::iter::zip(target, value_bits).try_for_each(|(t, v)| self.set_bool_target(t, v))
310    }
311}
312
313pub trait PartialWitnessAESState {
314    fn set_target_state(&mut self, target: StateTarget, value: State) -> anyhow::Result<()>;
315}
316
317impl<F: Field> PartialWitnessAESState for PartialWitness<F> {
318    fn set_target_state(&mut self, target: StateTarget, value: State) -> anyhow::Result<()> {
319        std::iter::zip(target.0, value).try_for_each(|(t, v)| {
320            std::iter::zip(t, v).try_for_each(|(t, v)| self.set_byte_array_target(t, v))
321        })
322    }
323}
324
325pub fn sbox_lut(builder: &mut CircuitBuilder<F, D>) -> usize {
326    builder.add_lookup_table_from_pairs(Arc::new(
327        SBOX.into_iter()
328            .enumerate()
329            .map(|(i, o)| (i as u16, o as u16))
330            .collect::<Vec<_>>(),
331    ))
332}
333
334pub fn state_mix_matrix_bits(builder: &mut CircuitBuilder<F, D>) -> [[[BoolTarget; 8]; 4]; 4] {
335    let one = builder.one();
336    let two = builder.two();
337    let three = builder.constant(F::from_canonical_u64(3));
338
339    let matrix = [
340        [two, three, one, one],
341        [one, two, three, one],
342        [one, one, two, three],
343        [three, one, one, two],
344    ];
345
346    array::from_fn(|i| array::from_fn(|j| bitarray_from_bytetarget(builder, matrix[i][j])))
347}
348
349pub fn xor(builder: &mut CircuitBuilder<F, D>, x: BoolTarget, y: BoolTarget) -> BoolTarget {
350    let x_or_y = builder.or(x, y);
351    BoolTarget::new_unsafe(builder.arithmetic(
352        F::NEG_ONE,
353        F::ONE,
354        x.target,
355        y.target,
356        x_or_y.target,
357    ))
358}
359
360// LE bit array conversions
361pub fn target_from_bitarray(builder: &mut CircuitBuilder<F, D>, x: &[BoolTarget]) -> Target {
362    let zero = builder.zero();
363    let two = builder.two();
364    x.iter()
365        .rev()
366        .fold(zero, |acc, b| builder.mul_add(two, acc, b.target))
367}
368
369pub fn bitarray_from_bytetarget(builder: &mut CircuitBuilder<F, D>, x: Target) -> [BoolTarget; 8] {
370    let x_bits = builder.split_le(x, 8);
371    array::from_fn(|i| x_bits[i])
372}
373
374pub fn le_bits_from_byte(v: u8) -> [bool; 8] {
375    let v_bits = (0..8)
376        .scan(v, |st, _| {
377            let cur_bit = (*st % 2) != 0;
378            *st >>= 1;
379            Some(cur_bit)
380        })
381        .collect::<Vec<_>>();
382    array::from_fn(|i| v_bits[i])
383}
384
385#[cfg(test)]
386mod tests {
387    use std::array;
388
389    use anyhow::Result;
390    use plonky2::{
391        field::{goldilocks_field::GoldilocksField as F, types::Field},
392        iop::witness::{PartialWitness, WitnessWrite},
393        plonk::{
394            circuit_builder::CircuitBuilder, circuit_data::CircuitConfig,
395            config::PoseidonGoldilocksConfig,
396        },
397    };
398    use rand::RngExt;
399
400    use crate::{
401        circuit::{
402            sbox_lut, state_mix_matrix_bits, ByteArrayTarget, CircuitBuilderAESState,
403            PartialWitnessAESState, PartialWitnessByteArray,
404        },
405        native::{encrypt_block, key_expansion, mix_columns, sub_bytes, State},
406    };
407
408    use super::D;
409
410    #[test]
411    fn test_sub_bytes() -> Result<()> {
412        // Circuit declaration
413        let config = CircuitConfig::standard_recursion_config();
414        let mut builder = CircuitBuilder::<F, D>::new(config);
415
416        let state_target = builder.add_virtual_state();
417        let sbox_lut = sbox_lut(&mut builder);
418        let out_state_target = builder.state_sub_bytes(sbox_lut, state_target);
419
420        let data = builder.build::<PoseidonGoldilocksConfig>();
421        let mut rng = rand::rng();
422
423        let test_states: [State; 10] = array::from_fn(|_| array::from_fn(|_| rng.random()));
424
425        test_states.into_iter().try_for_each(|s| {
426            let expected_result = sub_bytes(s);
427
428            let mut pw = PartialWitness::<F>::new();
429            pw.set_target_state(state_target, s)?;
430            pw.set_target_state(out_state_target, expected_result)?;
431
432            let proof = data.prove(pw)?;
433            data.verify(proof)
434        })
435    }
436
437    #[test]
438    fn test_mix_columns() -> Result<()> {
439        // Circuit declaration
440        let config = CircuitConfig::standard_recursion_config();
441        let mut builder = CircuitBuilder::<F, D>::new(config);
442
443        let state_target = builder.add_virtual_state();
444        let mix_matrix = state_mix_matrix_bits(&mut builder);
445        let out_state_target = builder.state_mix_columns(mix_matrix, state_target);
446
447        let data = builder.build::<PoseidonGoldilocksConfig>();
448        let mut rng = rand::rng();
449
450        let test_states: [State; 10] = array::from_fn(|_| array::from_fn(|_| rng.random()));
451
452        test_states.into_iter().try_for_each(|s| {
453            let expected_result = mix_columns(s);
454
455            let mut pw = PartialWitness::<F>::new();
456            pw.set_target_state(state_target, s)?;
457            pw.set_target_state(out_state_target, expected_result)?;
458
459            let proof = data.prove(pw)?;
460            data.verify(proof)
461        })
462    }
463
464    #[test]
465    fn test_gf_2_8_mul() -> Result<()> {
466        // Circuit declaration
467        let config = CircuitConfig::standard_recursion_config();
468        let mut builder = CircuitBuilder::<F, D>::new(config);
469
470        let a = builder.add_virtual_target();
471        let b = builder.add_virtual_target();
472
473        let a_times_b = builder.gf_2_8_mul(a, b);
474
475        let data = builder.build::<PoseidonGoldilocksConfig>();
476
477        let test_values = [
478            (0x57, 0x01, 0x57),
479            (0x57, 0x02, 0xae),
480            (0x57, 0x04, 0x47),
481            (0x57, 0x08, 0x8e),
482            (0x57, 0x10, 0x07),
483            (0x57, 0x20, 0x0e),
484            (0x57, 0x40, 0x1c),
485            (0x57, 0x80, 0x38),
486            (0x57, 0x13, 0xfe),
487        ];
488
489        test_values
490            .into_iter()
491            .map(|(a, b, c)| {
492                (
493                    F::from_canonical_u8(a),
494                    F::from_canonical_u8(b),
495                    F::from_canonical_u8(c),
496                )
497            })
498            .try_for_each(|(a_val, b_val, expected_result)| {
499                let mut pw = PartialWitness::<F>::new();
500                pw.set_target(a, a_val)?;
501                pw.set_target(b, b_val)?;
502                pw.set_target(a_times_b, expected_result)?;
503
504                let proof = data.prove(pw)?;
505                data.verify(proof)
506            })
507    }
508
509    #[test]
510    fn test_key_expansion() -> Result<()> {
511        // AES-128
512        let key = [
513            0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
514            0x4f, 0x3c,
515        ];
516        test_key_expansion_op::</*Nk,Nb,Nr*/ 4, 4, 10>(key)?;
517
518        // AES-196
519        let key = [
520            0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90,
521            0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
522        ];
523        test_key_expansion_op::</*Nk,Nb,Nr*/ 6, 4, 12>(key)?;
524
525        // AES-256
526        let key = [
527            0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d,
528            0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3,
529            0x09, 0x14, 0xdf, 0xf4,
530        ];
531        test_key_expansion_op::</*Nk,Nb,Nr*/ 8, 4, 14>(key)?;
532
533        Ok(())
534    }
535
536    /// Analogous tests to `native::tests::test_key_expansion`.
537    fn test_key_expansion_op<const NK: usize, const NB: usize, const NR: usize>(
538        key: [u8; NK * NB],
539    ) -> Result<()>
540    where
541        [(); 4 * (NR + 1)]:,
542        [(); 4 * NK]:,
543    {
544        // Circuit declaration
545        let config = CircuitConfig::standard_recursion_config();
546        let mut builder = CircuitBuilder::<F, D>::new(config);
547
548        let key_target: [ByteArrayTarget; _] =
549            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
550        let sbox_lut = sbox_lut(&mut builder);
551        let expanded_key_target = builder.key_expansion::<NK, NR>(sbox_lut, key_target);
552
553        println!(
554            "key_expansion(NK:{}, NB:{}, NR:{}) num_gates: {}",
555            NK,
556            NB,
557            NR,
558            builder.num_gates()
559        );
560        let data = builder.build::<PoseidonGoldilocksConfig>();
561
562        let w = key_expansion::<NK, NB, NR>(&key);
563
564        let mut pw = PartialWitness::<F>::new();
565
566        std::iter::zip(key_target, key).try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
567
568        std::iter::zip(expanded_key_target, w).try_for_each(|(t, v)| {
569            std::iter::zip(t, v).try_for_each(|(t, v)| pw.set_byte_array_target(t, v))
570        })?;
571
572        let proof = data.prove(pw)?;
573        data.verify(proof)
574    }
575
576    /// test against paper test vector (appendix B)
577    #[test]
578    fn test_encrypt_block_test_vector() -> Result<()> {
579        // Circuit declaration
580        let config = CircuitConfig::standard_recursion_config();
581        let mut builder = CircuitBuilder::<F, D>::new(config);
582
583        let key_target: [ByteArrayTarget; _] =
584            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
585        let sbox_lut = sbox_lut(&mut builder);
586        let mix_matrix = state_mix_matrix_bits(&mut builder);
587        let expanded_key_target = builder.key_expansion::<4, 10>(sbox_lut, key_target);
588
589        let input_state_target = builder.add_virtual_state();
590
591        let output = builder.aes_cipher(
592            sbox_lut,
593            mix_matrix,
594            input_state_target,
595            expanded_key_target,
596        );
597
598        println!("aes_cipher num_gates: {}", builder.num_gates());
599
600        let data = builder.build::<PoseidonGoldilocksConfig>();
601
602        let input_state: [[u8; _]; _] = [
603            [0x32, 0x43, 0xf6, 0xa8],
604            [0x88, 0x5a, 0x30, 0x8d],
605            [0x31, 0x31, 0x98, 0xa2],
606            [0xe0, 0x37, 0x07, 0x34],
607        ];
608
609        let key = [
610            0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
611            0x4f, 0x3c,
612        ];
613
614        let expanded_key = key_expansion::<4, 4, 10>(&key);
615
616        let expected_result: [[u8; _]; _] = [
617            [0x39, 0x02, 0xdc, 0x19],
618            [0x25, 0xdc, 0x11, 0x6a],
619            [0x84, 0x09, 0x85, 0x0b],
620            [0x1d, 0xfb, 0x97, 0x32],
621        ];
622
623        // TODO
624        let expected_result = encrypt_block::<10>(
625            &array::from_fn(|i| input_state[i % 4][i / 4]),
626            &expanded_key,
627        );
628
629        let mut pw = PartialWitness::<F>::new();
630
631        std::iter::zip(key_target, key).try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
632
633        pw.set_target_state(input_state_target, input_state)?;
634
635        std::iter::zip(expanded_key_target, expanded_key).try_for_each(|(t, v)| {
636            std::iter::zip(t, v).try_for_each(|(t, v)| pw.set_byte_array_target(t, v))
637        })?;
638
639        std::iter::zip(output.0, expected_result).try_for_each(|(o, e)| {
640            std::iter::zip(o, e).try_for_each(|(o, e)| pw.set_byte_array_target(o, e))
641        })?;
642
643        let proof = data.prove(pw)?;
644        data.verify(proof)
645    }
646}