Skip to content

Commit d8b948f

Browse files
author
GOURIOU Lénaïck
committed
Added documentation for the sigma module
1 parent b05fe10 commit d8b948f

File tree

10 files changed

+263
-30
lines changed

10 files changed

+263
-30
lines changed

src/toolbox/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub mod batch_verifier;
3939
pub mod prover;
4040
/// Implements proof verification of compact and batchable proofs.
4141
pub mod verifier;
42+
/// Contains lower-level tools that allow programmable specification of proof statements.
4243
pub mod sigma;
4344

4445
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};

src/toolbox/sigma/fiat_shamir.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,56 @@
1+
//! Fiat-Shamir transformation for Sigma protocols.
2+
//!
3+
//! This module defines `NISigmaProtocol`, a generic non-interactive Sigma protocol wrapper,
4+
//! based on applying the Fiat-Shamir heuristic using a transcript codec.
5+
//!
6+
//! It transforms an interactive Sigma protocol into a non-interactive one,
7+
//! by deriving challenges deterministically from previous protocol messages
8+
//! via a cryptographic sponge function (transcript).
9+
//!
10+
//! # Usage
11+
//! This struct is generic over:
12+
//! - `P`: the underlying Sigma protocol (`SigmaProtocol` trait).
13+
//! - `C`: the transcript codec (`TranscriptCodec` trait).
14+
//! - `G`: the group used for commitments and operations (`Group` trait).
15+
//!
16+
//! # Example
17+
//! ```
18+
//! use lox_zkp::toolbox::sigma::fiat_shamir::NISigmaProtocol;
19+
//!
20+
//! let protocol = SchnorrProof { morphismp: statement };
21+
//! let mut nizk = NISigmaProtocol::<_, KeccakTranscript<G>, G>::new(b"context", protocol);
22+
//! let proof = nizk.prove(&witness, &mut rng);
23+
//! assert!(nizk.verify(&proof).is_ok());
24+
//! ```
25+
126
use rand::{RngCore, CryptoRng};
227
use crate::toolbox::sigma::SigmaProtocol;
328
use crate::toolbox::sigma::transcript::TranscriptCodec;
429
use group::Group;
530

31+
/// A Fiat-Shamir transformation of a Sigma protocol into a non-interactive proof.
32+
///
33+
/// `NISigmaProtocol` wraps an interactive Sigma protocol `P`
34+
/// and a hash-based transcript `C`, to produce non-interactive proofs.
35+
///
36+
/// It manages the domain separation, transcript reset,
37+
/// proof generation, and proof verification.
38+
///
39+
/// # Type Parameters
40+
/// - `P`: the Sigma protocol implementation.
41+
/// - `C`: the transcript codec used for Fiat-Shamir.
42+
/// - `G`: the group on which the protocol operates.
643
pub struct NISigmaProtocol<P, C, G>
744
where
845
G: Group,
946
P: SigmaProtocol<Commitment = Vec<G>, Challenge = <G as Group>::Scalar>,
1047
C: TranscriptCodec<G>,
1148
{
49+
/// Domain separation string for the Fiat-Shamir transform.
1250
domain_sep: Vec<u8>,
51+
/// Current transcript state.
1352
hash_state: C,
53+
/// Underlying Sigma protocol.
1454
sigmap: P,
1555
}
1656

@@ -20,14 +60,14 @@ where
2060
P: SigmaProtocol<Commitment = Vec<G>, Challenge = <G as Group>::Scalar>,
2161
C: TranscriptCodec<G>,
2262
{
23-
// Create new NIZK transformator.
63+
/// Creates a new non-interactive Sigma protocol, identified by a domain separator (usually fixed per protocol instantiation), and an initialized Sigma protocol instance.
2464
pub fn new(iv: &[u8], instance: P) -> Self {
2565
let domain_sep = iv.to_vec();
2666
let hash_state = C::new(iv);
2767
Self { domain_sep, hash_state, sigmap: instance }
2868
}
2969

30-
// Generate new non-interactive proof
70+
/// Produces a non-interactive proof for a witness and serializes it as a vector of bytes.
3171
pub fn prove(
3272
&mut self,
3373
witness: &P::Witness,
@@ -49,7 +89,7 @@ where
4989
self.sigmap.serialize_batchable(&commitment, &challenge, &response)
5090
}
5191

52-
/// Verification of non-interactive proof
92+
/// Verify a non-interactive serialized proof and returns a Result: `Ok(())` if the proof verifies successfully, `Err(())` otherwise.
5393
pub fn verify(&mut self, proof: &Vec<u8>) -> Result<(), ()> {
5494
self.hash_state = C::new(&self.domain_sep);
5595

src/toolbox/sigma/group_morphism.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,37 @@
1+
//! # Group Morphism and Preimage Handling
2+
//!
3+
//! This module provides utilities for describing and manipulating **linear group morphisms**,
4+
//! supporting sigma protocols over group-based statements (e.g., discrete logarithms, DLEQ proofs). See Maurer09.
5+
//!
6+
//! It includes:
7+
//! - `LinearCombination`: a sparse representation of scalar multiplication relations
8+
//! - `Morphism`: a collection of linear combinations acting on group elements
9+
//! - `GroupMorphismPreimage`: a higher-level structure managing morphisms and their associated images
10+
111
use group::{Group, GroupEncoding};
212

13+
/// A sparse linear combination of scalars and group elements.
14+
///
15+
/// Stores indices into external lists of scalars and group elements.
16+
/// Used to define individual constraints inside a Morphism.
317
pub struct LinearCombination {
418
pub scalar_indices: Vec<usize>,
519
pub element_indices: Vec<usize>,
620
}
721

22+
/// A Morphism represents a list of linear combinations over group elements.
23+
///
24+
/// It supports dynamic allocation of scalars and elements,
25+
/// and evaluates by performing multi-scalar multiplications.
26+
827
pub struct Morphism<G: Group> {
928
pub linear_combination: Vec<LinearCombination>,
1029
pub group_elements: Vec<G>,
1130
pub num_scalars: usize,
1231
pub num_elements: usize,
1332
}
1433

34+
/// Perform a simple multi-scalar multiplication (MSM) over scalars and points.
1535
fn msm_pr<G: Group>(scalars: &[G::Scalar], bases: &[G]) -> G {
1636
let mut acc = G::identity();
1737
for (s, p) in scalars.iter().zip(bases.iter()) {
@@ -21,6 +41,7 @@ fn msm_pr<G: Group>(scalars: &[G::Scalar], bases: &[G]) -> G {
2141
}
2242

2343
impl<G: Group> Morphism<G> {
44+
/// Creates a new empty Morphism.
2445
pub fn new() -> Self {
2546
Self {
2647
linear_combination: Vec::new(),
@@ -30,14 +51,19 @@ impl<G: Group> Morphism<G> {
3051
}
3152
}
3253

54+
/// Adds a new linear combination constraint.
3355
pub fn append(&mut self, lc: LinearCombination) {
3456
self.linear_combination.push(lc);
3557
}
3658

59+
/// Returns the number of constraint statements.
3760
pub fn num_statements(&self) -> usize {
3861
self.linear_combination.len()
3962
}
4063

64+
/// Evaluate the Morphism given a set of scalar values.
65+
///
66+
/// Computes all linear combinations using the provided scalars and returns their group outputs.
4167
pub fn evaluate(&self, scalars: &[<G as Group>::Scalar]) -> Vec<G> {
4268
self.linear_combination.iter().map(|lc| {
4369
let coefficients: Vec<_> = lc.scalar_indices.iter().map(|&i| scalars[i].clone()).collect();
@@ -47,32 +73,44 @@ impl<G: Group> Morphism<G> {
4773
}
4874
}
4975

76+
/// A wrapper struct coupling a Morphism and the corresponding expected image elements.
77+
///
78+
/// Provides a higher-level API to build proof instances from sparse constraints. The equations are manipulated solely through 2 lists:
79+
/// - the index of a set of Group elements (maintained in Morphism)
80+
/// - the index of a set of scalars (provided as input for the execution)
5081
pub struct GroupMorphismPreimage<G>
5182
where
5283
G: Group + GroupEncoding,
5384
{
85+
/// The underlying morphism describing the structure of the statement.
5486
pub morphism: Morphism<G>,
87+
/// Indices pointing to elements representing the "target" images for each constraint.
5588
pub image: Vec<usize>,
5689
}
5790

5891
impl<G> GroupMorphismPreimage<G>
5992
where
6093
G: Group + GroupEncoding,
6194
{
95+
/// Create a new empty GroupMorphismPreimage.
6296
pub fn new() -> Self {
6397
Self {
6498
morphism: Morphism::new(),
6599
image: Vec::new(),
66100
}
67101
}
68102

103+
/// Computes the number of bytes needed when serializing all the current commitments.
69104
pub fn commit_bytes_len(&self) -> usize {
70105
let repr_len = <G::Repr as Default>::default()
71106
.as_ref()
72107
.len(); // size of encoded point
73108
self.morphism.num_statements() * repr_len // total size of a commit
74109
}
75110

111+
/// Append a new equation relating scalars to group elements.
112+
///
113+
/// `lhs` is the index of the image, and `rhs` is a list of (scalar_idx, element_idx) pairs.
76114
pub fn append_equation(&mut self, lhs: usize, rhs: &[(usize, usize)]) {
77115
let lc = LinearCombination {
78116
scalar_indices: rhs.iter().map(|&(s, _)| s).collect(),
@@ -82,15 +120,17 @@ where
82120
self.image.push(lhs);
83121
}
84122

85-
// Allocate n new Scalar's
123+
/// Allocate space for `n` new scalars and return their indices.
86124
pub fn allocate_scalars(&mut self, n: usize) -> Vec<usize> {
87125
let start = self.morphism.num_scalars;
88126
let indices: Vec<usize> = (start..start + n).collect();
89127
self.morphism.num_scalars += n;
90128
indices
91129
}
92130

93-
// Allocate n new emplacments for Group elements (completed with set_elements for assignation)
131+
/// Allocate space for `n` new group elements and return their indices.
132+
///
133+
/// The allocated elements are initially set to the identity.
94134
pub fn allocate_elements(&mut self, n: usize) -> Vec<usize> {
95135
let start = self.morphism.num_elements;
96136
let indices: Vec<usize> = (start..start + n).collect();
@@ -101,12 +141,14 @@ where
101141
indices
102142
}
103143

144+
/// Set the value of group elements at a given index, inside the list of allocated group elements.
104145
pub fn set_elements(&mut self, elements: &[(usize, G)]) {
105146
for &(i, ref elt) in elements {
106147
self.morphism.group_elements[i] = elt.clone();
107148
}
108149
}
109150

151+
/// Return the group elements corresponding to the image indices.
110152
pub fn image(&self) -> Vec<G> {
111153
let mut result = Vec::new();
112154
for i in &self.image {

src/toolbox/sigma/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub mod proof_composition;
33
pub mod fiat_shamir;
44
pub mod group_morphism;
55
pub mod schnorr_proof;
6+
/// Defines the transcript for a Sigma Protocol
67
pub mod transcript;
78

89
pub use r#trait::{SigmaProtocol, SigmaProtocolSimulator};

src/toolbox/sigma/proof_composition.rs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
1+
//! Sigma protocol composition: AND and OR constructions.
2+
//!
3+
//! This module provides combinators to compose two Sigma protocols
4+
//! into a new protocol proving statements with logical AND or OR relations.
5+
//!
6+
//! # Overview
7+
//! - `AndProtocol<P, Q>`: Proves both substatements (`P` and `Q`) simultaneously.
8+
//! - `OrProtocol<P, Q>`: Proves knowledge of a witness for *one* substatement, without revealing which one (using simulation for the other).
9+
//!
10+
//! These constructions preserve zero-knowledge properties and follow standard Sigma protocol composition techniques.
11+
112
use crate::toolbox::sigma::{SigmaProtocol, SigmaProtocolSimulator};
213
use rand::{Rng, CryptoRng};
314
use ff::PrimeField;
415

5-
16+
/// Logical AND composition of two Sigma protocols.
17+
///
18+
/// The prover must know witnesses for both subprotocols `P` and `Q`.
19+
///
20+
/// # Example
21+
/// Proves that two independent statements hold simultaneously.
622
pub struct AndProtocol<P, Q>
723
where
824
P: SigmaProtocol,
@@ -17,9 +33,10 @@ where
1733
P: SigmaProtocol,
1834
Q: SigmaProtocol
1935
{
20-
pub fn new(protocol0: P, protocol1: Q) -> Self {
21-
Self {protocol0, protocol1}
22-
}
36+
/// Create a new `AndProtocol` from two Sigma protocols.
37+
pub fn new(protocol0: P, protocol1: Q) -> Self {
38+
Self {protocol0, protocol1}
39+
}
2340
}
2441

2542
impl<P, Q> SigmaProtocol for AndProtocol<P, Q>
@@ -72,6 +89,10 @@ where
7289
}
7390
}
7491

92+
/// Logical OR composition of two Sigma protocols.
93+
///
94+
/// The prover knows a witness for **one** subprotocol `P` *or* `Q`,
95+
/// but does not reveal which. This uses simulation to hide the other statement.
7596
pub struct OrProtocol<P, Q>
7697
where
7798
P: SigmaProtocol,
@@ -86,18 +107,23 @@ where
86107
P: SigmaProtocol,
87108
Q: SigmaProtocol
88109
{
89-
pub fn new(protocol0: P, protocol1: Q) -> Self {
90-
Self {protocol0, protocol1}
91-
}
110+
/// Create a new `OrProtocol` from two Sigma protocols.
111+
pub fn new(protocol0: P, protocol1: Q) -> Self {
112+
Self {protocol0, protocol1}
113+
}
92114
}
93115

116+
/// Enum to wrap either the left or right variant in an OR proof.
94117
pub enum OrEnum<L, R> {
95118
Left(L),
96119
Right(R),
97120
}
98121

122+
/// Internal state for a simulated transcript in an OR proof.
99123
pub struct OrState<P: SigmaProtocol> (P::Challenge, P::Response);
100124

125+
126+
/// Enum to describe which side (left or right) is simulated in an OR proof.
101127
pub enum OrTranscription<P, Q>
102128
where
103129
P: SigmaProtocol,
@@ -107,6 +133,7 @@ where
107133
Right(OrState<Q>)
108134
}
109135

136+
110137
impl<P, Q, C> SigmaProtocol for OrProtocol<P, Q>
111138
where
112139
C: PrimeField,
@@ -116,10 +143,9 @@ where
116143
Q::Response: Clone,
117144
{
118145
type Commitment = (P::Commitment, Q::Commitment);
119-
// Here ProverState = (real index, real prover state = (r, &real witness), fake transcription)
120-
type ProverState = (usize, OrEnum<P::ProverState, Q::ProverState>, OrTranscription<P, Q>);
121-
type Response = (P::Challenge, P::Response, Q::Response); // The two responses
122-
type Witness = (usize, OrEnum<P::Witness, Q::Witness>); // Index of the witness and witness
146+
type ProverState = (usize, OrEnum<P::ProverState, Q::ProverState>, OrTranscription<P, Q>); // ProverState = (real index, real prover state = (r, &real witness), fake transcription)
147+
type Response = (P::Challenge, P::Response, Q::Response);
148+
type Witness = (usize, OrEnum<P::Witness, Q::Witness>); // Index of the real witness, and Enum to wrap the real witness
123149
type Challenge = P::Challenge;
124150

125151
fn prover_commit(

0 commit comments

Comments
 (0)