concordium_base/sigma_protocols/
com_eq.rs

1//! The module provides the implementation of the `com-dlog-eq` sigma protocol
2//! for the special case of one commitment (cf. "Proof of Equality for
3//! Aggregated Discrete Logarithms and Commitments" Section 9.2.5, Bluepaper
4//! v1.2.5). This protocol enables one to prove knowledge of discrete logarithm
5//! $a_1$ together with randomnesses $r_1$ corresponding to the public value $ y
6//! = \prod G_i^{a_i} $ and the commitment $C = commit(a_1, r_1)$. The product y
7//! and commitments can be in different groups, but they have to be of the same
8//! prime order, and for the implementation the field of scalars must be the
9//! same type for both groups.
10
11use super::common::*;
12use crate::{
13    common::*,
14    curve_arithmetic::{multiexp, Curve, Field},
15    pedersen_commitment::{Commitment, CommitmentKey, Randomness, Value},
16    random_oracle::RandomOracle,
17};
18
19#[derive(Clone, Debug, Eq, PartialEq, Serialize, SerdeBase16Serialize)]
20pub struct Response<T: Curve> {
21    /// The pair $(s, t)$ where
22    /// * $s = \alpha - c a$
23    /// * $t = R - c r$
24    ///
25    /// where $c$ is the challenge and $\alpha$ and $R$ are prover chosen
26    /// random scalars.
27    pub response: (T::Scalar, T::Scalar),
28}
29
30#[derive(Debug, Serialize)]
31pub struct CommittedMessages<C: Curve, D: Curve> {
32    pub u: C,
33    pub v: Commitment<D>,
34}
35
36pub struct ComEq<C: Curve, D: Curve<Scalar = C::Scalar>> {
37    /// The list of commitments.
38    pub commitment: Commitment<D>,
39    /// The evaluation $y$ (see above for notation).
40    pub y:          C,
41    /// The commitment key with which all the commitments are
42    /// generated
43    pub cmm_key:    CommitmentKey<D>,
44    /// The generator for discrete log.
45    pub g:          C,
46}
47
48pub struct ComEqSecret<C: Curve> {
49    pub r: Randomness<C>,
50    pub a: Value<C>,
51}
52
53#[allow(non_snake_case)]
54impl<C: Curve, D: Curve<Scalar = C::Scalar>> SigmaProtocol for ComEq<C, D> {
55    type CommitMessage = CommittedMessages<C, D>;
56    type ProtocolChallenge = C::Scalar;
57    // Vector of pairs (alpha_i, R_i).
58    type ProverState = (Value<D>, Randomness<D>);
59    type Response = Response<C>;
60    type SecretData = ComEqSecret<D>;
61
62    fn public(&self, ro: &mut RandomOracle) {
63        ro.append_message("commitment", &self.commitment);
64        ro.append_message("y", &self.y);
65        ro.append_message("cmm_key", &self.cmm_key);
66        ro.append_message("g", &self.g)
67    }
68
69    fn compute_commit_message<R: rand::Rng>(
70        &self,
71        csprng: &mut R,
72    ) -> Option<(Self::CommitMessage, Self::ProverState)> {
73        let mut u = C::zero_point();
74
75        let alpha = Value::<D>::generate_non_zero(csprng);
76        // This cR_i is R_i from the specification.
77        let (v, cR) = self.cmm_key.commit(&alpha, csprng);
78        u = u.plus_point(&self.g.mul_by_scalar(&alpha));
79        Some((CommittedMessages { u, v }, (alpha, cR)))
80    }
81
82    fn get_challenge(
83        &self,
84        challenge: &crate::random_oracle::Challenge,
85    ) -> Self::ProtocolChallenge {
86        C::scalar_from_bytes(challenge)
87    }
88
89    fn compute_response(
90        &self,
91        secret: Self::SecretData,
92        state: Self::ProverState,
93        challenge: &Self::ProtocolChallenge,
94    ) -> Option<Self::Response> {
95        let (ref alpha, ref cR) = state;
96        // compute alpha_i - a_i * c
97        let mut s = *challenge;
98        s.mul_assign(&secret.a);
99        s.negate();
100        s.add_assign(alpha);
101        // compute R_i - r_i * c
102        let mut t: C::Scalar = *challenge;
103        t.mul_assign(&secret.r);
104        t.negate();
105        t.add_assign(cR);
106        Some(Response { response: (s, t) })
107    }
108
109    fn extract_commit_message(
110        &self,
111        challenge: &Self::ProtocolChallenge,
112        response: &Self::Response,
113    ) -> Option<Self::CommitMessage> {
114        // let mut u = self.y.mul_by_scalar(challenge);
115        // FIXME: Could benefit from multiexponentiation
116        // u = u.plus_point(&self.g.mul_by_scalar(&response.response.0));
117
118        let u = multiexp(&[self.y, self.g], &[*challenge, response.response.0]);
119
120        let v = self.commitment.mul_by_scalar(challenge).plus_point(
121            &self
122                .cmm_key
123                .hide_worker(&response.response.0, &response.response.1),
124        );
125        Some(CommittedMessages {
126            u,
127            v: Commitment(v),
128        })
129    }
130
131    #[cfg(test)]
132    #[allow(clippy::many_single_char_names)]
133    fn with_valid_data<R: rand::Rng>(
134        _data_size: usize,
135        csprng: &mut R,
136        f: impl FnOnce(Self, Self::SecretData, &mut R),
137    ) {
138        let comm_key = CommitmentKey::generate(csprng);
139        let a = Value::<D>::generate_non_zero(csprng);
140        let (c, randomness) = comm_key.commit(&a, csprng);
141        let g = C::generate(csprng);
142        let mut y = C::zero_point();
143        y = y.plus_point(&g.mul_by_scalar(&a));
144        let com_eq = ComEq {
145            commitment: c,
146            y,
147            cmm_key: comm_key,
148            g,
149        };
150        let secret = ComEqSecret { r: randomness, a };
151        f(com_eq, secret, csprng)
152    }
153}
154
155#[cfg(test)]
156mod test {
157    use crate::curve_arithmetic::arkworks_instances::ArkGroup;
158
159    use super::*;
160    use ark_bls12_381::{G1Projective, G2Projective};
161
162    type G1 = ArkGroup<G1Projective>;
163    type G2 = ArkGroup<G2Projective>;
164
165    #[test]
166    pub fn test_com_eq_correctness() {
167        let mut csprng = rand::thread_rng();
168        for _i in 1..20 {
169            ComEq::<G1, G2>::with_valid_data(0, &mut csprng, |com_eq, secret, csprng| {
170                let challenge_prefix = generate_challenge_prefix(csprng);
171                let mut ro = RandomOracle::domain(challenge_prefix);
172                let proof = prove(&mut ro.split(), &com_eq, secret, csprng)
173                    .expect("Proving should succeed.");
174                let res = verify(&mut ro, &com_eq, &proof);
175                assert!(res, "Verification of produced proof.");
176            })
177        }
178    }
179
180    #[test]
181    pub fn test_com_eq_soundness() {
182        let mut csprng = rand::thread_rng();
183        for i in 1..20 {
184            ComEq::<G1, G2>::with_valid_data(i, &mut csprng, |com_eq, secret, csprng| {
185                let challenge_prefix = generate_challenge_prefix(csprng);
186                let ro = RandomOracle::domain(challenge_prefix);
187                let proof = prove(&mut ro.split(), &com_eq, secret, csprng)
188                    .expect("Proving should succeed.");
189
190                let mut wrong_ro = RandomOracle::domain(generate_challenge_prefix(csprng));
191                if verify(&mut wrong_ro, &com_eq, &proof) {
192                    assert_eq!(wrong_ro, ro);
193                }
194                let mut wrong_com_eq = com_eq;
195                {
196                    let tmp = wrong_com_eq.commitment;
197                    let v = Value::<G1>::generate(csprng);
198                    wrong_com_eq.commitment = wrong_com_eq.cmm_key.commit(&v, csprng).0;
199                    assert!(!verify(&mut ro.split(), &wrong_com_eq, &proof));
200                    wrong_com_eq.commitment = tmp;
201                }
202
203                {
204                    let tmp = wrong_com_eq.y;
205                    wrong_com_eq.y = G1::generate(csprng);
206                    assert!(!verify(&mut ro.split(), &wrong_com_eq, &proof));
207                    wrong_com_eq.y = tmp;
208                }
209
210                {
211                    let tmp = wrong_com_eq.cmm_key;
212                    wrong_com_eq.cmm_key = CommitmentKey::generate(csprng);
213                    assert!(!verify(&mut ro.split(), &wrong_com_eq, &proof));
214                    wrong_com_eq.cmm_key = tmp;
215                }
216
217                {
218                    let tmp = wrong_com_eq.g;
219                    wrong_com_eq.g = G1::generate(csprng);
220                    assert!(!verify(&mut ro.split(), &wrong_com_eq, &proof));
221                    wrong_com_eq.g = tmp;
222                }
223            })
224        }
225    }
226}