dkls23/keygen/
dkg.rs

1// Copyright (c) Silence Laboratories Pte. Ltd. All Rights Reserved.
2// This software is licensed under the Silence Laboratories License Agreement.
3
4//! This module implements the distributed key generation protocol based on Protocol 6.1 from
5//! the paper "Efficient Multi-Party Computation with Dispute Resolution" <https://eprint.iacr.org/2022/374.pdf> and OT parameters
6//! from here "Threshold ECDSA in Three Rounds" <https://eprint.iacr.org/2023/765.pdf>
7
8use k256::{
9    elliptic_curve::{
10        group::GroupEncoding,
11        subtle::{Choice, ConstantTimeEq},
12        Group,
13    },
14    NonZeroScalar, ProjectivePoint, Scalar,
15};
16use merlin::Transcript;
17use rand::prelude::*;
18use rand_chacha::ChaCha20Rng;
19use sha2::{Digest, Sha256};
20
21use sl_mpc_mate::{
22    coord::*,
23    math::{
24        feldman_verify, polynomial_coeff_multipliers, GroupPolynomial,
25        Polynomial,
26    },
27    message::*,
28    SessionId,
29};
30
31use sl_oblivious::{
32    endemic_ot::{EndemicOTMsg1, EndemicOTReceiver, EndemicOTSender},
33    soft_spoken::{build_pprf, eval_pprf},
34    utils::TranscriptProtocol,
35    zkproofs::DLogProof,
36};
37
38use crate::{
39    keygen::{
40        constants::*, messages::*, utils::check_secret_recovery, KeygenError,
41        Keyshare,
42    },
43    proto::{tags::*, *},
44    setup::{KeygenSetupMessage, ProtocolParticipant, ABORT_MESSAGE_TAG},
45};
46
47#[cfg(feature = "multi-thread")]
48use tokio::task::block_in_place;
49
50#[cfg(not(feature = "multi-thread"))]
51fn block_in_place<F, R>(f: F) -> R
52where
53    F: FnOnce() -> R,
54{
55    f()
56}
57
58/// Seed type for the ChaCha20 random number generator
59pub type Seed = <ChaCha20Rng as SeedableRng>::Seed;
60
61use crate::pairs::Pairs;
62
63/// Data structure for key refresh operations
64///
65/// This struct contains the necessary information for performing key refresh operations,
66/// including additive shares, lost key share information, and expected public key values.
67pub(crate) struct KeyRefreshData {
68    /// Additive share of participant_i (after interpolation)
69    /// \sum_{i=0}^{n-1} s_i_0 = private_key
70    /// s_i_0 can be equal to Zero in case when participant lost their key_share
71    /// and wants to recover it during key_refresh
72    pub(crate) s_i_0: Scalar,
73
74    /// List of participant IDs who lost their key shares
75    /// Should be in range [0, n-1]
76    pub(crate) lost_keyshare_party_ids: Vec<u8>,
77
78    /// Expected public key for key refresh
79    pub(crate) expected_public_key: ProjectivePoint,
80
81    /// Root chain code for key derivation
82    pub(crate) root_chain_code: [u8; 32],
83}
84
85/// Executes the Distributed Key Generation protocol
86///
87/// This is the main entry point for the DKG protocol. It orchestrates the entire process
88/// of generating a distributed key among the participants.
89///
90/// # Type Parameters
91///
92/// * `T` - A type implementing the `KeygenSetupMessage` trait
93/// * `R` - A type implementing the `Relay` trait for message communication
94///
95/// # Arguments
96///
97/// * `setup` - The protocol setup configuration
98/// * `seed` - The random seed for cryptographic operations
99/// * `relay` - The message relay for communication between parties
100///
101/// # Returns
102///
103/// * `Ok(Keyshare)` - The generated key share if the protocol succeeds
104/// * `Err(KeygenError)` - If the protocol fails
105///
106/// # Errors
107///
108/// This function may return the following errors:
109/// * `KeygenError::AbortProtocol` - If the protocol is aborted by a participant
110/// * `KeygenError::SendMessage` - If there's an error sending messages
111/// * Other `KeygenError` variants for various protocol failures
112pub async fn run<T, R>(
113    setup: T,
114    seed: Seed,
115    relay: R,
116) -> Result<Keyshare, KeygenError>
117where
118    T: KeygenSetupMessage,
119    R: Relay,
120{
121    let abort_msg = create_abort_message(&setup);
122    let mut relay = FilteredMsgRelay::new(relay);
123
124    let result = match run_inner(setup, seed, &mut relay, None).await {
125        Ok(share) => Ok(share),
126        Err(KeygenError::AbortProtocol(p)) => {
127            Err(KeygenError::AbortProtocol(p))
128        }
129        Err(KeygenError::SendMessage) => Err(KeygenError::SendMessage),
130        Err(err) => {
131            // ignore error of sending abort message
132            let _ = relay.send(abort_msg).await;
133            Err(err)
134        }
135    };
136
137    let _ = relay.close().await;
138
139    result
140}
141
142/// Internal implementation of the DKG protocol
143///
144/// This function implements the core logic of the DKG protocol, including:
145/// - Polynomial generation and commitment
146/// - Share distribution and verification
147/// - Oblivious transfer setup
148/// - Final key share computation
149///
150/// # Type Parameters
151///
152/// * `T` - A type implementing the `KeygenSetupMessage` trait
153/// * `R` - A type implementing the `Relay` trait
154///
155/// # Arguments
156///
157/// * `setup` - The protocol setup configuration
158/// * `seed` - The random seed for cryptographic operations
159/// * `relay` - The message relay for communication
160/// * `key_refresh_data` - Optional data for key refresh operations
161///
162/// # Returns
163///
164/// * `Ok(Keyshare)` - The generated key share if the protocol succeeds
165/// * `Err(KeygenError)` - If the protocol fails
166///
167/// # Errors
168///
169/// This function may return various `KeygenError` variants depending on the failure mode.
170#[allow(non_snake_case)]
171pub(crate) async fn run_inner<T, R>(
172    setup: T,
173    seed: Seed,
174    relay: &mut FilteredMsgRelay<R>,
175    key_refresh_data: Option<&KeyRefreshData>,
176) -> Result<Keyshare, KeygenError>
177where
178    T: KeygenSetupMessage,
179    R: Relay,
180{
181    let mut rng = ChaCha20Rng::from_seed(seed);
182
183    let mut scheme = crate::proto::Scheme::new(&mut rng);
184
185    let T = setup.threshold() as usize;
186    let N = setup.total_participants();
187
188    let my_party_id = setup.participant_index() as u8;
189    let my_rank = setup.participant_rank(my_party_id as usize);
190
191    if let Some(v) = key_refresh_data {
192        let cond1 = v.expected_public_key == ProjectivePoint::IDENTITY;
193        let cond2 = v.lost_keyshare_party_ids.len() > (N - T);
194        let cond3 = (v.s_i_0 == Scalar::ZERO)
195            && (!v.lost_keyshare_party_ids.contains(&my_party_id));
196        if cond1 || cond2 || cond3 {
197            return Err(KeygenError::InvalidKeyRefresh);
198        }
199    }
200
201    let mut keyshare =
202        Keyshare::new(N as u8, T as u8, my_party_id, setup.keyshare_extra());
203
204    let session_id = SessionId::new(rng.gen());
205    let r_i = rng.gen();
206
207    // u_i_k
208    let mut polynomial = Polynomial::random(&mut rng, T - 1);
209    if let Some(v) = key_refresh_data {
210        polynomial.set_constant(v.s_i_0);
211    }
212
213    let x_i = NonZeroScalar::random(&mut rng);
214
215    let big_f_i_vec = polynomial.commit(); // big_f_i_vector in dkg.py
216
217    let commitment = hash_commitment(
218        &session_id,
219        my_party_id as usize,
220        setup.participant_rank(my_party_id as usize) as usize,
221        &x_i,
222        &big_f_i_vec,
223        &r_i,
224    );
225
226    let mut d_i_list = vec![Scalar::ZERO; N];
227    d_i_list[my_party_id as usize] =
228        block_in_place(|| polynomial.derivative_at(my_rank as usize, &x_i));
229
230    relay.ask_messages(&setup, ABORT_MESSAGE_TAG, false).await?;
231    relay.ask_messages(&setup, DKG_MSG_R1, false).await?;
232    relay.ask_messages(&setup, DKG_MSG_R2, false).await?;
233    relay.ask_messages(&setup, DKG_MSG_OT1, true).await?;
234    relay.ask_messages(&setup, DKG_MSG_R3, true).await?;
235    relay.ask_messages(&setup, DKG_MSG_R4, false).await?;
236
237    let (sid_i_list, commitment_list, x_i_list, enc_pub_key) = broadcast_4(
238        &setup,
239        relay,
240        DKG_MSG_R1,
241        (session_id, commitment, x_i, scheme.public_key().to_vec()),
242    )
243    .await?;
244
245    for (receiver, pub_key) in enc_pub_key.into_iter().enumerate() {
246        if receiver != setup.participant_index() {
247            scheme
248                .receiver_public_key(receiver, &pub_key)
249                .map_err(|_| KeygenError::InvalidMessage)?;
250        }
251    }
252
253    // Check that x_i_list contains unique elements.
254    // N is small and following loops doesn't  allocate.
255    for i in 0..x_i_list.len() - 1 {
256        let x = &x_i_list[i];
257        for s in &x_i_list[i + 1..] {
258            if x.ct_eq(s).into() {
259                return Err(KeygenError::NotUniqueXiValues);
260            }
261        }
262    }
263
264    // TODO: Should parties be initialized with rank_list and x_i_list? Ask Vlad.
265    keyshare.info_mut().final_session_id = sid_i_list
266        .iter()
267        .fold(Sha256::new(), |hash, sid| hash.chain_update(sid))
268        .finalize()
269        .into();
270
271    let dlog_proofs = {
272        // Setup transcript for DLog proofs.
273        let mut dlog_transcript = Transcript::new_dlog_proof(
274            &keyshare.final_session_id,
275            my_party_id as usize,
276            &DLOG_PROOF1_LABEL,
277            &DKG_LABEL,
278        );
279
280        polynomial
281            .iter()
282            .map(|f_i| {
283                DLogProof::prove(
284                    f_i,
285                    &ProjectivePoint::GENERATOR,
286                    &mut dlog_transcript,
287                    &mut rng,
288                )
289            })
290            .collect::<Vec<_>>()
291    };
292
293    let mut base_ot_receivers: Pairs<EndemicOTReceiver> = Pairs::new();
294
295    for receiver_id in setup.all_other_parties() {
296        let sid = get_base_ot_session_id(
297            my_party_id,
298            receiver_id as u8,
299            &keyshare.final_session_id,
300        );
301
302        let mut enc_msg1 = EncryptedMessage::<EndemicOTMsg1>::new(
303            &setup.msg_id(Some(receiver_id), DKG_MSG_OT1),
304            setup.message_ttl().as_secs() as u32,
305            0,
306            0,
307            &scheme,
308        );
309
310        let (msg1, _) = enc_msg1.payload(&scheme);
311
312        let receiver = EndemicOTReceiver::new(&sid, msg1, &mut rng);
313
314        base_ot_receivers.push(receiver_id as u8, receiver);
315
316        // send out R2 P2P message. We call feed() in the loop
317        // and following send_broadcast() will call .send() that
318        // implies feed() + flush()
319        relay
320            .feed(
321                enc_msg1
322                    .encrypt(&mut scheme, receiver_id)
323                    .ok_or(KeygenError::SendMessage)?,
324            )
325            .await
326            .map_err(|_| KeygenError::SendMessage)?;
327    }
328
329    #[cfg(feature = "tracing")]
330    tracing::debug!("feed all OT1");
331
332    // generate chain_code_sid for root_chain_code or use already existed from key_refresh_data
333    let chain_code_sid = if let Some(v) = key_refresh_data {
334        v.root_chain_code
335    } else {
336        SessionId::new(rng.gen()).into()
337    };
338    let r_i_2 = rng.gen();
339
340    let (big_f_i_vecs, r_i_list, commitment_list_2, dlog_proofs_i_list) =
341        broadcast_4(
342            &setup,
343            relay,
344            DKG_MSG_R2,
345            (
346                big_f_i_vec,
347                r_i,
348                hash_commitment_2(
349                    &keyshare.final_session_id,
350                    &chain_code_sid,
351                    &r_i_2,
352                ),
353                dlog_proofs,
354            ),
355        )
356        .await?;
357
358    for party_id in 0..N {
359        let r_i = &r_i_list[party_id];
360        let x_i = &x_i_list[party_id];
361        let sid = &sid_i_list[party_id];
362        let commitment = &commitment_list[party_id];
363        let big_f_i_vector = &big_f_i_vecs[party_id];
364        let dlog_proofs_i = &dlog_proofs_i_list[party_id];
365
366        if big_f_i_vector.coeffs.len() != T {
367            return Err(KeygenError::InvalidMessage);
368        }
369        if dlog_proofs_i.len() != T {
370            return Err(KeygenError::InvalidMessage);
371        }
372
373        let commit_hash = hash_commitment(
374            sid,
375            party_id,
376            setup.participant_rank(party_id) as usize,
377            x_i,
378            big_f_i_vector,
379            r_i,
380        );
381
382        if commit_hash.ct_ne(commitment).into() {
383            return Err(KeygenError::InvalidCommitmentHash);
384        }
385
386        {
387            let mut points = big_f_i_vector.points();
388            if let Some(v) = key_refresh_data {
389                if v.lost_keyshare_party_ids.contains(&(party_id as u8)) {
390                    // for participant who lost their key_share, first point should be IDENTITY
391                    if points.next() != Some(&ProjectivePoint::IDENTITY) {
392                        return Err(KeygenError::InvalidPolynomialPoint);
393                    }
394                }
395            }
396            if points.any(|p| p.is_identity().into()) {
397                return Err(KeygenError::InvalidPolynomialPoint);
398            }
399        }
400
401        verify_dlog_proofs(
402            &keyshare.final_session_id,
403            party_id,
404            dlog_proofs_i,
405            &big_f_i_vector.coeffs,
406        )?;
407    }
408
409    // 6.d
410    let mut big_f_vec = GroupPolynomial::identity(T);
411    for v in big_f_i_vecs.iter() {
412        big_f_vec.add_mut(v); // big_f_vec += v; big_vec +
413    }
414
415    let public_key = big_f_vec.get_constant();
416
417    if let Some(v) = key_refresh_data {
418        if public_key != v.expected_public_key {
419            return Err(KeygenError::InvalidKeyRefresh);
420        }
421    }
422
423    Round::new(setup.total_participants() - 1, DKG_MSG_OT1, relay)
424        .of_encrypted_messages(
425            &setup,
426            &mut scheme,
427            0,
428            KeygenError::AbortProtocol,
429            |base_ot_msg1: &EndemicOTMsg1, receiver_index, _, scheme| {
430                let receiver_id = receiver_index as u8;
431                let rank = setup.participant_rank(receiver_id as usize);
432
433                let trailer = big_f_vec.external_size();
434
435                let mut enc_buf = EncryptedMessage::<KeygenMsg3>::new(
436                    &setup.msg_id(Some(receiver_id as usize), DKG_MSG_R3),
437                    setup.message_ttl().as_secs() as _,
438                    0,
439                    trailer,
440                    scheme,
441                );
442
443                let (msg3, trailer) = enc_buf.payload(scheme);
444
445                let sender_ot_seed = {
446                    let sid = get_base_ot_session_id(
447                        receiver_id,
448                        my_party_id,
449                        &keyshare.final_session_id,
450                    );
451
452                    block_in_place(|| {
453                        EndemicOTSender::process(
454                            &sid,
455                            base_ot_msg1,
456                            &mut msg3.base_ot_msg2,
457                            &mut rng,
458                        )
459                    })
460                    .map_err(|_| KeygenError::InvalidMessage)?
461                };
462
463                let all_but_one_session_id = get_all_but_one_session_id(
464                    my_party_id as usize,
465                    receiver_id as usize,
466                    &keyshare.final_session_id,
467                );
468
469                build_pprf(
470                    &all_but_one_session_id,
471                    &sender_ot_seed,
472                    &mut keyshare.other_mut(receiver_id).send_ot_seed,
473                    &mut msg3.pprf_output,
474                );
475
476                if receiver_id > my_party_id {
477                    rng.fill_bytes(&mut msg3.seed_i_j);
478                    keyshare.each_mut(receiver_id - 1).zeta_seed =
479                        msg3.seed_i_j;
480                };
481
482                let x_i = &x_i_list[receiver_id as usize];
483                let d_i = block_in_place(|| {
484                    polynomial.derivative_at(rank as usize, x_i)
485                });
486
487                msg3.d_i = encode_scalar(&d_i);
488                msg3.chain_code_sid = chain_code_sid;
489                msg3.r_i_2 = r_i_2;
490
491                big_f_vec.write(trailer);
492
493                Ok(Some(
494                    enc_buf
495                        .encrypt(scheme, receiver_id as usize)
496                        .ok_or(KeygenError::SendMessage)?,
497                ))
498            },
499        )
500        .await?;
501
502    let mut chain_code_sids =
503        Pairs::new_with_item(my_party_id, chain_code_sid);
504
505    if let Some(v) = key_refresh_data {
506        if v.lost_keyshare_party_ids.contains(&my_party_id) {
507            chain_code_sids = Pairs::new();
508        }
509    }
510
511    Round::new(setup.total_participants() - 1, DKG_MSG_R3, relay)
512        .of_encrypted_messages(
513            &setup,
514            &mut scheme,
515            big_f_vec.external_size(),
516            KeygenError::AbortProtocol,
517            |msg3: &KeygenMsg3, party_index, trailer, _| {
518                let party_id = party_index as u8;
519                let msg3_big_f_vec =
520                    <GroupPolynomial<ProjectivePoint> as Wrap>::read(trailer)
521                        .ok_or(KeygenError::InvalidMessage)?;
522
523                // also checks that msg3.big_f_vec.coeffs.len() == T
524                if msg3_big_f_vec != big_f_vec {
525                    return Err(KeygenError::BigFVecMismatch);
526                }
527
528                d_i_list[party_id as usize] = decode_scalar(&msg3.d_i)
529                    .ok_or(KeygenError::InvalidMessage)?;
530
531                let receiver = base_ot_receivers.pop_pair(party_id);
532                let receiver_output =
533                    block_in_place(|| receiver.process(&msg3.base_ot_msg2))
534                        .map_err(|_| KeygenError::InvalidMessage)?;
535                let all_but_one_session_id = get_all_but_one_session_id(
536                    party_id as usize,
537                    my_party_id as usize,
538                    &keyshare.final_session_id,
539                );
540
541                block_in_place(|| {
542                    eval_pprf(
543                        &all_but_one_session_id,
544                        &receiver_output,
545                        &msg3.pprf_output,
546                        &mut keyshare.other_mut(party_id).recv_ot_seed,
547                    )
548                })
549                .map_err(KeygenError::PPRFError)?;
550
551                if party_id < my_party_id {
552                    keyshare.each_mut(party_id).zeta_seed = msg3.seed_i_j;
553                }
554
555                // Verify commitments
556                let commitment_2 = &commitment_list_2[party_id as usize];
557                let commit_hash = hash_commitment_2(
558                    &keyshare.final_session_id,
559                    &msg3.chain_code_sid,
560                    &msg3.r_i_2,
561                );
562
563                bool::from(commit_hash.ct_eq(commitment_2))
564                    .then_some(())
565                    .ok_or(KeygenError::InvalidCommitmentHash)?;
566
567                if let Some(v) = key_refresh_data {
568                    if !v.lost_keyshare_party_ids.contains(&party_id) {
569                        chain_code_sids.push(party_id, msg3.chain_code_sid);
570                    }
571                } else {
572                    chain_code_sids.push(party_id, msg3.chain_code_sid);
573                }
574
575                Ok(None)
576            },
577        )
578        .await?;
579
580    if key_refresh_data.is_some() {
581        let chain_code_sids = chain_code_sids.remove_ids();
582        if chain_code_sids.is_empty() {
583            return Err(KeygenError::InvalidKeyRefresh);
584        }
585        let root_chain_code = chain_code_sids[0];
586        if !chain_code_sids.iter().all(|&item| item == root_chain_code) {
587            return Err(KeygenError::InvalidKeyRefresh);
588        }
589        // Use already existing root_chain_code
590        keyshare.info_mut().root_chain_code = root_chain_code;
591    } else {
592        // Generate common root_chain_code from chain_code_sids
593        keyshare.info_mut().root_chain_code = chain_code_sids
594            .iter()
595            .fold(Sha256::new(), |hash, (_, sid)| hash.chain_update(sid))
596            .finalize()
597            .into();
598    }
599
600    if big_f_i_vecs.len() != d_i_list.len() {
601        return Err(KeygenError::FailedFelmanVerify);
602    }
603
604    for (big_f_i_vec, f_i_val) in big_f_i_vecs.into_iter().zip(&d_i_list) {
605        let coeffs = block_in_place(|| {
606            big_f_i_vec.derivative_coeffs(my_rank as usize)
607        });
608        let valid = feldman_verify(
609            coeffs,
610            &x_i_list[my_party_id as usize],
611            f_i_val,
612            &ProjectivePoint::GENERATOR,
613        );
614        if !valid {
615            return Err(KeygenError::FailedFelmanVerify);
616        }
617    }
618
619    let s_i: Scalar = d_i_list.iter().sum();
620    let big_s_i = ProjectivePoint::GENERATOR * s_i;
621
622    // Use the root_chain_code in the final dlog proof
623    // so that all parties are sure they generated the same root_chain_code
624    let final_session_id_with_root_chain_code = {
625        let mut buf = [0u8; 32];
626        let mut transcript = Transcript::new(&DKG_LABEL);
627        transcript
628            .append_message(b"final_session_id", &keyshare.final_session_id);
629        transcript
630            .append_message(b"root_chain_code", &keyshare.root_chain_code);
631        transcript
632            .challenge_bytes(&DLOG_SESSION_ID_WITH_CHAIN_CODE, &mut buf);
633        SessionId::new(buf)
634    };
635
636    let proof = {
637        let mut transcript = Transcript::new_dlog_proof(
638            &final_session_id_with_root_chain_code,
639            my_party_id as usize,
640            &DLOG_PROOF2_LABEL,
641            &DKG_LABEL,
642        );
643
644        DLogProof::prove(
645            &s_i,
646            &ProjectivePoint::GENERATOR,
647            &mut transcript,
648            &mut rng,
649        )
650    };
651
652    let (public_key_list, big_s_list, proof_list, _) = broadcast_4(
653        &setup,
654        relay,
655        DKG_MSG_R4,
656        (public_key, big_s_i, proof, ()),
657    )
658    .await?;
659
660    if public_key_list.into_iter().any(|pk| pk != public_key) {
661        return Err(KeygenError::PublicKeyMismatch);
662    }
663
664    if big_s_list.len() != proof_list.len() {
665        return Err(KeygenError::InvalidDLogProof);
666    }
667
668    for (party_id, (big_s_i, dlog_proof)) in
669        big_s_list.iter().zip(proof_list.into_iter()).enumerate()
670    {
671        if party_id == my_party_id as usize {
672            continue;
673        }
674
675        let mut transcript = Transcript::new_dlog_proof(
676            &final_session_id_with_root_chain_code,
677            party_id,
678            &DLOG_PROOF2_LABEL,
679            &DKG_LABEL,
680        );
681
682        if dlog_proof
683            .verify(big_s_i, &ProjectivePoint::GENERATOR, &mut transcript)
684            .unwrap_u8()
685            == 0
686        {
687            return Err(KeygenError::InvalidDLogProof);
688        }
689    }
690
691    for (party_id, x_i) in x_i_list.iter().enumerate() {
692        let party_rank = setup.participant_rank(party_id);
693
694        let coeff_multipliers =
695            polynomial_coeff_multipliers(x_i, party_rank as usize, N);
696
697        let expected_point: ProjectivePoint = big_f_vec
698            .points()
699            .zip(coeff_multipliers)
700            .map(|(point, coeff)| point * &coeff)
701            .sum();
702
703        if expected_point != big_s_list[party_id] {
704            return Err(KeygenError::BigSMismatch);
705        }
706    }
707
708    // TODO:(sushi) Only for birkhoff now (with ranks), support lagrange later.
709    let rank_list = (0..setup.total_participants())
710        .map(|p| setup.participant_rank(p))
711        .collect::<Vec<_>>();
712
713    // FIXME: do we need this?
714    check_secret_recovery(&x_i_list, &rank_list, &big_s_list, &public_key)?;
715
716    keyshare.info_mut().public_key = encode_point(&public_key);
717    keyshare.info_mut().s_i = encode_scalar(&s_i);
718    keyshare.info_mut().key_id = setup.derive_key_id(&public_key.to_bytes());
719
720    for p in 0..N {
721        let each = keyshare.each_mut(p as u8);
722
723        each.x_i = encode_scalar(&x_i_list[p]);
724        each.big_s = encode_point(&big_s_list[p]);
725        each.rank = rank_list[p];
726    }
727
728    Ok(keyshare)
729}
730
731/// Computes a commitment hash for the DKG protocol
732///
733/// This function creates a cryptographic commitment for a participant's polynomial
734/// and other protocol parameters.
735///
736/// # Arguments
737///
738/// * `session_id` - The session identifier
739/// * `party_id` - The ID of the participant
740/// * `rank` - The rank of the participant
741/// * `x_i` - The x-coordinate for the participant's share
742/// * `big_f_i_vec` - The vector of polynomial commitments
743/// * `r_i` - Random value for the commitment
744///
745/// # Returns
746///
747/// A 32-byte hash representing the commitment
748fn hash_commitment(
749    session_id: &SessionId,
750    party_id: usize,
751    rank: usize,
752    x_i: &NonZeroScalar,
753    big_f_i_vec: &GroupPolynomial<ProjectivePoint>,
754    r_i: &[u8; 32],
755) -> [u8; 32] {
756    let mut hasher = Sha256::new();
757    hasher.update(DKG_LABEL);
758    hasher.update(session_id);
759    hasher.update((party_id as u64).to_be_bytes());
760    hasher.update((rank as u64).to_be_bytes());
761    hasher.update(x_i.to_bytes());
762    for point in big_f_i_vec.points() {
763        hasher.update(point.to_bytes());
764    }
765    hasher.update(r_i);
766    hasher.update(COMMITMENT_1_LABEL);
767
768    hasher.finalize().into()
769}
770
771/// Computes a secondary commitment hash
772///
773/// This function creates a commitment hash for chain code and session information.
774///
775/// # Arguments
776///
777/// * `session_id` - The session identifier
778/// * `chain_code_sid` - The chain code session identifier
779/// * `r_i` - Random value for the commitment
780///
781/// # Returns
782///
783/// A 32-byte hash representing the commitment
784fn hash_commitment_2(
785    session_id: &[u8],
786    chain_code_sid: &[u8; 32],
787    r_i: &[u8; 32],
788) -> [u8; 32] {
789    let mut hasher = Sha256::new();
790    hasher.update(DKG_LABEL);
791    hasher.update(session_id);
792    hasher.update(chain_code_sid);
793    hasher.update(r_i);
794    hasher.update(COMMITMENT_2_LABEL);
795
796    hasher.finalize().into()
797}
798
799/// Generates a session ID for base oblivious transfer
800///
801/// This function creates a unique session ID for the base oblivious transfer
802/// protocol between two participants.
803///
804/// # Arguments
805///
806/// * `sender_id` - The ID of the sender
807/// * `receiver_id` - The ID of the receiver
808/// * `session_id` - The base session identifier
809///
810/// # Returns
811///
812/// A new session ID for the oblivious transfer protocol
813pub(crate) fn get_base_ot_session_id(
814    sender_id: u8,
815    receiver_id: u8,
816    session_id: &[u8],
817) -> SessionId {
818    SessionId::new(
819        Sha256::new()
820            .chain_update(DKG_LABEL)
821            .chain_update(session_id)
822            .chain_update(b"sender_id")
823            .chain_update((sender_id as u64).to_be_bytes())
824            .chain_update(b"receiver_id")
825            .chain_update((receiver_id as u64).to_be_bytes())
826            .chain_update(b"base_ot_session_id")
827            .finalize()
828            .into(),
829    )
830}
831
832/// Generates a session ID for all-but-one protocol
833///
834/// This function creates a unique session ID for the all-but-one protocol
835/// between two participants.
836///
837/// # Arguments
838///
839/// * `sender_id` - The ID of the sender
840/// * `receiver_id` - The ID of the receiver
841/// * `session_id` - The base session identifier
842///
843/// # Returns
844///
845/// A new session ID for the all-but-one protocol
846pub(crate) fn get_all_but_one_session_id(
847    sender_id: usize,
848    receiver_id: usize,
849    session_id: &[u8],
850) -> SessionId {
851    SessionId::new(
852        Sha256::new()
853            .chain_update(DKG_LABEL)
854            .chain_update(session_id)
855            .chain_update(b"sender_id")
856            .chain_update((sender_id as u64).to_be_bytes())
857            .chain_update(b"receiver_id")
858            .chain_update((receiver_id as u64).to_be_bytes())
859            .chain_update(b"all_but_one_session_id")
860            .finalize()
861            .into(),
862    )
863}
864
865/// Verifies discrete logarithm proofs
866///
867/// This function verifies the discrete logarithm proofs provided by participants
868/// to ensure the correctness of their polynomial commitments.
869///
870/// # Arguments
871///
872/// * `final_session_id` - The final session identifier
873/// * `party_id` - The ID of the participant
874/// * `proofs` - The vector of discrete logarithm proofs
875/// * `points` - The vector of points to verify against
876///
877/// # Returns
878///
879/// * `Ok(())` - If all proofs are valid
880/// * `Err(KeygenError)` - If any proof is invalid
881fn verify_dlog_proofs(
882    final_session_id: &[u8],
883    party_id: usize,
884    proofs: &[DLogProof],
885    points: &[ProjectivePoint],
886) -> Result<(), KeygenError> {
887    let mut dlog_transcript = Transcript::new_dlog_proof(
888        final_session_id,
889        party_id,
890        &DLOG_PROOF1_LABEL,
891        &DKG_LABEL,
892    );
893
894    let mut ok = Choice::from(1);
895    for (proof, point) in proofs.iter().zip(points) {
896        ok &= proof.verify(
897            point,
898            &ProjectivePoint::GENERATOR,
899            &mut dlog_transcript,
900        );
901    }
902
903    if ok.unwrap_u8() == 0 {
904        return Err(KeygenError::InvalidDLogProof);
905    }
906
907    Ok(())
908}
909
910/// Broadcasts four messages to all participants
911///
912/// This function handles the broadcasting of four different message types
913/// to all participants in the protocol.
914///
915/// # Type Parameters
916///
917/// * `P` - A type implementing the `ProtocolParticipant` trait
918/// * `R` - A type implementing the `Relay` trait
919/// * `T1` - Type of the first message
920/// * `T2` - Type of the second message
921/// * `T3` - Type of the third message
922/// * `T4` - Type of the fourth message
923///
924/// # Arguments
925///
926/// * `setup` - The protocol setup configuration
927/// * `relay` - The message relay for communication
928/// * `tag` - The message tag for filtering
929/// * `msg` - Tuple containing the four messages to broadcast
930///
931/// # Returns
932///
933/// * `Ok((Vec<T1>, Vec<T2>, Vec<T3>, Vec<T4>))` - Vectors of received messages
934/// * `Err(KeygenError)` - If the broadcast fails
935pub(crate) async fn broadcast_4<P, R, T1, T2, T3, T4>(
936    setup: &P,
937    relay: &mut FilteredMsgRelay<R>,
938    tag: MessageTag,
939    msg: (T1, T2, T3, T4),
940) -> Result<(Vec<T1>, Vec<T2>, Vec<T3>, Vec<T4>), KeygenError>
941where
942    P: ProtocolParticipant,
943    R: Relay,
944    T1: Wrap,
945    T2: Wrap,
946    T3: Wrap,
947    T4: Wrap,
948{
949    let (v0, v1, v2, v3) =
950        Round::new(setup.total_participants() - 1, tag, relay)
951            .broadcast_4(setup, msg)
952            .await?;
953
954    Ok((v0.into(), v1.into(), v2.into(), v3.into()))
955}
956
957#[cfg(test)]
958mod tests {
959    use super::*;
960
961    use tokio::task::JoinSet;
962
963    use sl_mpc_mate::coord::{
964        adversary::{EvilMessageRelay, EvilPlay},
965        {MessageRelayService, SimpleMessageRelay},
966    };
967
968    use crate::{keygen::utils::setup_keygen, setup::keygen::SetupMessage};
969
970    async fn sim<S, R>(t: u8, ranks: &[u8], coord: S) -> Vec<Keyshare>
971    where
972        S: MessageRelayService<MessageRelay = R>,
973        R: Relay + Send + 'static,
974    {
975        let parties = setup_keygen(None, t, ranks.len() as u8, Some(ranks));
976        sim_parties(parties, coord).await
977    }
978
979    async fn sim_parties<S, R>(
980        parties: Vec<(SetupMessage, [u8; 32])>,
981        coord: S,
982    ) -> Vec<Keyshare>
983    where
984        S: MessageRelayService<MessageRelay = R>,
985        R: Send + Relay + 'static,
986    {
987        let mut jset = JoinSet::new();
988        for (setup, seed) in parties {
989            let relay = coord.connect().await.unwrap();
990
991            jset.spawn(run(setup, seed, relay));
992        }
993
994        let mut shares = vec![];
995
996        while let Some(fini) = jset.join_next().await {
997            let fini = fini.unwrap();
998
999            if let Err(ref err) = fini {
1000                println!("error {}", err);
1001            }
1002
1003            let share = fini.unwrap();
1004
1005            println!(
1006                "PK {}",
1007                share
1008                    .public_key()
1009                    .to_bytes()
1010                    .iter()
1011                    .map(|v| format!("{:02X}", v))
1012                    .collect::<Vec<_>>()
1013                    .join(".")
1014            );
1015
1016            shares.push(share);
1017        }
1018
1019        shares
1020    }
1021
1022    #[tokio::test(flavor = "multi_thread")]
1023    async fn dkg_r1() {
1024        sim(2, &[0, 1, 1], SimpleMessageRelay::new()).await;
1025    }
1026
1027    #[tokio::test(flavor = "multi_thread")]
1028    async fn keyshares() {
1029        let shares = sim(2, &[0, 1, 1], SimpleMessageRelay::new()).await;
1030
1031        for s in &shares {
1032            let bytes = s.as_slice().to_vec();
1033
1034            let _reloaded = Keyshare::from_vec(bytes).unwrap();
1035        }
1036    }
1037
1038    #[tokio::test(flavor = "multi_thread")]
1039    async fn n1() {
1040        let parties = setup_keygen(None, 2, 3, None);
1041
1042        let play = EvilPlay::new().drop_message(MsgId::ZERO_ID, None);
1043
1044        sim_parties(parties, EvilMessageRelay::new(play)).await;
1045    }
1046
1047    #[tokio::test(flavor = "multi_thread")]
1048    async fn inject_random_messages() {
1049        let parties = setup_keygen(None, 2, 3, None);
1050
1051        // We could extract protocol instance ID and signing keys of
1052        // all parties to generate messages to inject into the
1053        // execution.
1054        let _intance = parties[0].0.instance_id();
1055
1056        let mut rng = rand::thread_rng();
1057
1058        let mut play = EvilPlay::new().drop_message(MsgId::ZERO_ID, None);
1059
1060        for _ in 0..3 {
1061            let mut bytes = [0u8; 2000];
1062            rng.fill_bytes(&mut bytes);
1063            play = play.inject_message(bytes.into(), |_, _| true);
1064        }
1065
1066        let msg_id = parties[0].0.msg_id_from(
1067            0, //parties[0].0.verifier(0),
1068            None, DKG_MSG_R1,
1069        );
1070
1071        let mut bad_msg = vec![]; //Vec::<u8>::with_capacity(32 + 4 + 32 + 100);
1072
1073        // the first 32 bytes is message ID,
1074        bad_msg.extend(msg_id.as_slice());
1075        bad_msg.extend(10u32.to_le_bytes());
1076        bad_msg.extend(0u64.to_le_bytes()); // payload
1077        bad_msg.extend([0u8; 32]); // bad signature
1078
1079        let play = play.inject_message(bad_msg, |_, p| p == 1);
1080
1081        sim_parties(parties, EvilMessageRelay::new(play)).await;
1082    }
1083}