sigma-protocols 0.5.0

SIGMA zero-knowledge proof protocols
Documentation
//! Schnorr proof of knowledge of discrete logarithm.
//!
//! Proves knowledge of x such that X = x * G for a public point X.

use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::scalar::Scalar;
use rand::rngs::OsRng;
use rand::TryRngCore;

use crate::error::{Error, Result};
use crate::sigma::{PointCommitment, ScalarChallenge, ScalarResponse, SigmaProtocol};

/// Public statement for Schnorr proof: the public key.
#[derive(Clone, Debug)]
pub struct SchnorrStatement {
    pub public_key: RistrettoPoint,
}

/// Private witness for Schnorr proof: the secret key.
#[derive(Clone, Debug)]
pub struct SchnorrWitness {
    pub secret_key: Scalar,
}

/// Schnorr proof implementation.
///
/// Proves knowledge of discrete logarithm x such that X = x * G.
pub struct SchnorrProof;

impl SigmaProtocol for SchnorrProof {
    type Statement = SchnorrStatement;
    type Witness = SchnorrWitness;
    type Commitment = PointCommitment;
    type Challenge = ScalarChallenge;
    type Response = ScalarResponse;

    fn prover_commit(
        _statement: &Self::Statement,
        _witness: &Self::Witness,
    ) -> (Self::Commitment, Vec<u8>) {
        let mut r_bytes = [0u8; 32];
        OsRng
            .try_fill_bytes(&mut r_bytes)
            .expect("Failed to generate random bytes");
        let r = Scalar::from_bytes_mod_order(r_bytes);

        let commitment = r * RISTRETTO_BASEPOINT_POINT;

        let state = r.to_bytes().to_vec();

        (PointCommitment(commitment), state)
    }

    fn prover_response(
        _statement: &Self::Statement,
        witness: &Self::Witness,
        state: &[u8],
        challenge: &Self::Challenge,
    ) -> Result<Self::Response> {
        if state.len() != 32 {
            return Err(Error::InvalidProof);
        }

        let mut r_bytes = [0u8; 32];
        r_bytes.copy_from_slice(state);
        let r_option = Scalar::from_canonical_bytes(r_bytes);

        let r = if r_option.is_some().unwrap_u8() == 1 {
            r_option.unwrap()
        } else {
            return Err(Error::InvalidScalar);
        };

        let response = r + challenge.0 * witness.secret_key;

        Ok(ScalarResponse(response))
    }

    fn verifier(
        statement: &Self::Statement,
        commitment: &Self::Commitment,
        challenge: &Self::Challenge,
        response: &Self::Response,
    ) -> Result<()> {
        let lhs = response.0 * RISTRETTO_BASEPOINT_POINT;

        let rhs = commitment.0 + challenge.0 * statement.public_key;

        if lhs == rhs {
            Ok(())
        } else {
            Err(Error::InvalidProof)
        }
    }
}