arnaucube
arnaucube/plonky2-crypto-gadgetsPublic Self-hosted repository
plonky2-crypto-gadgets/src/circuit_gcm.rs
src/circuit_gcm.rsRaw
  1//! For LICENSE check out https://github.com/0xPARC/plonky2-aes/blob/main/LICENSE
  2
  3use anyhow::Result;
  4use std::array;
  5
  6use plonky2::{
  7    field::{goldilocks_field::GoldilocksField as F, types::Field},
  8    iop::target::BoolTarget,
  9    iop::witness::PartialWitness,
 10    plonk::circuit_builder::CircuitBuilder,
 11};
 12
 13use crate::{
 14    circuit_aes::{
 15        le_bits_from_byte, sbox_lut, state_mix_matrix_bits, xor, ByteTarget,
 16        CircuitBuilderAESState, PartialWitnessByteArray, StateTarget,
 17    },
 18    constants::TAG_LEN,
 19    D,
 20};
 21
 22pub struct AesGcmTarget<
 23    const NK: usize,
 24    const NB: usize,
 25    const NR: usize,
 26    // L: max size of plaintext to cipher.
 27    const L: usize,
 28    const TAG: bool,
 29> where
 30    [(); NK * NB]:,
 31    [(); 4 * (NR + 1)]:, // TODO rm
 32{
 33    key: [ByteTarget; NK * NB],
 34    nonce: [ByteTarget; 12],
 35    pt: [ByteTarget; L],
 36
 37    ct: [ByteTarget; L],
 38    tag: [ByteTarget; TAG_LEN / 8],
 39}
 40
 41impl<const NK: usize, const NB: usize, const NR: usize, const L: usize, const TAG: bool>
 42    AesGcmTarget<NK, NB, NR, L, TAG>
 43where
 44    [(); NK * NB]:,
 45    [(); 4 * (NR + 1)]:, // TODO rm
 46{
 47    pub fn new_virtual(builder: &mut CircuitBuilder<F, D>) -> Self {
 48        let key: [ByteTarget; NK * NB] =
 49            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
 50        let nonce: [ByteTarget; 12] =
 51            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
 52        let pt: [ByteTarget; L] =
 53            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
 54
 55        let ct: [ByteTarget; L] =
 56            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
 57        let tag: [ByteTarget; TAG_LEN / 8] =
 58            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
 59
 60        Self {
 61            key,
 62            nonce,
 63            pt,
 64            ct,
 65            tag,
 66        }
 67    }
 68
 69    pub fn build_circuit(&self, builder: &mut CircuitBuilder<F, D>)
 70    // where
 71    // [(); NK * NB]:,
 72    // [(); 4 * (NR + 1)]:,
 73    {
 74        let sbox_lut = sbox_lut(builder);
 75        let mix_matrix = state_mix_matrix_bits(builder);
 76
 77        let a: &[ByteTarget] = &[]; // additional authenticated data
 78
 79        let expanded_key = builder.key_expansion::<NK, NB, NR>(sbox_lut, self.key);
 80
 81        let empty_state = builder.empty_state();
 82
 83        // 1. CIPH_K
 84        let h = builder
 85            .encrypt_block::<NR>(sbox_lut, mix_matrix, empty_state, expanded_key)
 86            .flatten();
 87
 88        // 2. J_0
 89        let zero_byte = builder.zero_byte();
 90        let one_byte = array::from_fn(|i| {
 91            // 0x01
 92            if i == 0 {
 93                builder._true()
 94            } else {
 95                builder._false()
 96            }
 97        });
 98        let j0: [ByteTarget; 16] = if self.nonce.len() * 8 == 96 {
 99            // J_0 = IV || 0^31 || 1
100            let mut out = [zero_byte; 16];
101            out[..12].copy_from_slice(self.nonce.as_slice());
102            out[12..16].copy_from_slice(&[zero_byte, zero_byte, zero_byte, one_byte]);
103            out
104        } else {
105            panic!("unsuported at initial version; nonce.len()=12 (96 bits)");
106        };
107
108        // 3. C=GCTR()
109        let inc32_j0 = inc32_target(builder, j0);
110        let c = gctr_target::<NR, L>(
111            builder,
112            sbox_lut,
113            mix_matrix,
114            expanded_key,
115            &inc32_j0,
116            &self.pt,
117        );
118
119        // connect the ct value to the external ct value
120        for byte in 0..L {
121            for bit in 0..8 {
122                builder.connect(self.ct[byte][bit].target, c[byte][bit].target);
123            }
124        }
125
126        // the rest of the logic is for the tag; for some use cases we might skip it
127        // (saving a notable amount of gates, about double)
128        if !TAG {
129            return;
130        }
131
132        // 4. u, v
133        let u: usize = 16 * (L as f64 / 16_f64).ceil() as usize - L; // L=x.len()
134        let v: usize = 16 * (a.len() as f64 / 16_f64).ceil() as usize - a.len();
135
136        // 5. S = GHASH()
137        let const_a_len_u8: [u8; 8] = (a.len() * 8).to_be_bytes(); // const
138        let const_c_len_u8: [u8; 8] = (c.len() * 8).to_be_bytes(); // const
139        let a_len: [ByteTarget; 8] = array::from_fn(|byte_i| {
140            let const_bits = le_bits_from_byte(const_a_len_u8[byte_i]);
141            let byte: ByteTarget = array::from_fn(|bit_i| {
142                let bit_goldilocks = if const_bits[bit_i] { F::ONE } else { F::ZERO };
143                BoolTarget::new_unsafe(builder.constant(bit_goldilocks))
144            });
145            byte
146        });
147        let c_len: [ByteTarget; 8] = array::from_fn(|byte_i| {
148            let const_bits = le_bits_from_byte(const_c_len_u8[byte_i]);
149            let byte: ByteTarget = array::from_fn(|bit_i| {
150                let bit_goldilocks = if const_bits[bit_i] { F::ONE } else { F::ZERO };
151                BoolTarget::new_unsafe(builder.constant(bit_goldilocks))
152            });
153            byte
154        });
155        let ghash_input_vec = vec![
156            a.to_vec(),
157            vec![zero_byte; v],
158            c.to_vec(),
159            vec![zero_byte; u],
160            a_len.to_vec(),
161            c_len.to_vec(),
162        ]
163        .concat();
164        let s = ghash_target(builder, h, ghash_input_vec);
165
166        // 6. T=MSB(GCTR()))
167        let msb_input = gctr_target(builder, sbox_lut, mix_matrix, expanded_key, &j0, &s);
168        let t_vec = msb_t_target(builder, TAG_LEN / 8, &msb_input);
169        let mut t: [ByteTarget; TAG_LEN / 8] = [zero_byte; TAG_LEN / 8];
170        t.copy_from_slice(&t_vec);
171
172        // connect the computed tag value to the external tag value
173        for byte in 0..TAG_LEN / 8 {
174            for bit in 0..8 {
175                builder.connect(self.tag[byte][bit].target, t[byte][bit].target);
176            }
177        }
178    }
179
180    pub fn set_targets(
181        &self,
182        pw: &mut PartialWitness<F>,
183        key: &[u8; NK * NB],
184        nonce: &[u8; 12],
185        pt: &[u8],
186        ct: &[u8],
187        tag: &[u8],
188    ) -> Result<()> {
189        assert!(pt.len() <= L);
190        assert!(ct.len() <= L);
191        assert!(tag.len() <= TAG_LEN / 8);
192
193        // extend to expected sizes
194        let mut pt_arr: [u8; L] = [0u8; L];
195        pt_arr.copy_from_slice(&pt);
196        let mut ct_arr: [u8; L] = [0u8; L];
197        ct_arr.copy_from_slice(&ct);
198        let mut tag_arr: [u8; TAG_LEN / 8] = [0u8; TAG_LEN / 8];
199        tag_arr.copy_from_slice(&tag);
200
201        std::iter::zip(self.key, key).try_for_each(|(t, v)| pw.set_byte_array_target(t, *v))?;
202        std::iter::zip(self.nonce, nonce).try_for_each(|(t, v)| pw.set_byte_array_target(t, *v))?;
203        std::iter::zip(self.pt, pt_arr).try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
204
205        std::iter::zip(self.ct, ct_arr).try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
206        std::iter::zip(self.tag, tag_arr).try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
207        Ok(())
208    }
209}
210
211/// L: max size of plaintext to cipher.
212fn gctr_target<const NR: usize, const L: usize>(
213    builder: &mut CircuitBuilder<F, D>,
214    sbox_lut_idx: usize,
215    mix_matrix: [[ByteTarget; 4]; 4],
216    key: [[ByteTarget; 4]; 4 * (NR + 1)],
217    icb: &[ByteTarget; 16],
218    x: &[ByteTarget; L],
219) -> [ByteTarget; L] {
220    let n = ((L * 8) as f64 / 128_f64).ceil() as usize;
221
222    let mut y: [ByteTarget; L] = x.clone();
223    let mut cb_i = *icb;
224
225    let zero_byte = builder.zero_byte();
226    for (i, x_i_raw) in x.chunks(16).enumerate() {
227        if i > 0 {
228            cb_i = inc32_target(builder, cb_i);
229        }
230
231        let l = x_i_raw.len().min(16);
232        let mut x_i = [zero_byte; 16];
233        x_i[..l].copy_from_slice(x_i_raw);
234
235        let ciph_cb_i = builder
236            .encrypt_block::<NR>(sbox_lut_idx, mix_matrix, StateTarget::from_flat(cb_i), key)
237            .flatten();
238
239        let (y_i, n_bytes) = if i < n && x_i_raw.len() == 16 {
240            (xor_blocks(builder, x_i, ciph_cb_i), 16)
241        } else {
242            // last chunk, might be smaller than 16 bytes (L%16 bytes)
243            let msb_res = msb_t_target(builder, L % 16, ciph_cb_i.as_slice());
244            let l = msb_res.len().min(16);
245            let mut m: [ByteTarget; 16] = [zero_byte; 16];
246            m[..l].copy_from_slice(&msb_res);
247            (xor_blocks(builder, x_i, m), l)
248        };
249        y[i * 16..i * 16 + n_bytes].clone_from_slice(&y_i[..n_bytes]);
250    }
251    y
252}
253
254// fn ghash_target<const L: usize>(
255fn ghash_target(
256    builder: &mut CircuitBuilder<F, D>,
257    h: [ByteTarget; 16],
258    // x: &[ByteTarget; L],
259    x: Vec<ByteTarget>,
260) -> [ByteTarget; 16] {
261    // assert!(L.is_multiple_of(16)); // multiple of 128 bits
262    // let m = L / 16;
263    assert!(x.len().is_multiple_of(16)); // multiple of 128 bits
264    let m = x.len() / 16;
265
266    let zero_byte = builder.zero_byte();
267    let mut y = [zero_byte; 16]; // (128 bits)
268    for i in 0..m {
269        let mut xi = [zero_byte; 16];
270        xi.clone_from_slice(&x[i * 16..i * 16 + 16]);
271        let y_xi = xor_blocks(builder, y, xi);
272        y = gf_2_128_mul_target(builder, y_xi, h);
273    }
274    y
275}
276fn gf_2_128_mul_target(
277    builder: &mut CircuitBuilder<F, D>,
278    x: [ByteTarget; 16],
279    y: [ByteTarget; 16],
280) -> [ByteTarget; 16] {
281    let zero_byte = builder.zero_byte();
282    let one_bool = builder._true();
283
284    // R: 10000111 || 0^120 (in little-endian)
285    let mut r = [zero_byte; 16];
286    r[0][0] = one_bool;
287    r[0][5] = one_bool;
288    r[0][6] = one_bool;
289    r[0][7] = one_bool;
290
291    let mut z = [zero_byte; 16];
292    let mut v = y;
293    for i in 0..128 {
294        let byte_index = i / 8;
295        let bit_index = 7 - (i % 8);
296        let xi = x[byte_index][bit_index];
297
298        // set z = if xi==1: z^v, else: z
299        for b in 0..16 {
300            for k in 0..8 {
301                let z_xor_v = xor(builder, z[b][k], v[b][k]);
302                z[b][k] =
303                    BoolTarget::new_unsafe(builder.select(xi, z_xor_v.target, z[b][k].target));
304            }
305        }
306
307        let lsb = v[15][0].clone(); // (little-endian)
308        v = right_shift_one_target(builder, &v);
309
310        // if lsb==1: v=v^R, else: v
311        for b in 0..16 {
312            for k in 0..8 {
313                let v_xor_r = xor(builder, v[b][k], r[b][k]);
314                v[b][k] =
315                    BoolTarget::new_unsafe(builder.select(lsb, v_xor_r.target, v[b][k].target));
316            }
317        }
318    }
319    z
320}
321pub fn right_shift_one_target(
322    builder: &mut CircuitBuilder<F, D>,
323    v: &[ByteTarget; 16],
324) -> [ByteTarget; 16] {
325    let mut r: [ByteTarget; 16] = v.clone();
326
327    let mut carry = builder._false();
328    for i in 0..16 {
329        let current = v[i];
330
331        let next_carry = current[0];
332
333        let mut shifted = [builder._false(); 8];
334        for j in 0..7 {
335            shifted[j] = current[j + 1];
336        }
337        shifted[7] = carry;
338        r[i] = shifted;
339        carry = next_carry;
340    }
341    r
342}
343
344pub fn inc32_target(
345    builder: &mut CircuitBuilder<F, D>,
346    block: [ByteTarget; 16],
347) -> [ByteTarget; 16] {
348    let mut r = block;
349
350    let mut carry = BoolTarget::new_unsafe(builder.one());
351    for byte_index in (12..16).rev() {
352        for bit_index in 0..8 {
353            let a = block[byte_index][bit_index];
354            // builder.assert_bool(a); // TODO we can assume it's valid bool, skip check
355
356            let sum = xor(builder, a, carry);
357            let carry_out = builder.and(a, carry);
358            r[byte_index][bit_index] = sum;
359            carry = carry_out;
360        }
361    }
362    r
363}
364
365fn xor_byte(builder: &mut CircuitBuilder<F, D>, b1: ByteTarget, b2: ByteTarget) -> ByteTarget {
366    array::from_fn(|i| xor(builder, b1[i], b2[i]))
367}
368
369fn xor_blocks(
370    builder: &mut CircuitBuilder<F, D>,
371    b1: [ByteTarget; 16],
372    b2: [ByteTarget; 16],
373) -> [ByteTarget; 16] {
374    array::from_fn(|i| xor_byte(builder, b1[i], b2[i]))
375}
376
377pub fn msb_t_target(
378    builder: &mut CircuitBuilder<F, D>,
379    t_bytes: usize,
380    block: &[ByteTarget],
381) -> Vec<ByteTarget> {
382    let t = t_bytes * 8;
383
384    let full_bytes = t / 8;
385    let rem_bits = t % 8;
386
387    let mut out: Vec<ByteTarget> = Vec::new();
388    for i in 0..full_bytes {
389        out.push(block[i]);
390    }
391
392    let zero_bool = BoolTarget::new_unsafe(builder.zero());
393    if rem_bits != 0 {
394        let mut partial = [zero_bool; 8];
395
396        for bit in 0..8 {
397            if bit < rem_bits {
398                partial[bit] = block[full_bytes][bit];
399            } else {
400                partial[bit] = zero_bool;
401            }
402        }
403        out.push(partial);
404    }
405    out
406}
407
408#[cfg(test)]
409mod tests {
410    use std::array;
411
412    use anyhow::Result;
413    use plonky2::{
414        field::goldilocks_field::GoldilocksField as F,
415        iop::witness::PartialWitness,
416        plonk::{
417            circuit_builder::CircuitBuilder, circuit_data::CircuitConfig,
418            config::PoseidonGoldilocksConfig,
419        },
420    };
421
422    use super::*;
423    use crate::{
424        circuit_aes::PartialWitnessByteArray,
425        native_aes::key_expansion,
426        native_gcm::{gctr, gf_2_128_mul, ghash, right_shift_one},
427    };
428
429    use super::D;
430
431    #[test]
432    fn test_gctr() -> Result<()> {
433        // AES-GCM-128
434        test_gctr_opt::<4, 4, 10, 13>()?;
435        test_gctr_opt::<4, 4, 10, 17>()?;
436
437        // AES-GCM-256
438        test_gctr_opt::<8, 4, 14, 13>()?;
439
440        Ok(())
441    }
442    fn test_gctr_opt<const NK: usize, const NB: usize, const NR: usize, const L: usize>(
443    ) -> Result<()>
444    where
445        [(); 4 * (NR + 1)]:,
446        [(); NK * NB]:,
447    {
448        let key: &[u8; NK * NB] = &[42; NK * NB];
449        let expanded_key = key_expansion::<NK, NB, NR>(key);
450        let nonce: &[u8; 12] = &[111; 12];
451        let icb: &[u8; 16] = &[222; 16];
452        let pt: &[u8; L] = &[42u8; L];
453
454        let expected = gctr(expanded_key, &icb, pt);
455
456        // Circuit declaration
457        let config = CircuitConfig::standard_recursion_config();
458        let mut builder = CircuitBuilder::<F, D>::new(config);
459
460        let key_target: [ByteTarget; NK * NB] =
461            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
462        let nonce_target: [ByteTarget; 12] =
463            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
464        let icb_target: [ByteTarget; 16] =
465            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
466        let pt_target: [ByteTarget; L] =
467            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
468        let sbox_lut = sbox_lut(&mut builder);
469        let mix_matrix = state_mix_matrix_bits(&mut builder);
470
471        let key_expanded_target = builder.key_expansion::<NK, NB, NR>(sbox_lut, key_target);
472        let gctr_out_target = gctr_target::<NR, L>(
473            &mut builder,
474            sbox_lut,
475            mix_matrix,
476            key_expanded_target,
477            &icb_target,
478            &pt_target,
479        );
480
481        println!(
482            "gctr (NK:{}, NB:{}, NR:{}, L:{}) num_gates: {}",
483            NK,
484            NB,
485            NR,
486            L,
487            builder.num_gates()
488        );
489        let data = builder.build::<PoseidonGoldilocksConfig>();
490
491        // set values to circuit
492        let mut pw = PartialWitness::<F>::new();
493        std::iter::zip(key_target, key).try_for_each(|(t, v)| pw.set_byte_array_target(t, *v))?;
494        std::iter::zip(nonce_target, nonce)
495            .try_for_each(|(t, v)| pw.set_byte_array_target(t, *v))?;
496        std::iter::zip(icb_target, icb).try_for_each(|(t, v)| pw.set_byte_array_target(t, *v))?;
497        std::iter::zip(pt_target, pt).try_for_each(|(t, v)| pw.set_byte_array_target(t, *v))?;
498
499        std::iter::zip(gctr_out_target, expected)
500            .try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
501
502        let proof = data.prove(pw)?;
503        data.verify(proof)
504    }
505
506    #[test]
507    fn test_right_shift_one() -> Result<()> {
508        let x: [u8; 16] = [111; 16];
509
510        let mut expected = x.clone();
511        right_shift_one(&mut expected);
512
513        // Circuit declaration
514        let config = CircuitConfig::standard_recursion_config();
515        let mut builder = CircuitBuilder::<F, D>::new(config);
516
517        let x_target: [ByteTarget; 16] =
518            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
519
520        let out_target: [ByteTarget; 16] = right_shift_one_target(&mut builder, &x_target);
521
522        println!("right_shift_one num_gates: {}", builder.num_gates());
523        let data = builder.build::<PoseidonGoldilocksConfig>();
524
525        // set values to circuit
526        let mut pw = PartialWitness::<F>::new();
527        std::iter::zip(x_target, x).try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
528
529        std::iter::zip(out_target, expected)
530            .try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
531
532        let proof = data.prove(pw)?;
533        data.verify(proof)?;
534        Ok(())
535    }
536
537    #[test]
538    fn test_gf_mul() -> Result<()> {
539        // AES-GCM-128
540        test_gf_mul_opt::<4, 4, 10, 16>()?;
541        test_gf_mul_opt::<4, 4, 10, 32>()?;
542
543        // AES-GCM-256
544        test_gf_mul_opt::<8, 4, 14, 16>()?;
545
546        Ok(())
547    }
548    fn test_gf_mul_opt<const NK: usize, const NB: usize, const NR: usize, const L: usize>(
549    ) -> Result<()>
550    where
551        [(); 4 * (NR + 1)]:,
552        [(); NK * NB]:,
553    {
554        let x: [u8; 16] = [111; 16];
555        let y: [u8; 16] = [222; 16];
556
557        let expected = gf_2_128_mul(x, y);
558        let x_bits: [[bool; 8]; 16] =
559            array::from_fn(|b| crate::circuit_aes::le_bits_from_byte(x[b]));
560        let y_bits: [[bool; 8]; 16] =
561            array::from_fn(|b| crate::circuit_aes::le_bits_from_byte(y[b]));
562
563        // sanity check
564        let expected2 = crate::native_gcm::gf_2_128_mul_circuit_version(x_bits, y_bits);
565        let expected_bits: [[bool; 8]; 16] =
566            array::from_fn(|b| crate::circuit_aes::le_bits_from_byte(expected[b]));
567        assert_eq!(expected2, expected_bits);
568
569        // Circuit declaration
570        let config = CircuitConfig::standard_recursion_config();
571        let mut builder = CircuitBuilder::<F, D>::new(config);
572
573        let x_target: [ByteTarget; 16] =
574            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
575        let y_target: [ByteTarget; 16] =
576            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
577
578        let out_target = gf_2_128_mul_target(&mut builder, x_target, y_target);
579
580        println!("gf_2_128_mul (L:{}) num_gates: {}", L, builder.num_gates());
581        let data = builder.build::<PoseidonGoldilocksConfig>();
582
583        // set values to circuit
584        let mut pw = PartialWitness::<F>::new();
585        std::iter::zip(x_target, x).try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
586        std::iter::zip(y_target, y).try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
587
588        std::iter::zip(out_target, expected)
589            .try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
590
591        let proof = data.prove(pw)?;
592        data.verify(proof)
593    }
594
595    #[test]
596    fn test_ghash() -> Result<()> {
597        // AES-GCM-128
598        test_ghash_opt::<4, 4, 10, 16>()?;
599        test_ghash_opt::<4, 4, 10, 32>()?;
600
601        // AES-GCM-256
602        test_ghash_opt::<8, 4, 14, 16>()?;
603
604        Ok(())
605    }
606    fn test_ghash_opt<const NK: usize, const NB: usize, const NR: usize, const L: usize>(
607    ) -> Result<()>
608    where
609        [(); 4 * (NR + 1)]:,
610        [(); NK * NB]:,
611    {
612        let h: [u8; 16] = [222; 16];
613        let x: &[u8; L] = &[42u8; L];
614
615        let expected = ghash(h, x);
616
617        // Circuit declaration
618        let config = CircuitConfig::standard_recursion_config();
619        let mut builder = CircuitBuilder::<F, D>::new(config);
620
621        let h_target: [ByteTarget; 16] =
622            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
623        let x_target: [ByteTarget; L] =
624            array::from_fn(|_| array::from_fn(|_| builder.add_virtual_bool_target_safe()));
625
626        // let ghash_out_target = ghash_target::<L>(&mut builder, h_target, &x_target);
627        let ghash_out_target = ghash_target(&mut builder, h_target, x_target.to_vec());
628
629        println!("ghash (L:{}) num_gates: {}", L, builder.num_gates());
630        let data = builder.build::<PoseidonGoldilocksConfig>();
631
632        // set values to circuit
633        let mut pw = PartialWitness::<F>::new();
634        std::iter::zip(h_target, h).try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
635        std::iter::zip(x_target, x).try_for_each(|(t, v)| pw.set_byte_array_target(t, *v))?;
636
637        std::iter::zip(ghash_out_target, expected)
638            .try_for_each(|(t, v)| pw.set_byte_array_target(t, v))?;
639
640        let proof = data.prove(pw)?;
641        data.verify(proof)
642    }
643
644    #[test]
645    fn test_encrypt() -> Result<()> {
646        // AES-GCM-128
647        test_encrypt_opt::<4, 4, 10, 13, false>(false)?;
648        test_encrypt_opt::<4, 4, 10, 13, true>(false)?; // with tag
649        test_encrypt_opt::<4, 4, 10, 17, false>(false)?;
650
651        // AES-GCM-256
652        test_encrypt_opt::<8, 4, 14, 13, false>(false)?;
653
654        Ok(())
655    }
656    // run it with: cargo test -- --ignored
657    #[ignore]
658    #[test]
659    fn test_encrypt_report_sizes() -> Result<()> {
660        // AES-GCM-128
661        test_encrypt_opt::<4, 4, 10, 16, false>(true)?;
662        test_encrypt_opt::<4, 4, 10, 17, false>(true)?;
663        test_encrypt_opt::<4, 4, 10, 32, false>(true)?;
664        test_encrypt_opt::<4, 4, 10, 33, false>(true)?;
665        test_encrypt_opt::<4, 4, 10, 64, false>(true)?;
666        test_encrypt_opt::<4, 4, 10, 128, false>(true)?;
667        test_encrypt_opt::<4, 4, 10, 256, false>(true)?;
668        test_encrypt_opt::<4, 4, 10, 512, false>(true)?;
669        test_encrypt_opt::<4, 4, 10, 1024, false>(true)?;
670        test_encrypt_opt::<4, 4, 10, 2048, false>(true)?;
671
672        // AES-GCM-256
673        test_encrypt_opt::<8, 4, 14, 16, false>(true)?;
674        test_encrypt_opt::<8, 4, 14, 17, false>(true)?;
675        test_encrypt_opt::<8, 4, 14, 32, false>(true)?;
676        test_encrypt_opt::<8, 4, 14, 33, false>(true)?;
677        test_encrypt_opt::<8, 4, 14, 64, false>(true)?;
678        test_encrypt_opt::<8, 4, 14, 128, false>(true)?;
679        test_encrypt_opt::<8, 4, 14, 256, false>(true)?;
680        test_encrypt_opt::<8, 4, 14, 512, false>(true)?;
681        test_encrypt_opt::<8, 4, 14, 1024, false>(true)?;
682        test_encrypt_opt::<8, 4, 14, 2048, false>(true)?;
683
684        Ok(())
685    }
686    fn test_encrypt_opt<
687        const NK: usize,
688        const NB: usize,
689        const NR: usize,
690        const L: usize,
691        const TAG: bool,
692    >(
693        only_build: bool,
694    ) -> Result<()>
695    where
696        [(); 4 * (NR + 1)]:,
697        [(); NK * NB]:,
698    {
699        let key: &[u8; NK * NB] = &[42; NK * NB];
700        let nonce: &[u8; 12] = &[111; 12];
701        let pt: &[u8; L] = &[42u8; L];
702
703        let (ct, tag) = crate::native_gcm::encrypt::<NK, NB, NR>(key, nonce, pt);
704
705        // Circuit declaration
706        let config = CircuitConfig::standard_recursion_config();
707        let mut builder = CircuitBuilder::<F, D>::new(config);
708
709        let aes_targets = AesGcmTarget::<NK, NB, NR, L, TAG>::new_virtual(&mut builder);
710        aes_targets.build_circuit(&mut builder);
711
712        println!(
713            "encrypt (NK:{}, NB:{}, NR:{}, L:{}, TAG:{}) num_gates: {}",
714            NK,
715            NB,
716            NR,
717            L,
718            TAG,
719            builder.num_gates()
720        );
721        let data = builder.build::<PoseidonGoldilocksConfig>();
722
723        if only_build {
724            return Ok(());
725        }
726
727        // set values to circuit
728        let mut pw = PartialWitness::<F>::new();
729        aes_targets.set_targets(&mut pw, key, nonce, pt, &ct, &tag)?;
730
731        let proof = data.prove(pw)?;
732        data.verify(proof)
733    }
734}