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}