coconut_crypto/signature/
aggregated_signature.rs

1use alloc::vec::Vec;
2use ark_ff::PrimeField;
3use itertools::{process_results, Itertools};
4use secret_sharing_and_dkg::common::ParticipantId;
5use serde::{Deserialize, Serialize};
6
7use ark_ec::pairing::Pairing;
8
9use ark_serialize::*;
10use ark_std::cfg_iter;
11use utils::iter::validate;
12
13use super::{error::AggregatedPSError, ps_signature::Signature};
14use crate::helpers::{lagrange_basis_at_0, seq_pairs_satisfy, CheckLeft};
15use utils::owned_pairs;
16
17#[cfg(feature = "parallel")]
18use rayon::prelude::*;
19
20type Result<T, E = AggregatedPSError> = core::result::Result<T, E>;
21
22/// Signature produced by combining several Pointcheval-Sanders signatures together.
23/// This signature can be verified using the verification key.
24#[derive(
25    Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize,
26)]
27#[serde(bound = "")]
28pub struct AggregatedSignature<E: Pairing>(Signature<E>);
29utils::impl_deref! { AggregatedSignature<E: Pairing>(Signature<E>) }
30
31impl<E: Pairing> AggregatedSignature<E> {
32    /// Creates new `AggregatedSignature` using supplied signatures which must be provided
33    /// along with the corresponding unique `ParticipantId`s sorted in increasing order.
34    /// This signature can be verified using the verification key.
35    pub fn new<'a, SI>(participant_signatures: SI, &h: &E::G1Affine) -> Result<Self>
36    where
37        SI: IntoIterator<Item = (ParticipantId, &'a Signature<E>)>,
38    {
39        let validator = (
40            |(idx, sig): &(u16, &Signature<E>)| {
41                (sig.sigma_1 != h).then_some(AggregatedPSError::InvalidSigma1For(*idx))
42            },
43            CheckLeft(seq_pairs_satisfy(|a, b| a < b)),
44        );
45
46        let (participant_ids, s): (Vec<_>, Vec<_>) = process_results(
47            validate(participant_signatures, validator).map_ok(|(id, sig)| (id, sig.sigma_2)),
48            |iter| iter.unzip(),
49        )?;
50        if s.is_empty() {
51            Err(AggregatedPSError::NoSignatures)?
52        }
53        if cfg_iter!(participant_ids).any(|p| *p == 0) {
54            return Err(AggregatedPSError::ParticipantIdCantBeZero);
55        }
56        let l = lagrange_basis_at_0(participant_ids)
57            .map(<E::ScalarField as PrimeField>::into_bigint)
58            .collect();
59        let s_mul_l = owned_pairs!(s, l).msm_bigint();
60
61        Ok(Self(Signature::combine(h, s_mul_l)))
62    }
63}
64
65#[cfg(test)]
66mod aggregated_signature_tests {
67    use alloc::vec::Vec;
68    use ark_bls12_381::Bls12_381;
69    type G1 = <Bls12_381 as Pairing>::G1;
70    use ark_ec::{pairing::Pairing, CurveGroup};
71    use ark_ff::UniformRand;
72    use ark_std::{
73        cfg_into_iter,
74        rand::{rngs::StdRng, SeedableRng},
75    };
76    use blake2::Blake2b512;
77
78    use itertools::Itertools;
79    #[cfg(feature = "parallel")]
80    use rayon::prelude::*;
81
82    use crate::{
83        helpers::n_rand,
84        setup::test_setup,
85        signature::{aggregated_signature::AggregatedSignature, error::AggregatedPSError},
86        BlindSignature, CommitmentOrMessage, MessageCommitment, Signature,
87    };
88
89    #[test]
90    fn basic_workflow() {
91        cfg_into_iter!(2..5).for_each(|message_count| {
92            cfg_into_iter!(1..message_count).for_each(|blind_message_count| {
93                cfg_into_iter!(1..8).for_each(|authority_count| {
94                    // https://eprint.iacr.org/2022/011.pdf 7.1
95                    let mut rng = StdRng::seed_from_u64(0u64);
96                    let h = G1::rand(&mut rng).into_affine();
97                    let (sk, pk, params, msgs) =
98                        test_setup::<Bls12_381, Blake2b512, _>(&mut rng, message_count);
99
100                    // https://eprint.iacr.org/2022/011.pdf 7.2
101                    let (blind_msgs, reveal_msgs) = msgs.split_at(blind_message_count as usize);
102                    let blind_indices = 0..blind_msgs.len();
103
104                    let blindings: Vec<_> = n_rand(&mut rng, blind_msgs.len()).collect();
105                    let o_m_pairs = utils::pairs!(blindings, blind_msgs);
106
107                    let m_comms: Vec<_> =
108                        MessageCommitment::new_iter(o_m_pairs, &h, &params).collect();
109                    let comms = m_comms
110                        .iter()
111                        .copied()
112                        .map(CommitmentOrMessage::BlindedMessage)
113                        .chain(
114                            reveal_msgs
115                                .iter()
116                                .copied()
117                                .map(CommitmentOrMessage::RevealedMessage),
118                        );
119
120                    let sigs = (1..=authority_count)
121                        .map(|_| {
122                            let blind_signature =
123                                BlindSignature::new(comms.clone(), &sk, &h).unwrap();
124
125                            let sig = blind_signature
126                                .unblind(blind_indices.clone().zip(blindings.iter()), &pk, &h)
127                                .unwrap();
128
129                            sig.verify(&msgs, &pk, &params).unwrap();
130
131                            sig
132                        })
133                        .collect_vec();
134
135                    let aggregated = AggregatedSignature::new(
136                        sigs.iter()
137                            .enumerate()
138                            .map(|(idx, sig)| (idx as u16 + 1, sig)),
139                        &h,
140                    )
141                    .unwrap();
142                    aggregated.verify(&msgs, &pk, &params).unwrap();
143                })
144            })
145        });
146    }
147
148    #[test]
149    fn invalid_sigma_1() {
150        let mut rng = StdRng::seed_from_u64(0u64);
151        let h = G1::rand(&mut rng).into_affine();
152        let sigma_1 = G1::rand(&mut rng).into_affine();
153        let sigma_2 = G1::rand(&mut rng).into_affine();
154
155        assert_eq!(
156            AggregatedSignature::new(
157                Some(Signature::<Bls12_381>::combine(sigma_1, sigma_2))
158                    .iter()
159                    .enumerate()
160                    .map(|(idx, v)| (idx as u16 + 1, v)),
161                &h
162            ),
163            Err(AggregatedPSError::InvalidSigma1For(1))
164        )
165    }
166
167    #[test]
168    fn empty_signature() {
169        let mut rng = StdRng::seed_from_u64(0u64);
170        let h = G1::rand(&mut rng).into_affine();
171
172        assert_eq!(
173            AggregatedSignature::<Bls12_381>::new(None, &h),
174            Err(AggregatedPSError::NoSignatures)
175        );
176    }
177
178    #[test]
179    fn zero_participant_id() {
180        let mut rng = StdRng::seed_from_u64(0u64);
181        let h = G1::rand(&mut rng).into_affine();
182
183        let sig1 = Signature::<Bls12_381>::combine(h.clone(), G1::rand(&mut rng).into_affine());
184        let sig2 = Signature::<Bls12_381>::combine(h.clone(), G1::rand(&mut rng).into_affine());
185        let aggr_sigs = vec![(0, &sig1), (1, &sig2)];
186        assert_eq!(
187            AggregatedSignature::new(aggr_sigs, &h),
188            Err(AggregatedPSError::ParticipantIdCantBeZero)
189        )
190    }
191}