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