1use elliptic_curve::ops::Reduce;
37use elliptic_curve::FieldBytes;
38use rand::{Rng, RngExt};
39use rustcrypto_ff::Field;
40use rustcrypto_group::prime::PrimeCurveAffine;
41use rustcrypto_group::Curve as GroupCurve;
42use std::collections::HashSet;
43use std::marker::PhantomData;
44
45use crate::curve::DklsCurve;
46use crate::utilities::hashes::{
47 point_to_bytes, scalar_to_bytes, tagged_hash, tagged_hash_as_scalar, HashOutput,
48};
49use std::fmt;
50
51use crate::utilities::oracle_tags::{
52 TAG_DLOG_PROOF_COMMITMENT, TAG_DLOG_PROOF_FISCHLIN, TAG_ENCPROOF_FS,
53};
54use crate::utilities::rng;
55use subtle::ConstantTimeEq;
56
57#[derive(Debug, Clone)]
60pub struct ProofSearchExhausted;
61
62impl fmt::Display for ProofSearchExhausted {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 write!(
65 f,
66 "Fischlin proof-of-work search exhausted without finding all required hash collisions"
67 )
68 }
69}
70
71impl std::error::Error for ProofSearchExhausted {}
72
73pub const R: u16 = 64;
75pub const L: u16 = 4;
76pub const T: u16 = 32;
77
78#[derive(Debug, Clone)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83#[cfg_attr(
84 feature = "serde",
85 serde(bound(
86 serialize = "C::Scalar: serde::Serialize",
87 deserialize = "C::Scalar: serde::Deserialize<'de>"
88 ))
89)]
90pub struct InteractiveDLogProof<C: DklsCurve> {
91 pub challenge: Vec<u8>,
92 pub challenge_response: C::Scalar,
93 #[cfg_attr(feature = "serde", serde(skip))]
94 _curve: PhantomData<C>,
95}
96
97fn challenge_to_scalar<C: DklsCurve>(challenge: &[u8]) -> C::Scalar {
100 let mut extended = vec![0u8; 32 - challenge.len()];
101 extended.extend_from_slice(challenge);
102 let field_bytes: &FieldBytes<C> = extended
104 .as_slice()
105 .try_into()
106 .expect("extended challenge length matches field size");
107 <C::Scalar as Reduce<FieldBytes<C>>>::reduce(field_bytes)
108}
109
110impl<C: DklsCurve> InteractiveDLogProof<C> {
111 #[must_use]
115 pub fn prove_step1(mut rng: impl Rng) -> (C::Scalar, C::AffinePoint) {
116 let mut scalar_rand_commitment = <C::Scalar as Field>::ZERO;
118 while scalar_rand_commitment == <C::Scalar as Field>::ZERO {
119 scalar_rand_commitment = <C::Scalar as Field>::random(&mut rng);
120 }
121
122 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
123 let point_rand_commitment = (generator * scalar_rand_commitment).to_affine();
124
125 (scalar_rand_commitment, point_rand_commitment)
126 }
127
128 #[must_use]
133 pub fn prove_step2(
134 scalar: &C::Scalar,
135 scalar_rand_commitment: &C::Scalar,
136 challenge: &[u8],
137 ) -> InteractiveDLogProof<C> {
138 let challenge_scalar = challenge_to_scalar::<C>(challenge);
142
143 let challenge_response = *scalar_rand_commitment - (challenge_scalar * scalar);
145
146 InteractiveDLogProof {
147 challenge: challenge.to_vec(), challenge_response,
149 _curve: PhantomData,
150 }
151 }
152
153 #[must_use]
163 pub fn verify(&self, point: &C::AffinePoint, point_rand_commitment: &C::AffinePoint) -> bool {
164 if self.challenge.is_empty() || self.challenge.len() > (T / 8) as usize {
167 return false;
168 }
169
170 let challenge_scalar = challenge_to_scalar::<C>(&self.challenge);
174
175 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
176
177 let point_verify =
179 ((generator * self.challenge_response) + (*point * challenge_scalar)).to_affine();
180
181 point_verify == *point_rand_commitment
182 }
183}
184
185#[derive(Debug, Clone)]
203#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
204#[cfg_attr(
205 feature = "serde",
206 serde(bound(
207 serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
208 deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
209 ))
210)]
211pub struct DLogProof<C: DklsCurve> {
212 pub point: C::AffinePoint,
213 pub rand_commitments: Vec<C::AffinePoint>,
214 pub proofs: Vec<InteractiveDLogProof<C>>,
215}
216
217impl<C: DklsCurve> DLogProof<C> {
218 pub fn prove(
224 scalar: &C::Scalar,
225 session_id: &[u8],
226 ) -> Result<DLogProof<C>, ProofSearchExhausted> {
227 let mut rand_commitments: Vec<C::AffinePoint> = Vec::with_capacity(R as usize);
229 let mut states: Vec<C::Scalar> = Vec::with_capacity(R as usize);
230 let mut rng = rng::get_rng();
231 for _ in 0..R {
232 let (state, rand_commitment) = InteractiveDLogProof::<C>::prove_step1(&mut rng);
233
234 rand_commitments.push(rand_commitment);
235 states.push(state);
236 }
237
238 let rc_as_bytes = rand_commitments
240 .clone()
241 .into_iter()
242 .map(|x| point_to_bytes::<C>(&x))
243 .collect::<Vec<Vec<u8>>>()
244 .concat();
245
246 let mut first_proofs: Vec<InteractiveDLogProof<C>> = Vec::with_capacity((R / 2) as usize);
249 let mut last_proofs: Vec<InteractiveDLogProof<C>> = Vec::with_capacity((R / 2) as usize);
250 for i in 0..(R / 2) {
251 let mut flag = false;
259 let mut first_counter = 0u16;
260 while first_counter < u16::MAX && !flag {
261 let first_challenge = rng::get_rng().random::<[u8; (T / 8) as usize]>();
263
264 let first_proof = InteractiveDLogProof::<C>::prove_step2(
278 scalar,
279 &states[i as usize],
280 &first_challenge,
281 );
282
283 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
285 let first_msg = [
286 &point_to_bytes::<C>(&generator),
287 &rc_as_bytes[..],
288 &i.to_be_bytes(),
289 &first_challenge,
290 &scalar_to_bytes::<C>(&first_proof.challenge_response),
291 ]
292 .concat();
293 let first_hash = &tagged_hash(TAG_DLOG_PROOF_FISCHLIN, &[session_id, &first_msg])
295 [0..(L / 4) as usize];
296
297 let mut second_counter = 0u16;
299 let mut rng = rng::get_rng();
300 while second_counter < u16::MAX {
301 let second_challenge = rng.random::<[u8; (T / 8) as usize]>();
303
304 let second_proof = InteractiveDLogProof::<C>::prove_step2(
308 scalar,
309 &states[(i + (R / 2)) as usize],
310 &second_challenge,
311 );
312
313 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
315 let second_msg = [
316 &point_to_bytes::<C>(&generator),
317 &rc_as_bytes[..],
318 &(i + (R / 2)).to_be_bytes(),
319 &second_challenge,
320 &scalar_to_bytes::<C>(&second_proof.challenge_response),
321 ]
322 .concat();
323 let second_hash =
324 &tagged_hash(TAG_DLOG_PROOF_FISCHLIN, &[session_id, &second_msg])
325 [0..(L / 4) as usize];
326
327 if *first_hash == *second_hash {
329 first_proofs.push(first_proof);
331 last_proofs.push(second_proof);
332
333 flag = true;
335
336 break;
337 }
338
339 second_counter += 1;
341 }
342
343 first_counter += 1;
345 }
346
347 if !flag {
348 return Err(ProofSearchExhausted);
349 }
350 }
351
352 let proofs = [first_proofs, last_proofs].concat();
354
355 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
357 let point = (generator * scalar).to_affine();
358
359 Ok(DLogProof {
360 point,
361 rand_commitments,
362 proofs,
363 })
364 }
365
366 #[must_use]
370 pub fn verify(proof: &DLogProof<C>, session_id: &[u8]) -> bool {
371 if proof.rand_commitments.len() != (R as usize) || proof.proofs.len() != (R as usize) {
375 return false;
376 }
377
378 let vec_rc_as_bytes = proof
380 .rand_commitments
381 .clone()
382 .into_iter()
383 .map(|x| point_to_bytes::<C>(&x))
384 .collect::<Vec<Vec<u8>>>();
385
386 let mut without_repetitions: HashSet<Vec<u8>> = HashSet::with_capacity(R as usize);
389 if !vec_rc_as_bytes
390 .clone()
391 .into_iter()
392 .all(move |x| without_repetitions.insert(x))
393 {
394 return false;
395 }
396
397 let rc_as_bytes = vec_rc_as_bytes.concat();
399
400 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
401 for i in 0..(R / 2) {
402 let first_msg = [
404 &point_to_bytes::<C>(&generator),
405 &rc_as_bytes[..],
406 &i.to_be_bytes(),
407 &proof.proofs[i as usize].challenge,
408 &scalar_to_bytes::<C>(&proof.proofs[i as usize].challenge_response),
409 ]
410 .concat();
411 let first_hash = &tagged_hash(TAG_DLOG_PROOF_FISCHLIN, &[session_id, &first_msg])
412 [0..(L / 4) as usize];
413
414 let second_msg = [
415 &point_to_bytes::<C>(&generator),
416 &rc_as_bytes[..],
417 &(i + (R / 2)).to_be_bytes(),
418 &proof.proofs[(i + (R / 2)) as usize].challenge,
419 &scalar_to_bytes::<C>(&proof.proofs[(i + (R / 2)) as usize].challenge_response),
420 ]
421 .concat();
422 let second_hash = &tagged_hash(TAG_DLOG_PROOF_FISCHLIN, &[session_id, &second_msg])
423 [0..(L / 4) as usize];
424
425 if !bool::from(first_hash.ct_eq(second_hash)) {
428 return false;
429 }
430
431 let verification_1 =
433 proof.proofs[i as usize].verify(&proof.point, &proof.rand_commitments[i as usize]);
434 let verification_2 = proof.proofs[(i + (R / 2)) as usize].verify(
435 &proof.point,
436 &proof.rand_commitments[(i + (R / 2)) as usize],
437 );
438
439 if !verification_1 || !verification_2 {
440 return false;
441 }
442 }
443
444 true
446 }
447
448 pub fn prove_commit(
455 scalar: &C::Scalar,
456 session_id: &[u8],
457 ) -> Result<(DLogProof<C>, HashOutput), ProofSearchExhausted> {
458 let proof = Self::prove(scalar, session_id)?;
459
460 let point_as_bytes = point_to_bytes::<C>(&proof.point);
462 let rc_as_bytes = proof
463 .rand_commitments
464 .clone()
465 .into_iter()
466 .map(|x| point_to_bytes::<C>(&x))
467 .collect::<Vec<Vec<u8>>>()
468 .concat();
469 let challenges_as_bytes = proof
470 .proofs
471 .clone()
472 .into_iter()
473 .map(|x| x.challenge)
474 .collect::<Vec<Vec<u8>>>()
475 .concat();
476 let responses_as_bytes = proof
477 .proofs
478 .clone()
479 .into_iter()
480 .map(|x| scalar_to_bytes::<C>(&x.challenge_response))
481 .collect::<Vec<Vec<u8>>>()
482 .concat();
483
484 let msg_for_commitment = [
485 point_as_bytes,
486 rc_as_bytes,
487 challenges_as_bytes,
488 responses_as_bytes,
489 ]
490 .concat();
491 let commitment = tagged_hash(
492 TAG_DLOG_PROOF_COMMITMENT,
493 &[session_id, &msg_for_commitment],
494 );
495
496 Ok((proof, commitment))
497 }
498
499 #[must_use]
501 pub fn decommit_verify(
502 proof: &DLogProof<C>,
503 commitment: &HashOutput,
504 session_id: &[u8],
505 ) -> bool {
506 let point_as_bytes = point_to_bytes::<C>(&proof.point);
508 let rc_as_bytes = proof
509 .rand_commitments
510 .clone()
511 .into_iter()
512 .map(|x| point_to_bytes::<C>(&x))
513 .collect::<Vec<Vec<u8>>>()
514 .concat();
515 let challenges_as_bytes = proof
516 .proofs
517 .clone()
518 .into_iter()
519 .map(|x| x.challenge)
520 .collect::<Vec<Vec<u8>>>()
521 .concat();
522 let responses_as_bytes = proof
523 .proofs
524 .clone()
525 .into_iter()
526 .map(|x| scalar_to_bytes::<C>(&x.challenge_response))
527 .collect::<Vec<Vec<u8>>>()
528 .concat();
529
530 let msg_for_commitment = [
531 point_as_bytes,
532 rc_as_bytes,
533 challenges_as_bytes,
534 responses_as_bytes,
535 ]
536 .concat();
537 let expected_commitment = tagged_hash(
538 TAG_DLOG_PROOF_COMMITMENT,
539 &[session_id, &msg_for_commitment],
540 );
541
542 bool::from(commitment.ct_eq(&expected_commitment)) && Self::verify(proof, session_id)
543 }
544}
545
546#[derive(Debug, Clone)]
550#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
551#[cfg_attr(
552 feature = "serde",
553 serde(bound(
554 serialize = "C::AffinePoint: serde::Serialize",
555 deserialize = "C::AffinePoint: serde::Deserialize<'de>"
556 ))
557)]
558pub struct RandomCommitments<C: DklsCurve> {
559 pub rc_g: C::AffinePoint,
560 pub rc_h: C::AffinePoint,
561}
562
563#[derive(Debug, Clone)]
565#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
566#[cfg_attr(
567 feature = "serde",
568 serde(bound(
569 serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
570 deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
571 ))
572)]
573pub struct CPProof<C: DklsCurve> {
574 pub base_g: C::AffinePoint, pub base_h: C::AffinePoint, pub point_u: C::AffinePoint,
577 pub point_v: C::AffinePoint,
578
579 pub challenge_response: C::Scalar,
580}
581
582impl<C: DklsCurve> CPProof<C> {
583 #[must_use]
593 pub fn prove_step1(
594 base_g: &C::AffinePoint,
595 base_h: &C::AffinePoint,
596 ) -> (C::Scalar, RandomCommitments<C>) {
597 let mut scalar_rand_commitment = <C::Scalar as Field>::ZERO;
599 while scalar_rand_commitment == <C::Scalar as Field>::ZERO {
600 scalar_rand_commitment = <C::Scalar as Field>::random(&mut rng::get_rng());
601 }
602
603 let point_rand_commitment_g = (*base_g * scalar_rand_commitment).to_affine();
604 let point_rand_commitment_h = (*base_h * scalar_rand_commitment).to_affine();
605
606 let rand_commitments = RandomCommitments {
607 rc_g: point_rand_commitment_g,
608 rc_h: point_rand_commitment_h,
609 };
610
611 (scalar_rand_commitment, rand_commitments)
612 }
613
614 #[must_use]
619 pub fn prove_step2(
620 base_g: &C::AffinePoint,
621 base_h: &C::AffinePoint,
622 scalar: &C::Scalar,
623 scalar_rand_commitment: &C::Scalar,
624 challenge: &C::Scalar,
625 ) -> CPProof<C> {
626 let point_u = (*base_g * scalar).to_affine();
628 let point_v = (*base_h * scalar).to_affine();
629
630 let challenge_response = *scalar_rand_commitment - (*challenge * scalar);
632
633 CPProof {
634 base_g: *base_g,
635 base_h: *base_h,
636 point_u,
637 point_v,
638
639 challenge_response,
640 }
641 }
642
643 #[must_use]
649 pub fn verify(&self, rand_commitments: &RandomCommitments<C>, challenge: &C::Scalar) -> bool {
650 let point_verify_g =
652 ((self.base_g * self.challenge_response) + (self.point_u * challenge)).to_affine();
653 let point_verify_h =
654 ((self.base_h * self.challenge_response) + (self.point_v * challenge)).to_affine();
655
656 (point_verify_g == rand_commitments.rc_g) && (point_verify_h == rand_commitments.rc_h)
657 }
658
659 #[must_use]
667 pub fn simulate(
668 base_g: &C::AffinePoint,
669 base_h: &C::AffinePoint,
670 point_u: &C::AffinePoint,
671 point_v: &C::AffinePoint,
672 ) -> (RandomCommitments<C>, C::Scalar, CPProof<C>) {
673 let challenge = <C::Scalar as Field>::random(&mut rng::get_rng());
675
676 let challenge_response = <C::Scalar as Field>::random(&mut rng::get_rng());
677
678 let point_rand_commitment_g =
680 ((*base_g * challenge_response) + (*point_u * challenge)).to_affine();
681 let point_rand_commitment_h =
682 ((*base_h * challenge_response) + (*point_v * challenge)).to_affine();
683
684 let rand_commitments = RandomCommitments {
685 rc_g: point_rand_commitment_g,
686 rc_h: point_rand_commitment_h,
687 };
688
689 let proof = CPProof {
690 base_g: *base_g,
691 base_h: *base_h,
692 point_u: *point_u,
693 point_v: *point_v,
694
695 challenge_response,
696 };
697
698 (rand_commitments, challenge, proof)
699 }
700}
701
702#[derive(Debug, Clone)]
706#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
707#[cfg_attr(
708 feature = "serde",
709 serde(bound(
710 serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
711 deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
712 ))
713)]
714pub struct EncProof<C: DklsCurve> {
715 pub proof0: CPProof<C>,
717 pub proof1: CPProof<C>,
718
719 pub commitments0: RandomCommitments<C>,
720 pub commitments1: RandomCommitments<C>,
721
722 pub challenge0: C::Scalar,
723 pub challenge1: C::Scalar,
724}
725
726impl<C: DklsCurve> EncProof<C> {
727 #[must_use]
732 pub fn prove(
733 session_id: &[u8],
734 base_h: &C::AffinePoint,
735 scalar: &C::Scalar,
736 bit: bool,
737 ) -> EncProof<C> {
738 let base_g = <C::AffinePoint as PrimeCurveAffine>::generator();
742
743 let u = (*base_h * scalar).to_affine();
748
749 let base_h_proj = C::ProjectivePoint::from(*base_h);
756 let g_times_scalar = base_g * scalar;
757 let v_if_true = (g_times_scalar + base_h_proj).to_affine();
758 let v_if_false = g_times_scalar.to_affine();
759 let fake_v_if_true = v_if_true;
760 let fake_v_if_false = (g_times_scalar - base_h_proj).to_affine();
761
762 let (v, fake_v) = if bit {
763 (v_if_true, fake_v_if_true)
764 } else {
765 (v_if_false, fake_v_if_false)
766 };
767
768 let (real_scalar_commitment, real_commitments) = CPProof::prove_step1(&base_g, base_h);
781
782 let (fake_commitments, fake_challenge, fake_proof) =
784 CPProof::simulate(&base_g, base_h, &fake_v, &u);
785
786 let base_g_as_bytes = point_to_bytes::<C>(&base_g);
791 let base_h_as_bytes = point_to_bytes::<C>(base_h);
792 let u_as_bytes = point_to_bytes::<C>(&u);
793 let v_as_bytes = point_to_bytes::<C>(&v);
794
795 let r_rc_g_as_bytes = point_to_bytes::<C>(&real_commitments.rc_g);
796 let r_rc_h_as_bytes = point_to_bytes::<C>(&real_commitments.rc_h);
797
798 let f_rc_g_as_bytes = point_to_bytes::<C>(&fake_commitments.rc_g);
799 let f_rc_h_as_bytes = point_to_bytes::<C>(&fake_commitments.rc_h);
800
801 let msg_for_challenge = if bit {
806 [
807 base_g_as_bytes,
808 base_h_as_bytes,
809 u_as_bytes,
810 v_as_bytes,
811 f_rc_g_as_bytes,
812 f_rc_h_as_bytes,
813 r_rc_g_as_bytes,
814 r_rc_h_as_bytes,
815 ]
816 .concat()
817 } else {
818 [
819 base_g_as_bytes,
820 base_h_as_bytes,
821 u_as_bytes,
822 v_as_bytes,
823 r_rc_g_as_bytes,
824 r_rc_h_as_bytes,
825 f_rc_g_as_bytes,
826 f_rc_h_as_bytes,
827 ]
828 .concat()
829 };
830
831 let challenge =
832 tagged_hash_as_scalar::<C>(TAG_ENCPROOF_FS, &[session_id, &msg_for_challenge]);
833
834 let real_challenge = challenge - fake_challenge;
846
847 let real_proof = CPProof::prove_step2(
848 &base_g,
849 base_h,
850 scalar,
851 &real_scalar_commitment,
852 &real_challenge,
853 );
854
855 if bit {
860 EncProof {
861 proof0: fake_proof,
862 proof1: real_proof,
863
864 commitments0: fake_commitments,
865 commitments1: real_commitments,
866
867 challenge0: fake_challenge,
868 challenge1: real_challenge,
869 }
870 } else {
871 EncProof {
872 proof0: real_proof,
873 proof1: fake_proof,
874
875 commitments0: real_commitments,
876 commitments1: fake_commitments,
877
878 challenge0: real_challenge,
879 challenge1: fake_challenge,
880 }
881 }
882 }
883
884 #[must_use]
888 pub fn verify(&self, session_id: &[u8]) -> bool {
889 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
891 if (self.proof0.base_g != generator)
892 || (self.proof0.base_g != self.proof1.base_g)
893 || (self.proof0.base_h != self.proof1.base_h)
894 || (self.proof0.point_v != self.proof1.point_v) || (self.proof0.point_u != (C::ProjectivePoint::from(self.proof1.point_u) + C::ProjectivePoint::from(self.proof1.base_h)).to_affine())
896 {
898 return false;
899 }
900
901 let base_g_as_bytes = point_to_bytes::<C>(&self.proof0.base_g);
904 let base_h_as_bytes = point_to_bytes::<C>(&self.proof0.base_h);
905
906 let u_as_bytes = point_to_bytes::<C>(&self.proof0.point_v);
908 let v_as_bytes = point_to_bytes::<C>(&self.proof0.point_u);
909
910 let rc0_g_as_bytes = point_to_bytes::<C>(&self.commitments0.rc_g);
911 let rc0_h_as_bytes = point_to_bytes::<C>(&self.commitments0.rc_h);
912
913 let rc1_g_as_bytes = point_to_bytes::<C>(&self.commitments1.rc_g);
914 let rc1_h_as_bytes = point_to_bytes::<C>(&self.commitments1.rc_h);
915
916 let msg_for_challenge = [
917 base_g_as_bytes,
918 base_h_as_bytes,
919 u_as_bytes,
920 v_as_bytes,
921 rc0_g_as_bytes,
922 rc0_h_as_bytes,
923 rc1_g_as_bytes,
924 rc1_h_as_bytes,
925 ]
926 .concat();
927 let expected_challenge =
928 tagged_hash_as_scalar::<C>(TAG_ENCPROOF_FS, &[session_id, &msg_for_challenge]);
929
930 if expected_challenge != self.challenge0 + self.challenge1 {
932 return false;
933 }
934
935 self.proof0.verify(&self.commitments0, &self.challenge0)
937 && self.proof1.verify(&self.commitments1, &self.challenge1)
938 }
939
940 #[must_use]
949 pub fn get_u_and_v(&self) -> (C::AffinePoint, C::AffinePoint) {
950 (self.proof0.point_v, self.proof0.point_u)
951 }
952}
953
954#[cfg(test)]
955mod tests {
956 use super::*;
957 use k256::Secp256k1;
958
959 type TestCurve = Secp256k1;
960 type Scalar = <TestCurve as elliptic_curve::CurveArithmetic>::Scalar;
961 type AffinePoint = <TestCurve as elliptic_curve::CurveArithmetic>::AffinePoint;
962 type ProjectivePoint = <TestCurve as elliptic_curve::CurveArithmetic>::ProjectivePoint;
963
964 #[test]
968 fn test_dlog_proof() {
969 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
970 let session_id = rng::get_rng().random::<[u8; 32]>();
971 let proof = DLogProof::<TestCurve>::prove(&scalar, &session_id).unwrap();
972 assert!(DLogProof::<TestCurve>::verify(&proof, &session_id));
973 }
974
975 #[test]
978 fn test_dlog_proof_fail_proof() {
979 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
980 let session_id = rng::get_rng().random::<[u8; 32]>();
981 let mut proof = DLogProof::<TestCurve>::prove(&scalar, &session_id).unwrap();
982 proof.proofs[0].challenge_response *= Scalar::from(2u32); assert!(!(DLogProof::<TestCurve>::verify(&proof, &session_id)));
984 }
985
986 #[test]
988 fn test_dlog_proof_rejects_duplicate_rand_commitments() {
989 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
990 let session_id = rng::get_rng().random::<[u8; 32]>();
991 let mut proof = DLogProof::<TestCurve>::prove(&scalar, &session_id).unwrap();
992 proof.rand_commitments[1] = proof.rand_commitments[0];
993 assert!(!DLogProof::<TestCurve>::verify(&proof, &session_id));
994 }
995
996 #[test]
998 fn test_dlog_proof_rejects_wrong_vector_lengths() {
999 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
1000 let session_id = rng::get_rng().random::<[u8; 32]>();
1001
1002 let mut proof_short_commitments =
1003 DLogProof::<TestCurve>::prove(&scalar, &session_id).unwrap();
1004 proof_short_commitments.rand_commitments.pop();
1005 assert!(!DLogProof::<TestCurve>::verify(
1006 &proof_short_commitments,
1007 &session_id
1008 ));
1009
1010 let mut proof_short_proofs = DLogProof::<TestCurve>::prove(&scalar, &session_id).unwrap();
1011 proof_short_proofs.proofs.pop();
1012 assert!(!DLogProof::<TestCurve>::verify(
1013 &proof_short_proofs,
1014 &session_id
1015 ));
1016 }
1017
1018 #[test]
1020 fn test_dlog_proof_rejects_mismatched_session_id() {
1021 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
1022 let prove_sid = rng::get_rng().random::<[u8; 32]>();
1023 let mut verify_sid = prove_sid;
1024 verify_sid[0] ^= 1;
1025 let proof = DLogProof::<TestCurve>::prove(&scalar, &prove_sid).unwrap();
1026 assert!(!DLogProof::<TestCurve>::verify(&proof, &verify_sid));
1027 }
1028
1029 #[test]
1032 fn test_dlog_proof_commit() {
1033 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
1034 let session_id = rng::get_rng().random::<[u8; 32]>();
1035 let (proof, commitment) =
1036 DLogProof::<TestCurve>::prove_commit(&scalar, &session_id).unwrap();
1037 assert!(DLogProof::<TestCurve>::decommit_verify(
1038 &proof,
1039 &commitment,
1040 &session_id
1041 ));
1042 }
1043
1044 #[test]
1047 fn test_dlog_proof_commit_fail_proof() {
1048 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
1049 let session_id = rng::get_rng().random::<[u8; 32]>();
1050 let (mut proof, commitment) =
1051 DLogProof::<TestCurve>::prove_commit(&scalar, &session_id).unwrap();
1052 proof.proofs[0].challenge_response *= Scalar::from(2u32); assert!(!(DLogProof::<TestCurve>::decommit_verify(&proof, &commitment, &session_id)));
1054 }
1055
1056 #[test]
1059 fn test_dlog_proof_commit_fail_commitment() {
1060 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
1061 let session_id = rng::get_rng().random::<[u8; 32]>();
1062 let (proof, mut commitment) =
1063 DLogProof::<TestCurve>::prove_commit(&scalar, &session_id).unwrap();
1064 if commitment[0] == 0 {
1065 commitment[0] = 1;
1066 } else {
1067 commitment[0] -= 1;
1068 } assert!(!(DLogProof::<TestCurve>::decommit_verify(&proof, &commitment, &session_id)));
1070 }
1071
1072 #[test]
1076 fn test_cp_proof() {
1077 let log_base_g = <Scalar as Field>::random(&mut rng::get_rng());
1078 let log_base_h = <Scalar as Field>::random(&mut rng::get_rng());
1079 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
1080
1081 let generator = <AffinePoint as PrimeCurveAffine>::generator();
1082 let base_g = (generator * log_base_g).to_affine();
1083 let base_h = (generator * log_base_h).to_affine();
1084
1085 let (scalar_rand_commitment, rand_commitments) =
1087 CPProof::<TestCurve>::prove_step1(&base_g, &base_h);
1088
1089 let challenge = <Scalar as Field>::random(&mut rng::get_rng());
1091
1092 let proof = CPProof::<TestCurve>::prove_step2(
1094 &base_g,
1095 &base_h,
1096 &scalar,
1097 &scalar_rand_commitment,
1098 &challenge,
1099 );
1100
1101 let verification = proof.verify(&rand_commitments, &challenge);
1103
1104 assert!(verification);
1105 }
1106
1107 #[test]
1109 fn test_cp_proof_simulate() {
1110 let log_base_g = <Scalar as Field>::random(&mut rng::get_rng());
1111 let log_base_h = <Scalar as Field>::random(&mut rng::get_rng());
1112 let log_point_u = <Scalar as Field>::random(&mut rng::get_rng());
1113 let log_point_v = <Scalar as Field>::random(&mut rng::get_rng());
1114
1115 let generator = <AffinePoint as PrimeCurveAffine>::generator();
1116 let base_g = (generator * log_base_g).to_affine();
1117 let base_h = (generator * log_base_h).to_affine();
1118 let point_u = (generator * log_point_u).to_affine();
1119 let point_v = (generator * log_point_v).to_affine();
1120
1121 let (rand_commitments, challenge, proof) =
1123 CPProof::<TestCurve>::simulate(&base_g, &base_h, &point_u, &point_v);
1124
1125 let verification = proof.verify(&rand_commitments, &challenge);
1126
1127 assert!(verification);
1128 }
1129
1130 #[test]
1132 fn test_cp_proof_simulate_wrong_statement_fails() {
1133 let log_base_g = <Scalar as Field>::random(&mut rng::get_rng());
1134 let log_base_h = <Scalar as Field>::random(&mut rng::get_rng());
1135 let log_point_u = <Scalar as Field>::random(&mut rng::get_rng());
1136 let log_point_v = <Scalar as Field>::random(&mut rng::get_rng());
1137
1138 let generator = <AffinePoint as PrimeCurveAffine>::generator();
1139 let base_g = (generator * log_base_g).to_affine();
1140 let base_h = (generator * log_base_h).to_affine();
1141 let point_u = (generator * log_point_u).to_affine();
1142 let point_v = (generator * log_point_v).to_affine();
1143
1144 let (rand_commitments, challenge, mut proof) =
1146 CPProof::<TestCurve>::simulate(&base_g, &base_h, &point_u, &point_v);
1147 proof.point_u =
1148 (ProjectivePoint::from(proof.point_u) + ProjectivePoint::GENERATOR).to_affine();
1149
1150 assert!(!proof.verify(&rand_commitments, &challenge));
1151 }
1152
1153 #[test]
1157 fn test_enc_proof() {
1158 let session_id = rng::get_rng().random::<[u8; 32]>();
1160
1161 let log_base_h = <Scalar as Field>::random(&mut rng::get_rng());
1162 let generator = <AffinePoint as PrimeCurveAffine>::generator();
1163 let base_h = (generator * log_base_h).to_affine();
1164
1165 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
1166
1167 let bit: bool = rng::get_rng().random();
1168
1169 let proof = EncProof::<TestCurve>::prove(&session_id, &base_h, &scalar, bit);
1171
1172 let verification = proof.verify(&session_id);
1174
1175 assert!(verification);
1176 }
1177
1178 #[test]
1180 fn test_enc_proof_rejects_incompatible_subproofs() {
1181 let session_id = rng::get_rng().random::<[u8; 32]>();
1182 let log_base_h = <Scalar as Field>::random(&mut rng::get_rng());
1183 let generator = <AffinePoint as PrimeCurveAffine>::generator();
1184 let base_h = (generator * log_base_h).to_affine();
1185 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
1186 let bit: bool = rng::get_rng().random();
1187
1188 let mut proof = EncProof::<TestCurve>::prove(&session_id, &base_h, &scalar, bit);
1189 proof.proof0.base_g = (generator * Scalar::from(2u32)).to_affine();
1190
1191 assert!(!proof.verify(&session_id));
1192 }
1193
1194 #[test]
1196 fn test_enc_proof_rejects_challenge_sum_mismatch() {
1197 let session_id = rng::get_rng().random::<[u8; 32]>();
1198 let log_base_h = <Scalar as Field>::random(&mut rng::get_rng());
1199 let generator = <AffinePoint as PrimeCurveAffine>::generator();
1200 let base_h = (generator * log_base_h).to_affine();
1201 let scalar = <Scalar as Field>::random(&mut rng::get_rng());
1202 let bit: bool = rng::get_rng().random();
1203
1204 let mut proof = EncProof::<TestCurve>::prove(&session_id, &base_h, &scalar, bit);
1205 proof.challenge0 += Scalar::ONE;
1206
1207 assert!(!proof.verify(&session_id));
1208 }
1209
1210 #[test]
1212 fn test_interactive_dlog_proof_rejects_oversized_challenge() {
1213 let generator = <AffinePoint as PrimeCurveAffine>::generator();
1214 let proof: InteractiveDLogProof<TestCurve> = InteractiveDLogProof {
1215 challenge: vec![0u8; (T / 8 + 1) as usize],
1216 challenge_response: <Scalar as Field>::ZERO,
1217 _curve: PhantomData,
1218 };
1219 assert!(!proof.verify(&generator, &generator));
1220 }
1221
1222 #[test]
1224 fn test_interactive_dlog_proof_rejects_empty_challenge() {
1225 let generator = <AffinePoint as PrimeCurveAffine>::generator();
1226 let proof: InteractiveDLogProof<TestCurve> = InteractiveDLogProof {
1227 challenge: vec![],
1228 challenge_response: <Scalar as Field>::ZERO,
1229 _curve: PhantomData,
1230 };
1231 assert!(!proof.verify(&generator, &generator));
1232 }
1233}