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