commonware_cryptography/bls12381/dkg/
types.rs

1//! Standard types sent over the wire for [Dealer] shares and [Player] acknowledgements.
2//!
3//! [Dealer]: crate::bls12381::dkg::Dealer
4//! [Player]: crate::bls12381::dkg::Player
5
6use crate::{
7    bls12381::primitives::{group, poly::Public, variant::Variant},
8    PublicKey, Signature, Signer,
9};
10use bytes::{Buf, BufMut};
11use commonware_codec::{varint::UInt, EncodeSize, FixedSize, Read, ReadExt, Write};
12use commonware_utils::quorum;
13
14/// A [Share] sent by a [Dealer] node to a [Player] node.
15///
16/// Contains the [Dealer]'s public commitment to their polynomial and the specific
17/// share calculated for the receiving [Player].
18///
19/// [Share]: group::Share
20/// [Dealer]: crate::bls12381::dkg::Dealer
21/// [Player]: crate::bls12381::dkg::Player
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct Share<V: Variant> {
24    /// The [Dealer]'s public commitment (coefficients of the polynomial).
25    ///
26    /// [Dealer]: crate::bls12381::dkg::Dealer
27    pub commitment: Public<V>,
28    /// The secret share evaluated for the recipient [Player].
29    ///
30    /// [Player]: crate::bls12381::dkg::Player
31    pub share: group::Share,
32}
33
34impl<V: Variant> Share<V> {
35    /// Create a new [Share] message.
36    pub fn new(commitment: Public<V>, share: group::Share) -> Self {
37        Self { commitment, share }
38    }
39}
40
41impl<V: Variant> Write for Share<V> {
42    fn write(&self, buf: &mut impl BufMut) {
43        self.commitment.write(buf);
44        self.share.write(buf);
45    }
46}
47
48impl<V: Variant> EncodeSize for Share<V> {
49    fn encode_size(&self) -> usize {
50        self.commitment.encode_size() + self.share.encode_size()
51    }
52}
53
54impl<V: Variant> Read for Share<V> {
55    type Cfg = u32;
56
57    fn read_cfg(buf: &mut impl Buf, t: &u32) -> Result<Self, commonware_codec::Error> {
58        let q = quorum(*t);
59        Ok(Self {
60            commitment: Public::<V>::read_cfg(buf, &(q as usize))?,
61            share: group::Share::read(buf)?,
62        })
63    }
64}
65
66/// Acknowledgement message sent by a [Player] node back to the [Dealer] node.
67///
68/// Acknowledges the receipt and verification of a [Share] message.
69/// Includes a signature to authenticate the acknowledgment.
70///
71/// [Dealer]: crate::bls12381::dkg::Dealer
72/// [Player]: crate::bls12381::dkg::Player
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct Ack<S: Signature> {
75    /// The public key identifier of the [Player] sending the acknowledgment.
76    ///
77    /// [Player]: crate::bls12381::dkg::Player
78    pub player: u32,
79    /// A signature covering the DKG round, dealer ID, and the [Dealer]'s commitment.
80    /// This confirms the player received and validated the correct share.
81    ///
82    /// [Dealer]: crate::bls12381::dkg::Dealer
83    pub signature: S,
84}
85
86impl<S: Signature> Ack<S> {
87    /// Create a new [Ack] message, constructing and signing the payload with the provided [Signer].
88    pub fn new<C, V>(
89        namespace: &[u8],
90        signer: &C,
91        player: u32,
92        round: u64,
93        dealer: &C::PublicKey,
94        commitment: &Public<V>,
95    ) -> Self
96    where
97        C: Signer<Signature = S>,
98        V: Variant,
99    {
100        let payload = Self::signature_payload::<V, C::PublicKey>(round, dealer, commitment);
101        let signature = signer.sign(Some(namespace), &payload);
102        Self { player, signature }
103    }
104
105    /// Verifies the signature in the [Ack] message.
106    pub fn verify<V: Variant, P: PublicKey<Signature = S>>(
107        &self,
108        namespace: &[u8],
109        public_key: &P,
110        round: u64,
111        dealer: &P,
112        commitment: &Public<V>,
113    ) -> bool {
114        let payload = Self::signature_payload::<V, P>(round, dealer, commitment);
115        public_key.verify(Some(namespace), &payload, &self.signature)
116    }
117
118    /// Create a signature payload for acking a secret.
119    ///
120    /// This payload consists of the round number, [Dealer]'s public key, and the [Dealer]'s commitment,
121    /// and the signature over this payload is included in the [Ack] message.
122    ///
123    /// [Dealer]: crate::bls12381::dkg::Dealer
124    fn signature_payload<V: Variant, P: PublicKey>(
125        round: u64,
126        dealer: &P,
127        commitment: &Public<V>,
128    ) -> Vec<u8> {
129        let mut payload = Vec::with_capacity(u64::SIZE + P::SIZE + commitment.encode_size());
130        round.write(&mut payload);
131        dealer.write(&mut payload);
132        commitment.write(&mut payload);
133        payload
134    }
135}
136
137impl<S: Signature> Write for Ack<S> {
138    fn write(&self, buf: &mut impl BufMut) {
139        UInt(self.player).write(buf);
140        self.signature.write(buf);
141    }
142}
143
144impl<S: Signature> EncodeSize for Ack<S> {
145    fn encode_size(&self) -> usize {
146        UInt(self.player).encode_size() + self.signature.encode_size()
147    }
148}
149
150impl<S: Signature> Read for Ack<S> {
151    type Cfg = ();
152
153    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, commonware_codec::Error> {
154        Ok(Self {
155            player: UInt::read(buf)?.into(),
156            signature: S::read(buf)?,
157        })
158    }
159}
160
161#[cfg(test)]
162mod test {
163    use super::*;
164    use crate::{
165        bls12381::{
166            dkg::ops,
167            primitives::{group, poly::Public, variant::MinSig},
168        },
169        ed25519::PrivateKey,
170        PrivateKeyExt, Signer,
171    };
172    use commonware_utils::quorum;
173    use rand::SeedableRng;
174    use rand_chacha::ChaCha8Rng;
175
176    const ACK_NAMESPACE: &[u8] = b"DKG_ACK";
177
178    fn generate_identities(num_peers: u32) -> (Public<MinSig>, Vec<(PrivateKey, group::Share)>) {
179        let mut rng = ChaCha8Rng::seed_from_u64(num_peers as u64);
180
181        // Generate consensus key
182        let threshold = quorum(num_peers);
183        let (polynomial, shares) =
184            ops::generate_shares::<_, MinSig>(&mut rng, None, num_peers, threshold);
185
186        // Generate p2p private keys
187        let mut peer_signers = (0..num_peers)
188            .map(|_| PrivateKey::from_rng(&mut rng))
189            .collect::<Vec<_>>();
190        peer_signers.sort_by_key(|signer| signer.public_key());
191
192        let identities = peer_signers.into_iter().zip(shares).collect::<Vec<_>>();
193
194        (polynomial, identities)
195    }
196
197    #[test]
198    fn test_share_roundtrip() {
199        const NUM_PARTICIPANTS: u32 = 4;
200
201        let (commitment, identities) = generate_identities(NUM_PARTICIPANTS);
202        let (_, share) = &identities[0];
203
204        let share = Share::<MinSig>::new(commitment.clone(), share.clone());
205
206        let mut buf = Vec::with_capacity(share.encode_size());
207        share.write(&mut buf);
208
209        let decoded = Share::<MinSig>::read_cfg(&mut buf.as_slice(), &NUM_PARTICIPANTS).unwrap();
210        assert_eq!(decoded, share);
211    }
212
213    #[test]
214    fn test_ack_roundtrip() {
215        const NUM_PARTICIPANTS: u32 = 4;
216
217        let (commitment, identities) = generate_identities(NUM_PARTICIPANTS);
218        let (signer, _) = &identities[0];
219
220        let ack = Ack::new::<PrivateKey, MinSig>(
221            ACK_NAMESPACE,
222            signer,
223            1337,
224            42,
225            &signer.public_key(),
226            &commitment,
227        );
228
229        let mut buf = Vec::with_capacity(ack.encode_size());
230        ack.write(&mut buf);
231
232        let decoded =
233            Ack::<<PrivateKey as Signer>::Signature>::read_cfg(&mut buf.as_slice(), &()).unwrap();
234        assert_eq!(decoded, ack);
235    }
236}