testudo/src/nizk/mod.rs
src/nizk/mod.rsRaw
1#![allow(clippy::too_many_arguments)]
2use super::commitments::{Commitments, MultiCommitGens};
3use super::errors::ProofVerifyError;
4use super::group::CompressedGroup;
5use super::math::Math;
6use super::random::RandomTape;
7use super::scalar::Scalar;
8use super::transcript::{AppendToTranscript, ProofTranscript};
9use merlin::Transcript;
10use serde::{Deserialize, Serialize};
11
12mod bullet;
13use bullet::BulletReductionProof;
14
15#[derive(Serialize, Deserialize, Debug)]
16pub struct KnowledgeProof {
17 alpha: CompressedGroup,
18 z1: Scalar,
19 z2: Scalar,
20}
21
22impl KnowledgeProof {
23 fn protocol_name() -> &'static [u8] {
24 b"knowledge proof"
25 }
26
27 pub fn prove(
28 gens_n: &MultiCommitGens,
29 transcript: &mut Transcript,
30 random_tape: &mut RandomTape,
31 x: &Scalar,
32 r: &Scalar,
33 ) -> (KnowledgeProof, CompressedGroup) {
34 transcript.append_protocol_name(KnowledgeProof::protocol_name());
35
36 // produce two random Scalars
37 let t1 = random_tape.random_scalar(b"t1");
38 let t2 = random_tape.random_scalar(b"t2");
39
40 let C = x.commit(&r, gens_n).compress();
41 C.append_to_transcript(b"C", transcript);
42
43 let alpha = t1.commit(&t2, gens_n).compress();
44 alpha.append_to_transcript(b"alpha", transcript);
45
46 let c = transcript.challenge_scalar(b"c");
47
48 let z1 = x * c + t1;
49 let z2 = r * c + t2;
50
51 (KnowledgeProof { alpha, z1, z2 }, C)
52 }
53
54 pub fn verify(
55 &self,
56 gens_n: &MultiCommitGens,
57 transcript: &mut Transcript,
58 C: &CompressedGroup,
59 ) -> Result<(), ProofVerifyError> {
60 transcript.append_protocol_name(KnowledgeProof::protocol_name());
61 C.append_to_transcript(b"C", transcript);
62 self.alpha.append_to_transcript(b"alpha", transcript);
63
64 let c = transcript.challenge_scalar(b"c");
65
66 let lhs = self.z1.commit(&self.z2, gens_n).compress();
67 let rhs = (c * C.decompress().expect("Could not decompress C")
68 + self
69 .alpha
70 .decompress()
71 .expect("Could not decompress self.alpha"))
72 .compress();
73
74 if lhs == rhs {
75 Ok(())
76 } else {
77 Err(ProofVerifyError)
78 }
79 }
80}
81
82#[derive(Serialize, Deserialize, Debug)]
83pub struct EqualityProof {
84 alpha: CompressedGroup,
85 z: Scalar,
86}
87
88impl EqualityProof {
89 fn protocol_name() -> &'static [u8] {
90 b"equality proof"
91 }
92
93 pub fn prove(
94 gens_n: &MultiCommitGens,
95 transcript: &mut Transcript,
96 random_tape: &mut RandomTape,
97 v1: &Scalar,
98 s1: &Scalar,
99 v2: &Scalar,
100 s2: &Scalar,
101 ) -> (EqualityProof, CompressedGroup, CompressedGroup) {
102 transcript.append_protocol_name(EqualityProof::protocol_name());
103
104 // produce a random Scalar
105 let r = random_tape.random_scalar(b"r");
106
107 let C1 = v1.commit(&s1, gens_n).compress();
108 C1.append_to_transcript(b"C1", transcript);
109
110 let C2 = v2.commit(&s2, gens_n).compress();
111 C2.append_to_transcript(b"C2", transcript);
112
113 let alpha = (r * gens_n.h).compress();
114 alpha.append_to_transcript(b"alpha", transcript);
115
116 let c = transcript.challenge_scalar(b"c");
117
118 let z = c * (s1 - s2) + r;
119
120 (EqualityProof { alpha, z }, C1, C2)
121 }
122
123 pub fn verify(
124 &self,
125 gens_n: &MultiCommitGens,
126 transcript: &mut Transcript,
127 C1: &CompressedGroup,
128 C2: &CompressedGroup,
129 ) -> Result<(), ProofVerifyError> {
130 transcript.append_protocol_name(EqualityProof::protocol_name());
131 C1.append_to_transcript(b"C1", transcript);
132 C2.append_to_transcript(b"C2", transcript);
133 self.alpha.append_to_transcript(b"alpha", transcript);
134
135 let c = transcript.challenge_scalar(b"c");
136 let rhs = {
137 let C = C1.decompress().unwrap() - C2.decompress().unwrap();
138 (c * C + self.alpha.decompress().unwrap()).compress()
139 };
140
141 let lhs = (self.z * gens_n.h).compress();
142
143 if lhs == rhs {
144 Ok(())
145 } else {
146 Err(ProofVerifyError)
147 }
148 }
149}
150
151#[derive(Serialize, Deserialize, Debug)]
152pub struct ProductProof {
153 alpha: CompressedGroup,
154 beta: CompressedGroup,
155 delta: CompressedGroup,
156 z: [Scalar; 5],
157}
158
159impl ProductProof {
160 fn protocol_name() -> &'static [u8] {
161 b"product proof"
162 }
163
164 pub fn prove(
165 gens_n: &MultiCommitGens,
166 transcript: &mut Transcript,
167 random_tape: &mut RandomTape,
168 x: &Scalar,
169 rX: &Scalar,
170 y: &Scalar,
171 rY: &Scalar,
172 z: &Scalar,
173 rZ: &Scalar,
174 ) -> (
175 ProductProof,
176 CompressedGroup,
177 CompressedGroup,
178 CompressedGroup,
179 ) {
180 transcript.append_protocol_name(ProductProof::protocol_name());
181
182 // produce five random Scalar
183 let b1 = random_tape.random_scalar(b"b1");
184 let b2 = random_tape.random_scalar(b"b2");
185 let b3 = random_tape.random_scalar(b"b3");
186 let b4 = random_tape.random_scalar(b"b4");
187 let b5 = random_tape.random_scalar(b"b5");
188
189 let X = x.commit(&rX, gens_n).compress();
190 X.append_to_transcript(b"X", transcript);
191
192 let Y = y.commit(&rY, gens_n).compress();
193 Y.append_to_transcript(b"Y", transcript);
194
195 let Z = z.commit(&rZ, gens_n).compress();
196 Z.append_to_transcript(b"Z", transcript);
197
198 let alpha = b1.commit(&b2, gens_n).compress();
199 alpha.append_to_transcript(b"alpha", transcript);
200
201 let beta = b3.commit(&b4, gens_n).compress();
202 beta.append_to_transcript(b"beta", transcript);
203
204 let delta = {
205 let gens_X = &MultiCommitGens {
206 n: 1,
207 G: vec![X.decompress().unwrap()],
208 h: gens_n.h,
209 };
210 b3.commit(&b5, gens_X).compress()
211 };
212 delta.append_to_transcript(b"delta", transcript);
213
214 let c = transcript.challenge_scalar(b"c");
215
216 let z1 = b1 + c * x;
217 let z2 = b2 + c * rX;
218 let z3 = b3 + c * y;
219 let z4 = b4 + c * rY;
220 let z5 = b5 + c * (rZ - rX * y);
221 let z = [z1, z2, z3, z4, z5];
222
223 (
224 ProductProof {
225 alpha,
226 beta,
227 delta,
228 z,
229 },
230 X,
231 Y,
232 Z,
233 )
234 }
235
236 fn check_equality(
237 P: &CompressedGroup,
238 X: &CompressedGroup,
239 c: &Scalar,
240 gens_n: &MultiCommitGens,
241 z1: &Scalar,
242 z2: &Scalar,
243 ) -> bool {
244 let lhs = (P.decompress().unwrap() + c * X.decompress().unwrap()).compress();
245 let rhs = z1.commit(&z2, gens_n).compress();
246
247 lhs == rhs
248 }
249
250 pub fn verify(
251 &self,
252 gens_n: &MultiCommitGens,
253 transcript: &mut Transcript,
254 X: &CompressedGroup,
255 Y: &CompressedGroup,
256 Z: &CompressedGroup,
257 ) -> Result<(), ProofVerifyError> {
258 transcript.append_protocol_name(ProductProof::protocol_name());
259
260 X.append_to_transcript(b"X", transcript);
261 Y.append_to_transcript(b"Y", transcript);
262 Z.append_to_transcript(b"Z", transcript);
263 self.alpha.append_to_transcript(b"alpha", transcript);
264 self.beta.append_to_transcript(b"beta", transcript);
265 self.delta.append_to_transcript(b"delta", transcript);
266
267 let z1 = self.z[0];
268 let z2 = self.z[1];
269 let z3 = self.z[2];
270 let z4 = self.z[3];
271 let z5 = self.z[4];
272
273 let c = transcript.challenge_scalar(b"c");
274
275 if ProductProof::check_equality(&self.alpha, &X, &c, &gens_n, &z1, &z2)
276 && ProductProof::check_equality(&self.beta, &Y, &c, &gens_n, &z3, &z4)
277 && ProductProof::check_equality(
278 &self.delta,
279 &Z,
280 &c,
281 &MultiCommitGens {
282 n: 1,
283 G: vec![X.decompress().unwrap()],
284 h: gens_n.h,
285 },
286 &z3,
287 &z5,
288 )
289 {
290 Ok(())
291 } else {
292 Err(ProofVerifyError)
293 }
294 }
295}
296
297#[derive(Debug, Serialize, Deserialize)]
298pub struct DotProductProof {
299 delta: CompressedGroup,
300 beta: CompressedGroup,
301 z: Vec<Scalar>,
302 z_delta: Scalar,
303 z_beta: Scalar,
304}
305
306impl DotProductProof {
307 fn protocol_name() -> &'static [u8] {
308 b"dot product proof"
309 }
310
311 pub fn compute_dotproduct(a: &[Scalar], b: &[Scalar]) -> Scalar {
312 assert_eq!(a.len(), b.len());
313 (0..a.len()).map(|i| a[i] * b[i]).sum()
314 }
315
316 pub fn prove(
317 gens_1: &MultiCommitGens,
318 gens_n: &MultiCommitGens,
319 transcript: &mut Transcript,
320 random_tape: &mut RandomTape,
321 x_vec: &[Scalar],
322 blind_x: &Scalar,
323 a_vec: &[Scalar],
324 y: &Scalar,
325 blind_y: &Scalar,
326 ) -> (DotProductProof, CompressedGroup, CompressedGroup) {
327 transcript.append_protocol_name(DotProductProof::protocol_name());
328
329 let n = x_vec.len();
330 assert_eq!(x_vec.len(), a_vec.len());
331 assert_eq!(gens_n.n, a_vec.len());
332 assert_eq!(gens_1.n, 1);
333
334 // produce randomness for the proofs
335 let d_vec = random_tape.random_vector(b"d_vec", n);
336 let r_delta = random_tape.random_scalar(b"r_delta");
337 let r_beta = random_tape.random_scalar(b"r_beta");
338
339 let Cx = x_vec.commit(&blind_x, gens_n).compress();
340 Cx.append_to_transcript(b"Cx", transcript);
341
342 let Cy = y.commit(&blind_y, gens_1).compress();
343 Cy.append_to_transcript(b"Cy", transcript);
344
345 let delta = d_vec.commit(&r_delta, gens_n).compress();
346 delta.append_to_transcript(b"delta", transcript);
347
348 let dotproduct_a_d = DotProductProof::compute_dotproduct(&a_vec, &d_vec);
349
350 let beta = dotproduct_a_d.commit(&r_beta, gens_1).compress();
351 beta.append_to_transcript(b"beta", transcript);
352
353 let c = transcript.challenge_scalar(b"c");
354
355 let z = (0..d_vec.len())
356 .map(|i| c * x_vec[i] + d_vec[i])
357 .collect::<Vec<Scalar>>();
358
359 let z_delta = c * blind_x + r_delta;
360 let z_beta = c * blind_y + r_beta;
361
362 (
363 DotProductProof {
364 delta,
365 beta,
366 z,
367 z_delta,
368 z_beta,
369 },
370 Cx,
371 Cy,
372 )
373 }
374
375 pub fn verify(
376 &self,
377 gens_1: &MultiCommitGens,
378 gens_n: &MultiCommitGens,
379 transcript: &mut Transcript,
380 a: &[Scalar],
381 Cx: &CompressedGroup,
382 Cy: &CompressedGroup,
383 ) -> Result<(), ProofVerifyError> {
384 assert_eq!(gens_n.n, a.len());
385 assert_eq!(gens_1.n, 1);
386
387 transcript.append_protocol_name(DotProductProof::protocol_name());
388 Cx.append_to_transcript(b"Cx", transcript);
389 Cy.append_to_transcript(b"Cy", transcript);
390 self.delta.append_to_transcript(b"delta", transcript);
391 self.beta.append_to_transcript(b"beta", transcript);
392
393 let c = transcript.challenge_scalar(b"c");
394
395 let mut result = c * Cx.decompress().unwrap() + self.delta.decompress().unwrap()
396 == self.z.commit(&self.z_delta, gens_n);
397
398 let dotproduct_z_a = DotProductProof::compute_dotproduct(&self.z, &a);
399 result &= c * Cy.decompress().unwrap() + self.beta.decompress().unwrap()
400 == dotproduct_z_a.commit(&self.z_beta, gens_1);
401
402 if result {
403 Ok(())
404 } else {
405 Err(ProofVerifyError)
406 }
407 }
408}
409
410pub struct DotProductProofGens {
411 n: usize,
412 pub gens_n: MultiCommitGens,
413 pub gens_1: MultiCommitGens,
414}
415
416impl DotProductProofGens {
417 pub fn new(n: usize, label: &[u8]) -> Self {
418 let (gens_n, gens_1) = MultiCommitGens::new(n + 1, label).split_at_mut(n);
419 DotProductProofGens { n, gens_n, gens_1 }
420 }
421}
422
423#[derive(Debug, Serialize, Deserialize)]
424pub struct DotProductProofLog {
425 bullet_reduction_proof: BulletReductionProof,
426 delta: CompressedGroup,
427 beta: CompressedGroup,
428 z1: Scalar,
429 z2: Scalar,
430}
431
432impl DotProductProofLog {
433 fn protocol_name() -> &'static [u8] {
434 b"dot product proof (log)"
435 }
436
437 pub fn compute_dotproduct(a: &[Scalar], b: &[Scalar]) -> Scalar {
438 assert_eq!(a.len(), b.len());
439 (0..a.len()).map(|i| a[i] * b[i]).sum()
440 }
441
442 pub fn prove(
443 gens: &DotProductProofGens,
444 transcript: &mut Transcript,
445 random_tape: &mut RandomTape,
446 x_vec: &[Scalar],
447 blind_x: &Scalar,
448 a_vec: &[Scalar],
449 y: &Scalar,
450 blind_y: &Scalar,
451 ) -> (DotProductProofLog, CompressedGroup, CompressedGroup) {
452 transcript.append_protocol_name(DotProductProofLog::protocol_name());
453
454 let n = x_vec.len();
455 assert_eq!(x_vec.len(), a_vec.len());
456 assert_eq!(gens.n, n);
457
458 // produce randomness for generating a proof
459 let d = random_tape.random_scalar(b"d");
460 let r_delta = random_tape.random_scalar(b"r_delta");
461 let r_beta = random_tape.random_scalar(b"r_delta");
462 let blinds_vec = {
463 let v1 = random_tape.random_vector(b"blinds_vec_1", 2 * n.log2());
464 let v2 = random_tape.random_vector(b"blinds_vec_2", 2 * n.log2());
465 (0..v1.len())
466 .map(|i| (v1[i], v2[i]))
467 .collect::<Vec<(Scalar, Scalar)>>()
468 };
469
470 let Cx = x_vec.commit(&blind_x, &gens.gens_n).compress();
471 Cx.append_to_transcript(b"Cx", transcript);
472
473 let Cy = y.commit(&blind_y, &gens.gens_1).compress();
474 Cy.append_to_transcript(b"Cy", transcript);
475
476 let blind_Gamma = blind_x + blind_y;
477 let (bullet_reduction_proof, _Gamma_hat, x_hat, a_hat, g_hat, rhat_Gamma) =
478 BulletReductionProof::prove(
479 transcript,
480 &gens.gens_1.G[0],
481 &gens.gens_n.G,
482 &gens.gens_n.h,
483 x_vec,
484 a_vec,
485 &blind_Gamma,
486 &blinds_vec,
487 );
488 let y_hat = x_hat * a_hat;
489
490 let delta = {
491 let gens_hat = MultiCommitGens {
492 n: 1,
493 G: vec![g_hat],
494 h: gens.gens_1.h,
495 };
496 d.commit(&r_delta, &gens_hat).compress()
497 };
498 delta.append_to_transcript(b"delta", transcript);
499
500 let beta = d.commit(&r_beta, &gens.gens_1).compress();
501 beta.append_to_transcript(b"beta", transcript);
502
503 let c = transcript.challenge_scalar(b"c");
504
505 let z1 = d + c * y_hat;
506 let z2 = a_hat * (c * rhat_Gamma + r_beta) + r_delta;
507
508 (
509 DotProductProofLog {
510 bullet_reduction_proof,
511 delta,
512 beta,
513 z1,
514 z2,
515 },
516 Cx,
517 Cy,
518 )
519 }
520
521 pub fn verify(
522 &self,
523 n: usize,
524 gens: &DotProductProofGens,
525 transcript: &mut Transcript,
526 a: &[Scalar],
527 Cx: &CompressedGroup,
528 Cy: &CompressedGroup,
529 ) -> Result<(), ProofVerifyError> {
530 assert_eq!(gens.n, n);
531 assert_eq!(a.len(), n);
532
533 transcript.append_protocol_name(DotProductProofLog::protocol_name());
534 Cx.append_to_transcript(b"Cx", transcript);
535 Cy.append_to_transcript(b"Cy", transcript);
536
537 let Gamma = Cx.decompress().unwrap() + Cy.decompress().unwrap();
538
539 let (g_hat, Gamma_hat, a_hat) = self
540 .bullet_reduction_proof
541 .verify(n, a, transcript, &Gamma, &gens.gens_n.G)
542 .unwrap();
543
544 self.delta.append_to_transcript(b"delta", transcript);
545 self.beta.append_to_transcript(b"beta", transcript);
546
547 let c = transcript.challenge_scalar(b"c");
548
549 let c_s = &c;
550 let beta_s = self.beta.decompress().unwrap();
551 let a_hat_s = &a_hat;
552 let delta_s = self.delta.decompress().unwrap();
553 let z1_s = &self.z1;
554 let z2_s = &self.z2;
555
556 let lhs = ((Gamma_hat * c_s + beta_s) * a_hat_s + delta_s).compress();
557 let rhs = ((g_hat + gens.gens_1.G[0] * a_hat_s) * z1_s + gens.gens_1.h * z2_s).compress();
558
559 assert_eq!(lhs, rhs);
560
561 if lhs == rhs {
562 Ok(())
563 } else {
564 Err(ProofVerifyError)
565 }
566 }
567}
568
569#[cfg(test)]
570mod tests {
571 use super::*;
572 use rand::rngs::OsRng;
573 #[test]
574 fn check_knowledgeproof() {
575 let mut csprng: OsRng = OsRng;
576
577 let gens_1 = MultiCommitGens::new(1, b"test-knowledgeproof");
578
579 let x = Scalar::random(&mut csprng);
580 let r = Scalar::random(&mut csprng);
581
582 let mut random_tape = RandomTape::new(b"proof");
583 let mut prover_transcript = Transcript::new(b"example");
584 let (proof, committed_value) =
585 KnowledgeProof::prove(&gens_1, &mut prover_transcript, &mut random_tape, &x, &r);
586
587 let mut verifier_transcript = Transcript::new(b"example");
588 assert!(proof
589 .verify(&gens_1, &mut verifier_transcript, &committed_value)
590 .is_ok());
591 }
592
593 #[test]
594 fn check_equalityproof() {
595 let mut csprng: OsRng = OsRng;
596
597 let gens_1 = MultiCommitGens::new(1, b"test-equalityproof");
598 let v1 = Scalar::random(&mut csprng);
599 let v2 = v1;
600 let s1 = Scalar::random(&mut csprng);
601 let s2 = Scalar::random(&mut csprng);
602
603 let mut random_tape = RandomTape::new(b"proof");
604 let mut prover_transcript = Transcript::new(b"example");
605 let (proof, C1, C2) = EqualityProof::prove(
606 &gens_1,
607 &mut prover_transcript,
608 &mut random_tape,
609 &v1,
610 &s1,
611 &v2,
612 &s2,
613 );
614
615 let mut verifier_transcript = Transcript::new(b"example");
616 assert!(proof
617 .verify(&gens_1, &mut verifier_transcript, &C1, &C2)
618 .is_ok());
619 }
620
621 #[test]
622 fn check_productproof() {
623 let mut csprng: OsRng = OsRng;
624
625 let gens_1 = MultiCommitGens::new(1, b"test-productproof");
626 let x = Scalar::random(&mut csprng);
627 let rX = Scalar::random(&mut csprng);
628 let y = Scalar::random(&mut csprng);
629 let rY = Scalar::random(&mut csprng);
630 let z = x * y;
631 let rZ = Scalar::random(&mut csprng);
632
633 let mut random_tape = RandomTape::new(b"proof");
634 let mut prover_transcript = Transcript::new(b"example");
635 let (proof, X, Y, Z) = ProductProof::prove(
636 &gens_1,
637 &mut prover_transcript,
638 &mut random_tape,
639 &x,
640 &rX,
641 &y,
642 &rY,
643 &z,
644 &rZ,
645 );
646
647 let mut verifier_transcript = Transcript::new(b"example");
648 assert!(proof
649 .verify(&gens_1, &mut verifier_transcript, &X, &Y, &Z)
650 .is_ok());
651 }
652
653 #[test]
654 fn check_dotproductproof() {
655 let mut csprng: OsRng = OsRng;
656
657 let n = 1024;
658
659 let gens_1 = MultiCommitGens::new(1, b"test-two");
660 let gens_1024 = MultiCommitGens::new(n, b"test-1024");
661
662 let mut x: Vec<Scalar> = Vec::new();
663 let mut a: Vec<Scalar> = Vec::new();
664 for _ in 0..n {
665 x.push(Scalar::random(&mut csprng));
666 a.push(Scalar::random(&mut csprng));
667 }
668 let y = DotProductProofLog::compute_dotproduct(&x, &a);
669 let r_x = Scalar::random(&mut csprng);
670 let r_y = Scalar::random(&mut csprng);
671
672 let mut random_tape = RandomTape::new(b"proof");
673 let mut prover_transcript = Transcript::new(b"example");
674 let (proof, Cx, Cy) = DotProductProof::prove(
675 &gens_1,
676 &gens_1024,
677 &mut prover_transcript,
678 &mut random_tape,
679 &x,
680 &r_x,
681 &a,
682 &y,
683 &r_y,
684 );
685
686 let mut verifier_transcript = Transcript::new(b"example");
687 assert!(proof
688 .verify(&gens_1, &gens_1024, &mut verifier_transcript, &a, &Cx, &Cy)
689 .is_ok());
690 }
691
692 #[test]
693 fn check_dotproductproof_log() {
694 let mut csprng: OsRng = OsRng;
695
696 let n = 1024;
697
698 let gens = DotProductProofGens::new(n, b"test-1024");
699
700 let x: Vec<Scalar> = (0..n).map(|_i| Scalar::random(&mut csprng)).collect();
701 let a: Vec<Scalar> = (0..n).map(|_i| Scalar::random(&mut csprng)).collect();
702 let y = DotProductProof::compute_dotproduct(&x, &a);
703
704 let r_x = Scalar::random(&mut csprng);
705 let r_y = Scalar::random(&mut csprng);
706
707 let mut random_tape = RandomTape::new(b"proof");
708 let mut prover_transcript = Transcript::new(b"example");
709 let (proof, Cx, Cy) = DotProductProofLog::prove(
710 &gens,
711 &mut prover_transcript,
712 &mut random_tape,
713 &x,
714 &r_x,
715 &a,
716 &y,
717 &r_y,
718 );
719
720 let mut verifier_transcript = Transcript::new(b"example");
721 assert!(proof
722 .verify(n, &gens, &mut verifier_transcript, &a, &Cx, &Cy)
723 .is_ok());
724 }
725}