commit_groth09/
lib.rs

1//! [Homomorphic Trapdoor Commitments to Group Elements](https://eprint.iacr.org/2009/007.pdf) as
2//! described by Jens Groth.
3//!
4//! This implementation uses [BLS12-381](https://docs.rs/bls12_381) for the groups and pairing.
5
6use bls12_381::{pairing, G1Affine, G2Affine, G2Projective, Gt, Scalar};
7use ff::Field;
8use rand::prelude::*;
9use std::iter::zip;
10use std::ops::{Add, Mul};
11
12pub struct Values<const N: usize> {
13    values: [G2Affine; N],
14}
15
16impl<const N: usize> Values<N> {
17    pub fn new(values: [G2Affine; N]) -> Self {
18        Values { values }
19    }
20
21    #[cfg(test)]
22    pub(crate) fn random() -> Self {
23        use group::Group;
24
25        let mut values = Vec::with_capacity(N);
26        for _ in 0..N {
27            values.push(G2Affine::from(G2Projective::random(thread_rng())));
28        }
29        Values {
30            values: values.try_into().unwrap(),
31        }
32    }
33
34    pub fn from_bytes(_bytes: &[u8]) -> Self {
35        todo!("implement converting to and from bytes")
36    }
37
38    pub fn to_bytes(&self) -> Vec<u8> {
39        todo!("implement converting to and from bytes")
40    }
41}
42
43#[allow(clippy::suspicious_arithmetic_impl)]
44impl<const N: usize> Mul for &Values<N> {
45    type Output = Values<N>;
46
47    fn mul(self, rhs: Self) -> Self::Output {
48        let mut values = Vec::with_capacity(N);
49        for i in 0..N {
50            let v = G2Affine::from(self.values[i] + G2Projective::from(rhs.values[i]));
51            values.push(v);
52        }
53        Values {
54            values: values.try_into().unwrap(),
55        }
56    }
57}
58
59pub struct Randomness {
60    r: G2Affine,
61    s: G2Affine,
62}
63
64impl Randomness {
65    pub fn gen(rng: &mut impl RngCore) -> Self {
66        let g = G2Affine::generator();
67        let r = gen_g2_elem(rng, g);
68        let s = gen_g2_elem(rng, g);
69        Randomness { r, s }
70    }
71}
72
73impl Mul for &Randomness {
74    type Output = Randomness;
75
76    fn mul(self, rhs: Self) -> Self::Output {
77        let r = self.r.add(&G2Projective::from(rhs.r)).into();
78        let s = self.s.add(&G2Projective::from(rhs.s)).into();
79        Randomness { r, s }
80    }
81}
82
83#[derive(Debug, PartialEq)]
84pub struct Commitment {
85    c: Gt,
86    d: Gt,
87}
88
89impl Mul for &Commitment {
90    type Output = Commitment;
91
92    fn mul(self, rhs: &Commitment) -> Self::Output {
93        let c = self.c.add(rhs.c);
94        let d = self.d.add(rhs.d);
95        Commitment { c, d }
96    }
97}
98
99pub struct CommitmentKey<const N: usize> {
100    g_arr: [G1Affine; N],
101    h_arr: [G1Affine; N],
102    gr: G1Affine,
103    hr: G1Affine,
104    gs: G1Affine,
105    hs: G1Affine,
106}
107
108impl<const N: usize> CommitmentKey<N> {
109    pub fn generate() -> CommitmentKey<N> {
110        let mut rng = thread_rng();
111        let g = G1Affine::generator();
112
113        let mut g_vec = Vec::with_capacity(N);
114        let mut h_vec = Vec::with_capacity(N);
115        for _ in 0..N {
116            g_vec.push(gen_g1_elem(&mut rng, g));
117            h_vec.push(gen_g1_elem(&mut rng, g));
118        }
119        let g_arr = g_vec.try_into().unwrap();
120        let h_arr = h_vec.try_into().unwrap();
121
122        let gr = gen_g1_elem(&mut rng, g);
123        let hr = gen_g1_elem(&mut rng, g);
124
125        let gs = gen_g1_elem(&mut rng, g);
126        let hs = gen_g1_elem(&mut rng, g);
127
128        CommitmentKey {
129            g_arr,
130            h_arr,
131            gr,
132            hr,
133            gs,
134            hs,
135        }
136    }
137
138    pub fn commit_with_randomness(&self, value: &Values<N>, randomness: &Randomness) -> Commitment {
139        let mut c = pairing(&self.gr, &randomness.r) + pairing(&self.gs, &randomness.s);
140        for (g, v) in zip(&self.g_arr, &value.values) {
141            c += pairing(g, v)
142        }
143
144        let mut d = pairing(&self.hr, &randomness.r) + pairing(&self.hs, &randomness.s);
145        for (h, v) in zip(&self.h_arr, &value.values) {
146            d += pairing(h, v)
147        }
148
149        Commitment { c, d }
150    }
151
152    pub fn commit(&self, value: &Values<N>) -> (Commitment, Randomness) {
153        let randomness = Randomness::gen(&mut thread_rng());
154        let commitment = Self::commit_with_randomness(self, value, &randomness);
155        (commitment, randomness)
156    }
157}
158
159fn gen_g1_elem(rng: &mut impl RngCore, generator: G1Affine) -> G1Affine {
160    let r = Scalar::random(rng);
161    (generator * r).into()
162}
163
164fn gen_g2_elem(rng: &mut impl RngCore, generator: G2Affine) -> G2Affine {
165    let r = Scalar::random(rng);
166    (generator * r).into()
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172
173    #[test]
174    fn it_works() {
175        let ck = CommitmentKey::<10>::generate();
176        let value = Values::random();
177        let (c, r) = ck.commit(&value);
178        let d = ck.commit_with_randomness(&value, &r);
179        assert_eq!(c, d);
180    }
181
182    #[test]
183    fn multiplicatively_homomorphic() {
184        let ck = CommitmentKey::<1>::generate();
185
186        let v1 = Values::random();
187        let (c1, r1) = ck.commit(&v1);
188
189        let v2 = Values::random();
190        let (c2, r2) = ck.commit(&v2);
191
192        let v_mul = &v1 * &v2;
193        let r_mul = &r1 * &r2;
194        let expected = ck.commit_with_randomness(&v_mul, &r_mul);
195
196        let actual = c1.mul(&c2);
197
198        assert_eq!(actual, expected);
199    }
200}