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}