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