sigma_protocols/protocols/
dleq.rs

1//! Discrete Logarithm Equality (DLEQ) proof.
2//!
3//! Proves that two points share the same discrete logarithm with respect to different bases.
4//! Specifically, proves knowledge of α such that X = α * G and Y = α * H.
5
6use curve25519_dalek::ristretto::RistrettoPoint;
7use curve25519_dalek::scalar::Scalar;
8use rand::rngs::OsRng;
9use rand::TryRngCore;
10
11use crate::error::{Error, Result};
12use crate::sigma::{MultiPointCommitment, ScalarChallenge, ScalarResponse, SigmaProtocol};
13
14/// Public statement for DLEQ proof.
15///
16/// Contains two base points (g, h) and two result points (x, y).
17#[derive(Clone, Debug)]
18pub struct DLEQStatement {
19    pub g: RistrettoPoint,
20    pub h: RistrettoPoint,
21    pub x: RistrettoPoint,
22    pub y: RistrettoPoint,
23}
24
25/// Private witness for DLEQ proof: the common discrete logarithm.
26#[derive(Clone, Debug)]
27pub struct DLEQWitness {
28    pub alpha: Scalar,
29}
30
31/// DLEQ proof implementation.
32///
33/// Proves that log_g(x) = log_h(y) without revealing the common logarithm.
34pub struct DLEQProof;
35
36impl SigmaProtocol for DLEQProof {
37    type Statement = DLEQStatement;
38    type Witness = DLEQWitness;
39    type Commitment = MultiPointCommitment;
40    type Challenge = ScalarChallenge;
41    type Response = ScalarResponse;
42
43    fn prover_commit(
44        statement: &Self::Statement,
45        _witness: &Self::Witness,
46    ) -> (Self::Commitment, Vec<u8>) {
47        let mut r_bytes = [0u8; 32];
48        OsRng
49            .try_fill_bytes(&mut r_bytes)
50            .expect("Failed to generate random bytes");
51        let r = Scalar::from_bytes_mod_order(r_bytes);
52
53        let a = r * statement.g;
54        let b = r * statement.h;
55
56        let state = r.to_bytes().to_vec();
57
58        (MultiPointCommitment(vec![a, b]), state)
59    }
60
61    fn prover_response(
62        _statement: &Self::Statement,
63        witness: &Self::Witness,
64        state: &[u8],
65        challenge: &Self::Challenge,
66    ) -> Result<Self::Response> {
67        if state.len() != 32 {
68            return Err(Error::InvalidProof);
69        }
70
71        let mut r_bytes = [0u8; 32];
72        r_bytes.copy_from_slice(state);
73        let r_option = Scalar::from_canonical_bytes(r_bytes);
74
75        let r = if r_option.is_some().unwrap_u8() == 1 {
76            r_option.unwrap()
77        } else {
78            return Err(Error::InvalidScalar);
79        };
80
81        let response = r + challenge.0 * witness.alpha;
82
83        Ok(ScalarResponse(response))
84    }
85
86    fn verifier(
87        statement: &Self::Statement,
88        commitment: &Self::Commitment,
89        challenge: &Self::Challenge,
90        response: &Self::Response,
91    ) -> Result<()> {
92        if commitment.0.len() != 2 {
93            return Err(Error::InvalidCommitment);
94        }
95
96        let a = commitment.0[0];
97        let b = commitment.0[1];
98
99        let lhs_g = response.0 * statement.g;
100        let rhs_g = a + challenge.0 * statement.x;
101
102        let lhs_h = response.0 * statement.h;
103        let rhs_h = b + challenge.0 * statement.y;
104
105        if lhs_g == rhs_g && lhs_h == rhs_h {
106            Ok(())
107        } else {
108            Err(Error::InvalidProof)
109        }
110    }
111}