Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 60 additions & 60 deletions src/composition.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
//! Implementation of a structure [`Protocol`] aimed at generalizing the [`SchnorrProof`]
//! using the compositions of the latter via AND and OR links
//! # Protocol Composition with AND/OR Logic
//!
//! This structure allows, for example, the construction of protocols of the form:
//! This module defines the [`Protocol`] enum, which generalizes the [`SchnorrProof`]
//! by enabling compositional logic between multiple proof instances.
//!
//! Specifically, it supports:
//! - Simple atomic proofs (e.g., discrete logarithm, Pedersen commitments)
//! - Conjunctions (`And`) of multiple sub-protocols
//! - Disjunctions (`Or`) of multiple sub-protocols
//!
//! ## Example Composition
//!
//! ```ignore
//! And(
//! Or( dleq, pedersen_commitment ),
//! Simple( discrete_logarithm ),
//! And( pedersen_commitment_dleq, bbs_blind_commitment_computation )
//! )
//! ```

use ff::{Field, PrimeField};
use group::{Group, GroupEncoding};
Expand Down Expand Up @@ -104,11 +114,12 @@ impl<G: Group + GroupEncoding> SigmaProtocol for Protocol<G> {
) -> Result<(Self::Commitment, Self::ProverState), Error> {
match (self, witness) {
(Protocol::Simple(p), ProtocolWitness::Simple(w)) => {
let (c, s) = p.prover_commit(w, rng)?;
Ok((
ProtocolCommitment::Simple(c),
ProtocolProverState::Simple(s),
))
p.prover_commit(w, rng).map(|(c, s)| {
(
ProtocolCommitment::Simple(c),
ProtocolProverState::Simple(s),
)
})
}
(Protocol::And(ps), ProtocolWitness::And(ws)) => {
if ps.len() != ws.len() {
Expand Down Expand Up @@ -162,20 +173,20 @@ impl<G: Group + GroupEncoding> SigmaProtocol for Protocol<G> {
challenge: &Self::Challenge,
) -> Result<Self::Response, Error> {
match (self, state) {
(Protocol::Simple(p), ProtocolProverState::Simple(state)) => {
let response = p.prover_response(state, challenge)?;
Ok(ProtocolResponse::Simple(response))
}
(Protocol::Simple(p), ProtocolProverState::Simple(state)) => p
.prover_response(state, challenge)
.map(ProtocolResponse::Simple),
(Protocol::And(ps), ProtocolProverState::And(states)) => {
if ps.len() != states.len() {
return Err(Error::ProofSizeMismatch);
}
let mut responses = Vec::with_capacity(ps.len());
for (i, p) in ps.iter().enumerate() {
let r = p.prover_response(states[i].clone(), challenge)?;
responses.push(r);
}
Ok(ProtocolResponse::And(responses))
let responses: Result<Vec<_>, _> = ps
.iter()
.zip(states)
.map(|(p, s)| p.prover_response(s, challenge))
.collect();

Ok(ProtocolResponse::And(responses?))
}
(
Protocol::Or(ps),
Expand Down Expand Up @@ -225,12 +236,11 @@ impl<G: Group + GroupEncoding> SigmaProtocol for Protocol<G> {
Protocol::And(ps),
ProtocolCommitment::And(commitments),
ProtocolResponse::And(responses),
) => {
for (i, p) in ps.iter().enumerate() {
p.verifier(&commitments[i], challenge, &responses[i])?;
}
Ok(())
}
) => ps
.iter()
.zip(commitments)
.zip(responses)
.try_for_each(|((p, c), r)| p.verifier(c, challenge, r)),
(
Protocol::Or(ps),
ProtocolCommitment::Or(commitments),
Expand All @@ -253,20 +263,12 @@ impl<G: Group + GroupEncoding> SigmaProtocol for Protocol<G> {
fn serialize_commitment(&self, commitment: &Self::Commitment) -> Vec<u8> {
match (self, commitment) {
(Protocol::Simple(p), ProtocolCommitment::Simple(c)) => p.serialize_commitment(c),
(Protocol::And(ps), ProtocolCommitment::And(commitments)) => {
let mut bytes = Vec::new();
for (i, p) in ps.iter().enumerate() {
bytes.extend(p.serialize_commitment(&commitments[i]));
}
bytes
}
(Protocol::Or(ps), ProtocolCommitment::Or(commitments)) => {
let mut bytes = Vec::new();
for (i, p) in ps.iter().enumerate() {
bytes.extend(p.serialize_commitment(&commitments[i]));
}
bytes
}
(Protocol::And(ps), ProtocolCommitment::And(commitments))
| (Protocol::Or(ps), ProtocolCommitment::Or(commitments)) => ps
.iter()
.zip(commitments)
.flat_map(|(p, c)| p.serialize_commitment(c))
.collect(),
_ => panic!(),
}
}
Expand Down Expand Up @@ -354,27 +356,22 @@ impl<G: Group + GroupEncoding> SigmaProtocol for Protocol<G> {
let c = p.deserialize_commitment(data)?;
Ok(ProtocolCommitment::Simple(c))
}
Protocol::And(ps) => {
let mut cursor = 0;
let mut commitments = Vec::with_capacity(ps.len());
for p in ps {
let c = p.deserialize_commitment(&data[cursor..])?;
let size = p.serialize_commitment(&c).len();
cursor += size;
commitments.push(c);
}
Ok(ProtocolCommitment::And(commitments))
}
Protocol::Or(ps) => {
Protocol::And(ps) | Protocol::Or(ps) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The calculation is done in the same way in the cases And and Or

let mut cursor = 0;
let mut commitments = Vec::with_capacity(ps.len());

for p in ps {
let c = p.deserialize_commitment(&data[cursor..])?;
let size = p.serialize_commitment(&c).len();
cursor += size;
commitments.push(c);
}
Ok(ProtocolCommitment::Or(commitments))

Ok(match self {
Protocol::And(_) => ProtocolCommitment::And(commitments),
Protocol::Or(_) => ProtocolCommitment::Or(commitments),
_ => unreachable!(),
})
}
}
}
Expand Down Expand Up @@ -436,17 +433,20 @@ impl<G: Group + GroupEncoding> SigmaProtocolSimulator for Protocol<G> {
p.simulate_commitment(challenge, r)?,
)),
(Protocol::And(ps), ProtocolResponse::And(rs)) => {
let mut commitments = Vec::with_capacity(ps.len());
for (i, p) in ps.iter().enumerate() {
commitments.push(p.simulate_commitment(challenge, &rs[i])?);
}
let commitments = ps
.iter()
.zip(rs)
.map(|(p, r)| p.simulate_commitment(challenge, r))
.collect::<Result<Vec<_>, _>>()?;
Ok(ProtocolCommitment::And(commitments))
}
(Protocol::Or(ps), ProtocolResponse::Or(ch, rs)) => {
let mut commitments = Vec::with_capacity(ps.len());
for (i, p) in ps.iter().enumerate() {
commitments.push(p.simulate_commitment(&ch[i], &rs[i])?);
}
(Protocol::Or(ps), ProtocolResponse::Or(challenges, rs)) => {
let commitments = ps
.iter()
.zip(challenges)
.zip(rs)
.map(|((p, ch), r)| p.simulate_commitment(ch, r))
.collect::<Result<Vec<_>, _>>()?;
Ok(ProtocolCommitment::Or(commitments))
}
_ => panic!(),
Expand Down
7 changes: 5 additions & 2 deletions src/duplex_sponge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! This module defines the [`DuplexSpongeInterface`] trait, which provides
//! a generic interface for cryptographic sponge functions that support
//! duplex operation (absorb and squeeze phases).
//! duplex operations: alternating absorb and squeeze phases.

pub mod keccak;
pub mod shake;
Expand All @@ -15,7 +15,9 @@ pub mod shake;
///
/// This is the core primitive used for building cryptographic codecs.
pub trait DuplexSpongeInterface {
/// Creates a new sponge instance with an initialization vector.
/// Creates a new sponge instance with a given initialization vector (IV).
///
/// The IV enables domain separation and reproducibility between parties.
fn new(iv: [u8; 32]) -> Self;

/// Absorbs input data into the sponge state.
Expand All @@ -24,5 +26,6 @@ pub trait DuplexSpongeInterface {
/// Squeezes output data from the sponge state.
fn squeeze(&mut self, length: usize) -> Vec<u8>;

/// Applies a state ratcheting mechanism to prevent backtracking attacks.
fn ratchet(&mut self);
}
27 changes: 17 additions & 10 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
//! # Error: Error Types for Zero-Knowledge Proofs.
//!
//! This module defines the [`Error`] enum, which encapsulates possible errors that may occur
//! during the execution of Sigma protocols or their non-interactive variants.
//! This module defines the [`Error`] enum, which enumerates the possible failure modes
//! encountered during the execution of interactive or non-interactive Sigma protocols.
//!
//! These errors include:
//! - Verification failures (e.g., when a proof does not verify correctly).
//! - Mismatched parameters during batch verification.
//! - Uninitialized group variables.
//! - Failed proof verification,
//! - Mismatched parameter lengths (e.g., during batch verification),
//! - Access to unassigned group variables in constraint systems.
/// An error during proving or verification, such as a verification failure.
/// Represents an error encountered during the execution of a Sigma protocol.
///
/// This may occur during proof generation, response computation, or verification.
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Something is wrong with the proof, causing a verification failure.
/// The proof is invalid: verification failed.
#[error("Verification failed.")]
VerificationFailure,
/// Indicates a mismatch in parameter sizes during batch verification.
#[error("Mismatched parameter sizes for batch verification.")]

/// The sizes of input parameters (e.g., witnesses, commitments) do not match expected values.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error can occur in different contexts (always being due to a size problem)

#[error("Mismatched parameter sizes in proof or batch verification.")]
ProofSizeMismatch,

/// Uninitialized group element variable.
#[error("Uninitialized group element variable: {var_debug}")]
UnassignedGroupVar { var_debug: String },
UnassignedGroupVar {
/// Debug representation of the unassigned variable.
var_debug: String,
},
}
19 changes: 16 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
//! # Σ-rs
//! # Σ-rs: Sigma Protocols in Rust
//!
//! A Rust library for building zero-knowledge proofs using Sigma protocols (Σ-protocols).
//! Create proofs that demonstrate knowledge of secret information without revealing it.
//! **Σ-rs** is a Rust library for constructing zero-knowledge proofs using Sigma protocols (Σ-protocols).
//! It allows proving knowledge of secret data without revealing the data itself.
//!
//! ---
//!
//! ## What are Sigma Protocols?
//!
//! Sigma protocols are interactive cryptographic protocols that allow a prover to convince
//! a verifier they know a secret (like a private key) without revealing the secret itself.
//! They follow a simple three-step pattern: commitment, challenge, response.
//!
//! ---
//!
//! ## Key Features
//!
//! - **Composable**: Combine multiple proofs into compound statements
//! - **Generic**: Works with any cryptographic group supporting the required operations
//! - **Flexible Hashing**: Multiple hash function backends for different use cases
//! - **Non-Interactive Ready**: Support for Fiat–Shamir transformation
//!
//! ---
//!
//! ## Basic Usage
//!
Expand All @@ -24,13 +31,19 @@
//! 3. Convert to non-interactive using [`fiat_shamir::NISigmaProtocol`]
//! 4. Generate and verify proofs using the protocol interface
//!
//! ---
//!
//! ## Core Components
//!
//! - **[`traits::SigmaProtocol`]**: The fundamental three-move protocol interface
//! - **[`linear_relation::LinearRelation`]**: Express mathematical relations over groups
//! - **[`fiat_shamir::NISigmaProtocol`]**: Convert interactive proofs to standalone proofs
//! - **[`composition::Protocol`]**: Combine multiple proofs together
//! - **[`codec`]**: Hash function backends for proof generation
//!
//! ---
//!
//! Σ-rs is designed to be modular, extensible, and easy to integrate into zero-knowledge applications.

#![allow(non_snake_case)]
#![doc(html_logo_url = "https://mmaker.github.io/sigma-rs/")]
Expand Down
Loading
Loading