Skip to main content

group_threshold_cryptography_pre_release/
decryption.rs

1use std::ops::Mul;
2
3use ark_ec::{pairing::Pairing, CurveGroup};
4use ark_ff::{Field, One, Zero};
5use ferveo_common::serialization;
6use itertools::{izip, zip_eq};
7use rand_core::RngCore;
8use serde::{de::DeserializeOwned, Deserialize, Serialize};
9use serde_with::serde_as;
10
11use crate::{
12    generate_random, Ciphertext, CiphertextHeader, PrivateKeyShare,
13    PublicDecryptionContextFast, PublicDecryptionContextSimple, Result,
14};
15
16#[serde_as]
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct DecryptionShareFast<E: Pairing> {
19    pub decrypter_index: usize,
20    #[serde_as(as = "serialization::SerdeAs")]
21    pub decryption_share: E::G1Affine,
22}
23
24#[serde_as]
25#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
26pub struct ValidatorShareChecksum<E: Pairing> {
27    #[serde_as(as = "serialization::SerdeAs")]
28    pub checksum: E::G1Affine,
29}
30
31impl<E: Pairing> ValidatorShareChecksum<E> {
32    pub fn new(
33        validator_decryption_key: &E::ScalarField,
34        ciphertext_header: &CiphertextHeader<E>,
35    ) -> Result<Self> {
36        // C_i = dk_i^{-1} * U
37        let checksum = ciphertext_header
38            .commitment
39            // TODO: Should we panic here? I think we should since that would mean that the decryption key is invalid.
40            //   And so, the validator should not be able to create a decryption share.
41            //   And so, the validator should remake their keypair.
42            .mul(
43                validator_decryption_key
44                    .inverse()
45                    .expect("Inverse of this key doesn't exist"),
46            )
47            .into_affine();
48        Ok(Self { checksum })
49    }
50
51    pub fn verify(
52        &self,
53        decryption_share: &E::TargetField,
54        share_aggregate: &E::G2Affine,
55        validator_public_key: &E::G2Affine,
56        h: &E::G2,
57        ciphertext: &Ciphertext<E>,
58    ) -> bool {
59        // D_i == e(C_i, Y_i)
60        if *decryption_share != E::pairing(self.checksum, *share_aggregate).0 {
61            return false;
62        }
63
64        // e(C_i, ek_i) == e(U, H)
65        if E::pairing(self.checksum, *validator_public_key)
66            != E::pairing(ciphertext.commitment, *h)
67        {
68            return false;
69        }
70
71        true
72    }
73}
74
75#[serde_as]
76#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(bound(
78    serialize = "ValidatorShareChecksum<E>: Serialize",
79    deserialize = "ValidatorShareChecksum<E>: DeserializeOwned"
80))]
81pub struct DecryptionShareSimple<E: Pairing> {
82    #[serde_as(as = "serialization::SerdeAs")]
83    pub decryption_share: E::TargetField,
84    pub validator_checksum: ValidatorShareChecksum<E>,
85}
86
87impl<E: Pairing> DecryptionShareSimple<E> {
88    /// Create a decryption share from the given parameters.
89    /// This function checks that the ciphertext is valid.
90    pub fn create(
91        validator_decryption_key: &E::ScalarField,
92        private_key_share: &PrivateKeyShare<E>,
93        ciphertext_header: &CiphertextHeader<E>,
94        aad: &[u8],
95        g_inv: &E::G1Prepared,
96    ) -> Result<Self> {
97        ciphertext_header.check(aad, g_inv)?;
98        Self::create_unchecked(
99            validator_decryption_key,
100            private_key_share,
101            ciphertext_header,
102        )
103    }
104
105    /// Create a decryption share from the given parameters.
106    /// This function does not check that the ciphertext is valid.
107    pub fn create_unchecked(
108        validator_decryption_key: &E::ScalarField,
109        private_key_share: &PrivateKeyShare<E>,
110        ciphertext_header: &CiphertextHeader<E>,
111    ) -> Result<Self> {
112        // D_i = e(U, Z_i)
113        let decryption_share = E::pairing(
114            ciphertext_header.commitment,
115            private_key_share.private_key_share,
116        )
117        .0;
118
119        let validator_checksum = ValidatorShareChecksum::new(
120            validator_decryption_key,
121            ciphertext_header,
122        )?;
123
124        Ok(Self {
125            decryption_share,
126            validator_checksum,
127        })
128    }
129    /// Verify that the decryption share is valid.
130    pub fn verify(
131        &self,
132        share_aggregate: &E::G2Affine,
133        validator_public_key: &E::G2Affine,
134        h: &E::G2,
135        ciphertext: &Ciphertext<E>,
136    ) -> bool {
137        self.validator_checksum.verify(
138            &self.decryption_share,
139            share_aggregate,
140            validator_public_key,
141            h,
142            ciphertext,
143        )
144    }
145}
146
147#[serde_as]
148#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
149#[serde(bound(
150    serialize = "ValidatorShareChecksum<E>: Serialize",
151    deserialize = "ValidatorShareChecksum<E>: DeserializeOwned"
152))]
153pub struct DecryptionSharePrecomputed<E: Pairing> {
154    pub decrypter_index: usize,
155    #[serde_as(as = "serialization::SerdeAs")]
156    pub decryption_share: E::TargetField,
157    pub validator_checksum: ValidatorShareChecksum<E>,
158}
159
160impl<E: Pairing> DecryptionSharePrecomputed<E> {
161    pub fn new(
162        validator_index: usize,
163        validator_decryption_key: &E::ScalarField,
164        private_key_share: &PrivateKeyShare<E>,
165        ciphertext_header: &CiphertextHeader<E>,
166        aad: &[u8],
167        lagrange_coeff: &E::ScalarField,
168        g_inv: &E::G1Prepared,
169    ) -> Result<Self> {
170        ciphertext_header.check(aad, g_inv)?;
171        Self::create_unchecked(
172            validator_index,
173            validator_decryption_key,
174            private_key_share,
175            ciphertext_header,
176            lagrange_coeff,
177        )
178    }
179
180    pub fn create_unchecked(
181        validator_index: usize,
182        validator_decryption_key: &E::ScalarField,
183        private_key_share: &PrivateKeyShare<E>,
184        ciphertext_header: &CiphertextHeader<E>,
185        lagrange_coeff: &E::ScalarField,
186    ) -> Result<Self> {
187        // U_{λ_i} = [λ_{i}(0)] U
188        let u_to_lagrange_coeff =
189            ciphertext_header.commitment.mul(lagrange_coeff);
190        // C_{λ_i} = e(U_{λ_i}, Z_i)
191        let decryption_share = E::pairing(
192            u_to_lagrange_coeff,
193            private_key_share.private_key_share,
194        )
195        .0;
196
197        let validator_checksum = ValidatorShareChecksum::new(
198            validator_decryption_key,
199            ciphertext_header,
200        )?;
201
202        Ok(Self {
203            decrypter_index: validator_index,
204            decryption_share,
205            validator_checksum,
206        })
207    }
208
209    /// Verify that the decryption share is valid.
210    pub fn verify(
211        &self,
212        share_aggregate: &E::G2Affine,
213        validator_public_key: &E::G2Affine,
214        h: &E::G2,
215        ciphertext: &Ciphertext<E>,
216    ) -> bool {
217        self.validator_checksum.verify(
218            &self.decryption_share,
219            share_aggregate,
220            validator_public_key,
221            h,
222            ciphertext,
223        )
224    }
225}
226
227// TODO: Remove this code? Currently only used in benchmarks. Move to benchmark suite?
228pub fn batch_verify_decryption_shares<R: RngCore, E: Pairing>(
229    pub_contexts: &[PublicDecryptionContextFast<E>],
230    ciphertexts: &[Ciphertext<E>],
231    decryption_shares: &[Vec<DecryptionShareFast<E>>],
232    rng: &mut R,
233) -> bool {
234    let num_ciphertexts = ciphertexts.len();
235    let num_shares = decryption_shares[0].len();
236
237    // Get [b_i] H for each of the decryption shares
238    let blinding_keys = decryption_shares[0]
239        .iter()
240        .map(|d| {
241            pub_contexts[d.decrypter_index]
242                .blinded_key_share
243                .blinding_key_prepared
244                .clone()
245        })
246        .collect::<Vec<_>>();
247
248    // For each ciphertext, generate num_shares random scalars
249    let alpha_ij = (0..num_ciphertexts)
250        .map(|_| generate_random::<_, E>(num_shares, rng))
251        .collect::<Vec<_>>();
252
253    let mut pairings_a = Vec::with_capacity(num_shares + 1);
254    let mut pairings_b = Vec::with_capacity(num_shares + 1);
255
256    // Compute \sum_j \alpha_{i,j} for each ciphertext i
257    let sum_alpha_i = alpha_ij
258        .iter()
259        .map(|alpha_j| alpha_j.iter().sum::<E::ScalarField>())
260        .collect::<Vec<_>>();
261
262    // Compute \sum_i [ \sum_j \alpha_{i,j} ] U_i
263    let sum_u_i = E::G1Prepared::from(
264        izip!(ciphertexts.iter(), sum_alpha_i.iter())
265            .map(|(c, alpha_j)| c.commitment.mul(*alpha_j))
266            .sum::<E::G1>()
267            .into_affine(),
268    );
269
270    // e(\sum_i [ \sum_j \alpha_{i,j} ] U_i, -H)
271    pairings_a.push(sum_u_i);
272    pairings_b.push(pub_contexts[0].h_inv.clone());
273
274    let mut sum_d_i = vec![E::G1::zero(); num_shares];
275
276    // sum_D_i = { [\sum_i \alpha_{i,j} ] D_i }
277    for (d, alpha_j) in izip!(decryption_shares.iter(), alpha_ij.iter()) {
278        for (sum_alpha_d_i, d_ij, alpha) in
279            izip!(sum_d_i.iter_mut(), d.iter(), alpha_j.iter())
280        {
281            *sum_alpha_d_i += d_ij.decryption_share.mul(*alpha);
282        }
283    }
284
285    // e([\sum_i \alpha_{i,j} ] D_i, B_i)
286    for (d_i, b_i) in izip!(sum_d_i.iter(), blinding_keys.iter()) {
287        pairings_a.push(E::G1Prepared::from(d_i.into_affine()));
288        pairings_b.push(b_i.clone());
289    }
290
291    E::multi_pairing(pairings_a, pairings_b).0 == E::TargetField::one()
292}
293
294pub fn verify_decryption_shares_fast<E: Pairing>(
295    pub_contexts: &[PublicDecryptionContextFast<E>],
296    ciphertext: &Ciphertext<E>,
297    decryption_shares: &[DecryptionShareFast<E>],
298) -> bool {
299    // [b_i] H
300    let blinding_keys = decryption_shares
301        .iter()
302        .map(|d| {
303            pub_contexts[d.decrypter_index]
304                .blinded_key_share
305                .blinding_key_prepared
306                .clone()
307        })
308        .collect::<Vec<_>>();
309
310    let mut pairing_a: Vec<E::G1Prepared> = vec![];
311    let mut pairing_b = vec![];
312
313    // e(U, -H)
314    pairing_a.push(ciphertext.commitment.into());
315    pairing_b.push(pub_contexts[0].h_inv.clone());
316
317    for (d_i, p_i) in zip_eq(decryption_shares, blinding_keys) {
318        let mut pairing_a_i = pairing_a.clone();
319        let mut pairing_b_i = pairing_b.clone();
320        // e(D_i, B_i)
321        pairing_a_i.push(d_i.decryption_share.into());
322        pairing_b_i.push(p_i.clone());
323        if E::multi_pairing(pairing_a_i, pairing_b_i).0 != E::TargetField::one()
324        {
325            return false;
326        }
327    }
328
329    true
330}
331
332pub fn verify_decryption_shares_simple<E: Pairing>(
333    pub_contexts: &Vec<PublicDecryptionContextSimple<E>>,
334    ciphertext: &Ciphertext<E>,
335    decryption_shares: &Vec<DecryptionShareSimple<E>>,
336) -> bool {
337    let blinded_key_shares = &pub_contexts
338        .iter()
339        .map(|c| &c.blinded_key_share.blinded_key_share)
340        .collect::<Vec<_>>();
341    for (decryption_share, y_i, pub_context) in
342        izip!(decryption_shares, blinded_key_shares, pub_contexts)
343    {
344        let is_valid = decryption_share.verify(
345            y_i,
346            &pub_context.validator_public_key.into_affine(),
347            &pub_context.h.into(),
348            ciphertext,
349        );
350        if !is_valid {
351            return false;
352        }
353    }
354    true
355}
356
357#[cfg(test)]
358mod tests {
359    use ark_ec::AffineRepr;
360    use ferveo_common::{FromBytes, ToBytes};
361
362    use crate::*;
363
364    type E = ark_bls12_381::Bls12_381;
365
366    #[test]
367    fn decryption_share_serialization() {
368        let decryption_share = DecryptionShareFast::<E> {
369            decrypter_index: 1,
370            decryption_share: ark_bls12_381::G1Affine::generator(),
371        };
372
373        let serialized = decryption_share.to_bytes().unwrap();
374        let deserialized: DecryptionShareFast<E> =
375            DecryptionShareFast::from_bytes(&serialized).unwrap();
376        assert_eq!(serialized, deserialized.to_bytes().unwrap())
377    }
378}