elastic_elgamal/proofs/
mod.rs

1//! Zero-knowledge proofs.
2
3use core::fmt;
4
5use merlin::Transcript;
6
7use crate::{
8    alloc::vec,
9    group::{Group, RandomBytesProvider},
10};
11
12mod commitment;
13mod log_equality;
14mod mul;
15mod possession;
16mod range;
17mod ring;
18
19pub use self::{
20    commitment::CommitmentEquivalenceProof,
21    log_equality::LogEqualityProof,
22    mul::SumOfSquaresProof,
23    possession::ProofOfPossession,
24    range::{PreparedRange, RangeDecomposition, RangeProof},
25    ring::{RingProof, RingProofBuilder},
26};
27
28/// Extension trait for Merlin transcripts used in constructing our proofs.
29pub(crate) trait TranscriptForGroup {
30    fn start_proof(&mut self, proof_label: &'static [u8]);
31
32    fn append_element_bytes(&mut self, label: &'static [u8], element_bytes: &[u8]);
33
34    fn append_element<G: Group>(&mut self, label: &'static [u8], element: &G::Element);
35
36    fn challenge_scalar<G: Group>(&mut self, label: &'static [u8]) -> G::Scalar;
37}
38
39impl TranscriptForGroup for Transcript {
40    fn start_proof(&mut self, proof_label: &'static [u8]) {
41        self.append_message(b"dom-sep", proof_label);
42    }
43
44    fn append_element_bytes(&mut self, label: &'static [u8], element_bytes: &[u8]) {
45        self.append_message(label, element_bytes);
46    }
47
48    fn append_element<G: Group>(&mut self, label: &'static [u8], element: &G::Element) {
49        let mut output = vec![0_u8; G::ELEMENT_SIZE];
50        G::serialize_element(element, &mut output);
51        self.append_element_bytes(label, &output);
52    }
53
54    fn challenge_scalar<G: Group>(&mut self, label: &'static [u8]) -> G::Scalar {
55        G::scalar_from_random_bytes(RandomBytesProvider::new(self, label))
56    }
57}
58
59/// Error verifying base proofs, such as [`RingProof`], [`LogEqualityProof`]
60/// or [`ProofOfPossession`].
61#[derive(Debug)]
62#[non_exhaustive]
63pub enum VerificationError {
64    /// Restored challenge scalar does not match the one provided in the proof.
65    ///
66    /// This error most likely means that the proof itself is malformed, or that it was created
67    /// for a different context than it is being verified for.
68    ChallengeMismatch,
69    /// A collection (e.g., number of responses in a [`RingProof`]) has a different size
70    /// than expected.
71    ///
72    /// This error most likely means that the proof is malformed.
73    LenMismatch {
74        /// Human-readable collection name, such as "public keys".
75        collection: &'static str,
76        /// Expected size of the collection.
77        expected: usize,
78        /// Actual size of the collection.
79        actual: usize,
80    },
81}
82
83impl VerificationError {
84    pub(crate) fn check_lengths(
85        collection: &'static str,
86        expected: usize,
87        actual: usize,
88    ) -> Result<(), Self> {
89        if expected == actual {
90            Ok(())
91        } else {
92            Err(Self::LenMismatch {
93                collection,
94                expected,
95                actual,
96            })
97        }
98    }
99}
100
101impl fmt::Display for VerificationError {
102    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
103        match self {
104            Self::ChallengeMismatch => formatter.write_str(
105                "restored challenge scalar does not match the one provided in the proof",
106            ),
107
108            Self::LenMismatch {
109                collection,
110                expected,
111                actual,
112            } => write!(
113                formatter,
114                "number of {collection} ({actual}) differs from expected ({expected})",
115            ),
116        }
117    }
118}
119
120#[cfg(feature = "std")]
121impl std::error::Error for VerificationError {}