sigma-protocols 0.5.1

SIGMA zero-knowledge proof protocols
Documentation
//! Core traits and types for SIGMA protocols.
//!
//! This module defines the fundamental traits and implementations for
//! zero-knowledge proof protocols following the SIGMA protocol structure.

use crate::error::{Error, Result};
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar;

/// Trait for protocol commitments (first message in SIGMA protocol).
pub trait Commitment: Clone + Send + Sync {
    fn to_bytes(&self) -> Vec<u8>;
    fn from_bytes(bytes: &[u8]) -> Result<Self>
    where
        Self: Sized;
}

/// Trait for protocol challenges (second message in SIGMA protocol).
pub trait Challenge: Clone + Send + Sync {
    fn to_bytes(&self) -> Vec<u8>;
    fn from_bytes(bytes: &[u8]) -> Result<Self>
    where
        Self: Sized;
}

/// Trait for protocol responses (third message in SIGMA protocol).
pub trait Response: Clone + Send + Sync {
    fn to_bytes(&self) -> Vec<u8>;
    fn from_bytes(bytes: &[u8]) -> Result<Self>
    where
        Self: Sized;
}

/// Core trait for SIGMA protocols.
///
/// A SIGMA protocol is a three-round interactive proof system with:
/// 1. Commitment: Prover sends initial commitment
/// 2. Challenge: Verifier sends random challenge
/// 3. Response: Prover responds to challenge
pub trait SigmaProtocol {
    /// The public statement to be proven.
    type Statement: Clone + Send + Sync;
    /// The private witness known only to the prover.
    type Witness: Clone + Send + Sync;
    /// The commitment message type.
    type Commitment: Commitment;
    /// The challenge message type.
    type Challenge: Challenge;
    /// The response message type.
    type Response: Response;

    /// Generate the prover's initial commitment.
    ///
    /// Returns the commitment and internal state for later use.
    fn prover_commit(
        statement: &Self::Statement,
        witness: &Self::Witness,
    ) -> (Self::Commitment, Vec<u8>);

    /// Generate the prover's response to a challenge.
    ///
    /// Uses the internal state from commitment phase.
    fn prover_response(
        statement: &Self::Statement,
        witness: &Self::Witness,
        state: &[u8],
        challenge: &Self::Challenge,
    ) -> Result<Self::Response>;

    /// Verify a proof transcript.
    ///
    /// Returns `Ok(())` if the proof is valid, `Err` otherwise.
    fn verifier(
        statement: &Self::Statement,
        commitment: &Self::Commitment,
        challenge: &Self::Challenge,
        response: &Self::Response,
    ) -> Result<()>;
}

/// Helper function to deserialize a scalar from bytes
fn scalar_from_bytes(bytes: &[u8]) -> Result<Scalar> {
    if bytes.len() != 32 {
        return Err(Error::InvalidScalar);
    }
    let mut array = [0u8; 32];
    array.copy_from_slice(bytes);

    Scalar::from_canonical_bytes(array)
        .into_option()
        .ok_or(Error::InvalidScalar)
}

/// Helper function to deserialize a point from bytes
fn point_from_bytes(bytes: &[u8]) -> Result<RistrettoPoint> {
    if bytes.len() != 32 {
        return Err(Error::InvalidPoint);
    }
    let mut array = [0u8; 32];
    array.copy_from_slice(bytes);

    CompressedRistretto::from_slice(&array)
        .map_err(|_| Error::InvalidPoint)?
        .decompress()
        .ok_or(Error::InvalidPoint)
}

/// A challenge consisting of a single scalar value.
#[derive(Clone, Debug)]
pub struct ScalarChallenge(pub Scalar);

impl Challenge for ScalarChallenge {
    fn to_bytes(&self) -> Vec<u8> {
        self.0.to_bytes().to_vec()
    }

    fn from_bytes(bytes: &[u8]) -> Result<Self> {
        if bytes.len() != 32 {
            return Err(Error::InvalidChallenge);
        }
        let mut array = [0u8; 32];
        array.copy_from_slice(bytes);
        // Use from_bytes_mod_order for challenge generation (non-canonical is OK for challenges)
        Ok(ScalarChallenge(Scalar::from_bytes_mod_order(array)))
    }
}

/// A commitment consisting of a single elliptic curve point.
#[derive(Clone, Debug)]
pub struct PointCommitment(pub RistrettoPoint);

impl Commitment for PointCommitment {
    fn to_bytes(&self) -> Vec<u8> {
        self.0.compress().to_bytes().to_vec()
    }

    fn from_bytes(bytes: &[u8]) -> Result<Self> {
        point_from_bytes(bytes)
            .map(PointCommitment)
            .map_err(|_| Error::InvalidCommitment)
    }
}

/// A response consisting of a single scalar value.
#[derive(Clone, Debug)]
pub struct ScalarResponse(pub Scalar);

impl Response for ScalarResponse {
    fn to_bytes(&self) -> Vec<u8> {
        self.0.to_bytes().to_vec()
    }

    fn from_bytes(bytes: &[u8]) -> Result<Self> {
        scalar_from_bytes(bytes)
            .map(ScalarResponse)
            .map_err(|_| Error::InvalidResponse)
    }
}

/// A commitment consisting of multiple elliptic curve points.
#[derive(Clone, Debug)]
pub struct MultiPointCommitment(pub Vec<RistrettoPoint>);

impl Commitment for MultiPointCommitment {
    fn to_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(self.0.len() * 32 + 4);
        bytes.extend_from_slice(&(self.0.len() as u32).to_le_bytes());

        for point in &self.0 {
            bytes.extend_from_slice(&point.compress().to_bytes());
        }

        bytes
    }

    fn from_bytes(bytes: &[u8]) -> Result<Self> {
        if bytes.len() < 4 {
            return Err(Error::InvalidCommitment);
        }

        let len = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as usize;

        if bytes.len() != 4 + len * 32 {
            return Err(Error::InvalidCommitment);
        }

        let mut points = Vec::with_capacity(len);
        for i in 0..len {
            let start = 4 + i * 32;
            let end = start + 32;
            points.push(point_from_bytes(&bytes[start..end])?)
        }

        Ok(MultiPointCommitment(points))
    }
}

/// A response consisting of multiple scalar values.
#[derive(Clone, Debug)]
pub struct MultiScalarResponse(pub Vec<Scalar>);

impl Response for MultiScalarResponse {
    fn to_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(self.0.len() * 32 + 4);
        bytes.extend_from_slice(&(self.0.len() as u32).to_le_bytes());

        for scalar in &self.0 {
            bytes.extend_from_slice(&scalar.to_bytes());
        }

        bytes
    }

    fn from_bytes(bytes: &[u8]) -> Result<Self> {
        if bytes.len() < 4 {
            return Err(Error::InvalidResponse);
        }

        let len = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as usize;

        if bytes.len() != 4 + len * 32 {
            return Err(Error::InvalidResponse);
        }

        let mut scalars = Vec::with_capacity(len);
        for i in 0..len {
            let start = 4 + i * 32;
            let end = start + 32;
            scalars.push(scalar_from_bytes(&bytes[start..end])?)
        }

        Ok(MultiScalarResponse(scalars))
    }
}