lox_zkp/toolbox/
mod.rs

1//! Contains lower-level tools that allow programmable specification
2//! of proof statements.
3//!
4//! The higher-level [`define_proof`] macro allows declarative
5//! specification of static proof statements, and expands into code
6//! that uses this lower-level API.  This lower-level API can also be
7//! used directly to perform imperative specification of proof
8//! statements, allowing proof statements with runtime parameters
9//! (e.g., an anonymous credential with a variable number of
10//! attributes).
11//!
12//! The `SchnorrCS` trait defines the common constraint system API
13//! used for specifying proof statements; it is implemented by the
14//! `Prover`, `Verifier`, and `BatchVerifier` structs.
15//!
16//! Roughly speaking, the tools fit together in the following way:
17//!
18//! * Statements are defined as generic functions that take a
19//!   `SchnorrCS` implementation and some variables, and add the
20//!   proof statements to the constraint system;
21//!
22//! * To create a proof, construct a `Prover`, allocate and assign
23//!   variables, pass the prover and the variables to the generic
24//!   statement function, then consume the prover to obtain a proof.
25//!
26//! * To verify a proof, construct a `Verifier`,
27//!   allocate and assign variables, pass the verifier and the variables
28//!   to the generic statement function, then consume the verifier to
29//!   obtain a verification result.
30//!
31//! Note that the expansion of the [`define_proof`] macro contains a
32//! public `internal` module with the generated proof statement
33//! function, making it possible to combine generated and hand-crafted
34//! proof statements into the same constraint system.
35
36/// Implements batch verification of batchable proofs.
37pub mod batch_verifier;
38/// Implements proof creation.
39pub mod prover;
40/// Implements proof verification of compact and batchable proofs.
41pub mod verifier;
42
43use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
44use curve25519_dalek::scalar::Scalar;
45use curve25519_dalek::traits::IsIdentity;
46
47use crate::{ProofError, Transcript};
48
49/// An interface for specifying proof statements, common between
50/// provers and verifiers.
51///
52/// The variables for the constraint system are provided as associated
53/// types, allowing different implementations to have different point
54/// and scalar types.  For instance, the batch verifier has two types
55/// of point variables, one for points common to all proofs in the
56/// batch, and one for points varying per-proof.
57///
58/// This is why variable allocation is *not* included in the trait, as
59/// different roles may have different behaviour -- for instance, a
60/// prover needs to supply assignments to the scalar variables, but
61/// a verifier doesn't have access to the prover's secret scalars.
62///
63/// To specify a proof statement using this trait, write a generic
64/// function that takes a constraint system as a parameter and adds
65/// the statements.  For instance, to specify an equality of discrete
66/// logarithms, one could write
67/// ```rust,ignore
68/// fn dleq_statement<CS: SchnorrCS>(
69///     cs: &mut CS,
70///     x: CS::ScalarVar,
71///     A: CS::PointVar,
72///     G: CS::PointVar,
73///     B: CS::PointVar,
74///     H: CS::PointVar,
75/// ) {
76///     cs.constrain(A, vec![(x, B)]);
77///     cs.constrain(G, vec![(x, H)]);
78/// }
79/// ```
80///
81/// This means that multiple statements can be added to the same
82/// proof, independently of the specification of the statement, by
83/// constructing a constraint system and then passing it to multiple
84/// statement functions.
85pub trait SchnorrCS {
86    /// A handle for a scalar variable in the constraint system.
87    type ScalarVar: Copy;
88    /// A handle for a point variable in the constraint system.
89    type PointVar: Copy;
90
91    /// Add a constraint of the form `lhs = linear_combination`.
92    fn constrain(
93        &mut self,
94        lhs: Self::PointVar,
95        linear_combination: Vec<(Self::ScalarVar, Self::PointVar)>,
96    );
97}
98
99/// This trait defines the wire format for how the constraint system
100/// interacts with the proof transcript.
101pub trait TranscriptProtocol {
102    /// Appends `label` to the transcript as a domain separator.
103    fn domain_sep(&mut self, label: &'static [u8]);
104
105    /// Append the `label` for a scalar variable to the transcript.
106    ///
107    /// Note: this does not commit its assignment, which is secret,
108    /// and only serves to bind the proof to the variable allocations.
109    fn append_scalar_var(&mut self, label: &'static [u8]);
110
111    /// Append a point variable to the transcript, for use by a prover.
112    ///
113    /// Returns the compressed point encoding to allow reusing the
114    /// result of the encoding computation; the return value can be
115    /// discarded if it's unused.
116    fn append_point_var(
117        &mut self,
118        label: &'static [u8],
119        point: &RistrettoPoint,
120    ) -> CompressedRistretto;
121
122    /// Check that point variable is not the identity and
123    /// append it to the transcript, for use by a verifier.
124    ///
125    /// Returns `Ok(())` if the point is not the identity point (and
126    /// therefore generates the full ristretto255 group).
127    ///
128    /// Using this function prevents small-subgroup attacks.
129    fn validate_and_append_point_var(
130        &mut self,
131        label: &'static [u8],
132        point: &CompressedRistretto,
133    ) -> Result<(), ProofError>;
134
135    /// Append a blinding factor commitment to the transcript, for use by
136    /// a prover.
137    ///
138    /// Returns the compressed point encoding to allow reusing the
139    /// result of the encoding computation; the return value can be
140    /// discarded if it's unused.
141    fn append_blinding_commitment(
142        &mut self,
143        label: &'static [u8],
144        point: &RistrettoPoint,
145    ) -> CompressedRistretto;
146
147    /// Check that a blinding factor commitment is not the identity and
148    /// commit it to the transcript, for use by a verifier.
149    ///
150    /// Returns `Ok(())` if the point is not the identity point (and
151    /// therefore generates the full ristretto255 group).
152    ///
153    /// Using this function prevents small-subgroup attacks.
154    fn validate_and_append_blinding_commitment(
155        &mut self,
156        label: &'static [u8],
157        point: &CompressedRistretto,
158    ) -> Result<(), ProofError>;
159
160    /// Get a scalar challenge from the transcript.
161    fn get_challenge(&mut self, label: &'static [u8]) -> Scalar;
162}
163
164impl TranscriptProtocol for Transcript {
165    fn domain_sep(&mut self, label: &'static [u8]) {
166        self.append_message(b"dom-sep", b"schnorrzkp/1.0/ristretto255");
167        self.append_message(b"dom-sep", label);
168    }
169
170    fn append_scalar_var(&mut self, label: &'static [u8]) {
171        self.append_message(b"scvar", label);
172    }
173
174    fn append_point_var(
175        &mut self,
176        label: &'static [u8],
177        point: &RistrettoPoint,
178    ) -> CompressedRistretto {
179        let encoding = point.compress();
180        self.append_message(b"ptvar", label);
181        self.append_message(b"val", encoding.as_bytes());
182        encoding
183    }
184
185    fn validate_and_append_point_var(
186        &mut self,
187        label: &'static [u8],
188        point: &CompressedRistretto,
189    ) -> Result<(), ProofError> {
190        if point.is_identity() {
191            return Err(ProofError::VerificationFailure);
192        }
193        self.append_message(b"ptvar", label);
194        self.append_message(b"val", point.as_bytes());
195        Ok(())
196    }
197
198    fn append_blinding_commitment(
199        &mut self,
200        label: &'static [u8],
201        point: &RistrettoPoint,
202    ) -> CompressedRistretto {
203        let encoding = point.compress();
204        self.append_message(b"blindcom", label);
205        self.append_message(b"val", encoding.as_bytes());
206        encoding
207    }
208
209    fn validate_and_append_blinding_commitment(
210        &mut self,
211        label: &'static [u8],
212        point: &CompressedRistretto,
213    ) -> Result<(), ProofError> {
214        if point.is_identity() {
215            return Err(ProofError::VerificationFailure);
216        }
217        self.append_message(b"blindcom", label);
218        self.append_message(b"val", point.as_bytes());
219        Ok(())
220    }
221
222    fn get_challenge(&mut self, label: &'static [u8]) -> Scalar {
223        let mut bytes = [0; 64];
224        self.challenge_bytes(label, &mut bytes);
225        Scalar::from_bytes_mod_order_wide(&bytes)
226    }
227}