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