Skip to main content

mpvss_rs/
participant.rs

1// Copyright 2020-2026 MathxH Chen.
2//
3// Code is licensed under MIT Apache Dual License
4
5//! Participant implementation supporting multiple cryptographic groups.
6//!
7//! This module provides `Participant<G: Group>` which works with any group
8//! implementation (MODP, secp256k1, etc.), enabling the PVSS scheme to use different
9//! cryptographic backends.
10
11use num_bigint::{BigInt, BigUint, ToBigInt};
12use num_integer::Integer;
13use num_traits::identities::{One, Zero};
14use sha2::{Digest, Sha256};
15use std::collections::{BTreeMap, HashMap};
16use std::sync::Arc;
17
18use crate::dleq::DLEQ;
19use crate::group::Group;
20use crate::groups::ModpGroup;
21use crate::polynomial::Polynomial;
22use crate::sharebox::{DistributionSharesBox, ShareBox};
23
24// secp256k1-specific imports (only available when feature is enabled)
25
26use crate::groups::Secp256k1Group;
27
28use k256::elliptic_curve::FieldBytes;
29
30use k256::elliptic_curve::ff::PrimeField;
31
32use k256::{AffinePoint, Scalar};
33
34// Ristretto255-specific imports
35use crate::groups::Ristretto255Group;
36
37use curve25519_dalek::ristretto::RistrettoPoint;
38
39use curve25519_dalek::scalar::Scalar as RistrettoScalar;
40
41// Type aliases for convenience
42// Note: These are already defined in sharebox.rs but re-exported here for convenience
43
44// ============================================================================
45// Participant
46// ============================================================================
47
48/// Participant that works with any cryptographic group.
49///
50/// # Type Parameters
51/// - `G`: A type implementing the `Group` trait (e.g., `ModpGroup`, `Secp256k1Group`)
52///
53/// # Example
54///
55/// ```rust
56/// use mpvss_rs::groups::ModpGroup;
57/// use mpvss_rs::participant::Participant;
58///
59/// let group = ModpGroup::new();
60/// let mut dealer = Participant::with_arc(group);
61/// dealer.initialize();
62/// ```
63#[derive(Debug)]
64pub struct Participant<G: Group> {
65    group: Arc<G>,
66    pub privatekey: G::Scalar,
67    pub publickey: G::Element,
68}
69
70// Manual Clone implementation that doesn't require G: Clone
71// Only requires G::Scalar and G::Element to be Clone
72impl<G: Group> Clone for Participant<G>
73where
74    G::Scalar: Clone,
75    G::Element: Clone,
76{
77    fn clone(&self) -> Self {
78        Participant {
79            group: Arc::clone(&self.group),
80            privatekey: self.privatekey.clone(),
81            publickey: self.publickey.clone(),
82        }
83    }
84}
85
86impl<G: Group> Participant<G> {
87    /// Create a new generic participant with an Arc-wrapped group.
88    ///
89    /// # Example
90    ///
91    /// ```rust
92    /// use mpvss_rs::groups::ModpGroup;
93    /// use mpvss_rs::Participant;
94    ///
95    /// let group = ModpGroup::new();
96    /// let participant = Participant::with_arc(group);
97    /// ```
98    pub fn with_arc(group: Arc<G>) -> Self
99    where
100        G::Scalar: Default,
101        G::Element: Default,
102    {
103        Participant {
104            group,
105            privatekey: Default::default(),
106            publickey: Default::default(),
107        }
108    }
109
110    /// Create a new generic participant, wrapping the group in Arc internally.
111    ///
112    /// This method takes a group by value and wraps it in an Arc internally.
113    /// For ModpGroup, since `ModpGroup::new()` already returns `Arc<ModpGroup>`,
114    /// use `with_arc()` instead.
115    ///
116    /// # Example
117    ///
118    /// ```rust
119    /// use mpvss_rs::groups::ModpGroup;
120    /// use mpvss_rs::Participant;
121    ///
122    /// // For ModpGroup, use with_arc since ModpGroup::new() returns Arc<ModpGroup>
123    /// let group = ModpGroup::new();
124    /// let participant = Participant::with_arc(group);
125    /// ```
126    pub fn new(group: G) -> Self
127    where
128        G::Scalar: Default,
129        G::Element: Default,
130    {
131        Participant {
132            group: Arc::new(group),
133            privatekey: Default::default(),
134            publickey: Default::default(),
135        }
136    }
137
138    /// Initialize the participant by generating a key pair.
139    pub fn initialize(&mut self)
140    where
141        G::Scalar: Default,
142        G::Element: Default,
143    {
144        self.privatekey = self.group.generate_private_key();
145        self.publickey = self.group.generate_public_key(&self.privatekey);
146    }
147}
148
149// ============================================================================
150// ModpGroup-Specific Implementation
151// ============================================================================
152
153/// Full PVSS distribute_secret implementation for ModpGroup.
154///
155/// Note: This implementation uses Group trait abstraction where possible,
156/// but some BigInt operations remain for non-group computations (Lagrange coefficients,
157/// polynomial arithmetic, etc.).
158impl Participant<ModpGroup> {
159    /// Distribute a secret among participants (full implementation for ModpGroup).
160    pub fn distribute_secret(
161        &mut self,
162        secret: &BigInt,
163        publickeys: &[BigInt],
164        threshold: u32,
165    ) -> DistributionSharesBox<ModpGroup> {
166        assert!(threshold <= publickeys.len() as u32);
167
168        // Group generators
169        let subgroup_gen = self.group.subgroup_generator();
170        let main_gen = self.group.generator();
171        let group_order = self.group.order();
172
173        // Generate random polynomial (coefficients are scalars in Z_q)
174        let mut polynomial = Polynomial::new();
175        polynomial.init((threshold - 1) as i32, group_order);
176
177        // Generate random witness w (scalar)
178        let w = self.group.generate_private_key();
179
180        // Data structures
181        let mut commitments: Vec<BigInt> = Vec::new();
182        let mut positions: HashMap<Vec<u8>, i64> = HashMap::new();
183        let mut x: HashMap<Vec<u8>, BigInt> = HashMap::new();
184        let mut shares: HashMap<Vec<u8>, BigInt> = HashMap::new();
185        let mut challenge_hasher = Sha256::new();
186
187        let mut sampling_points: HashMap<Vec<u8>, BigInt> = HashMap::new();
188        let mut dleq_w: HashMap<Vec<u8>, BigInt> = HashMap::new();
189        let mut position: i64 = 1;
190
191        // Calculate commitments C_j = g^a_j using group.exp()
192        for j in 0..threshold {
193            let coeff = &polynomial.coefficients[j as usize];
194            let commitment = self.group.exp(&subgroup_gen, coeff);
195            commitments.push(commitment);
196        }
197
198        // Calculate encrypted shares for each participant
199        for pubkey in publickeys {
200            let pubkey_bytes = self.group.element_to_bytes(pubkey);
201            positions.insert(pubkey_bytes.clone(), position);
202
203            // P(position) mod order (scalar arithmetic)
204            let pos_scalar = &BigInt::from(position);
205            let secret_share = polynomial.get_value(pos_scalar) % group_order;
206            sampling_points.insert(pubkey_bytes.clone(), secret_share.clone());
207
208            // Calculate X_i = g^P(i) using commitments and group operations
209            // X_i = ∏_{j=0}^{t-1} C_j^{i^j} where C_j are commitments
210            let mut x_val = self.group.identity();
211            let mut exponent = BigInt::one();
212            for j in 0..threshold {
213                let c_j_pow =
214                    self.group.exp(&commitments[j as usize], &exponent);
215                x_val = self.group.mul(&x_val, &c_j_pow);
216                exponent =
217                    self.group.scalar_mul(&exponent, pos_scalar) % group_order;
218            }
219            x.insert(pubkey_bytes.clone(), x_val.clone());
220
221            // Calculate Y_i = y_i^P(i) (encrypted share) using group.exp()
222            let encrypted_secret_share = self.group.exp(pubkey, &secret_share);
223            shares.insert(pubkey_bytes.clone(), encrypted_secret_share.clone());
224
225            // Generate DLEQ proof: DLEQ(g, X_i, y_i, Y_i)
226            let mut dleq = DLEQ::new(self.group.clone());
227            dleq.init(
228                subgroup_gen.clone(),
229                x_val.clone(),
230                pubkey.clone(),
231                encrypted_secret_share.clone(),
232                secret_share.clone(),
233                w.clone(),
234            );
235            dleq_w.insert(pubkey_bytes.clone(), w.clone());
236
237            // Update challenge hash - use same format as legacy implementation
238            let a1 = dleq.get_a1();
239            let a2 = dleq.get_a2();
240            challenge_hasher.update(
241                x_val.to_biguint().unwrap().to_str_radix(10).as_bytes(),
242            );
243            challenge_hasher.update(
244                encrypted_secret_share
245                    .to_biguint()
246                    .unwrap()
247                    .to_str_radix(10)
248                    .as_bytes(),
249            );
250            challenge_hasher
251                .update(a1.to_biguint().unwrap().to_str_radix(10).as_bytes());
252            challenge_hasher
253                .update(a2.to_biguint().unwrap().to_str_radix(10).as_bytes());
254
255            position += 1;
256        }
257
258        // Compute common challenge using group operations
259        let challenge_hash = challenge_hasher.finalize();
260        let challenge = self.group.hash_to_scalar(&challenge_hash);
261
262        // Compute responses using scalar arithmetic
263        let mut responses: HashMap<Vec<u8>, BigInt> = HashMap::new();
264        for pubkey in publickeys {
265            let pubkey_bytes = self.group.element_to_bytes(pubkey);
266            let alpha = sampling_points.get(&pubkey_bytes).unwrap();
267            let w_i = dleq_w.get(&pubkey_bytes).unwrap();
268            let alpha_c =
269                self.group.scalar_mul(alpha, &challenge) % group_order;
270            let response = self.group.scalar_sub(w_i, &alpha_c) % group_order;
271            responses.insert(pubkey_bytes, response);
272        }
273
274        // Compute U = secret XOR H(G^s) using group.exp()
275        let s = polynomial.get_value(&BigInt::zero()) % group_order;
276        let g_s = self.group.exp(&main_gen, &s);
277        let sha256_hash = Sha256::digest(
278            g_s.to_biguint().unwrap().to_str_radix(10).as_bytes(),
279        );
280        let hash_biguint = BigUint::from_bytes_be(&sha256_hash[..])
281            .mod_floor(&self.group.modulus().to_biguint().unwrap());
282        let u = secret.to_biguint().unwrap() ^ hash_biguint;
283
284        // Build shares box
285        let mut shares_box = DistributionSharesBox::new();
286        shares_box.init(
287            &commitments,
288            positions,
289            shares,
290            publickeys,
291            &challenge,
292            responses,
293            &u.to_bigint().unwrap(),
294        );
295        shares_box
296    }
297
298    /// Extract a secret share from the distribution box (ModpGroup implementation).
299    ///
300    /// # Parameters
301    /// - `shares_box`: The distribution shares box from the dealer
302    /// - `private_key`: The participant's private key
303    /// - `w`: Random witness for DLEQ proof
304    pub fn extract_secret_share(
305        &self,
306        shares_box: &DistributionSharesBox<ModpGroup>,
307        private_key: &BigInt,
308        w: &BigInt,
309    ) -> Option<ShareBox<ModpGroup>> {
310        use crate::util::Util;
311
312        let main_gen = self.group.generator();
313        let group_order = self.group.order();
314
315        // Generate public key from private key using group method
316        let public_key = self.group.generate_public_key(private_key);
317
318        // Get encrypted share from distribution box
319        let pubkey_bytes = self.group.element_to_bytes(&public_key);
320        let encrypted_secret_share = shares_box.shares.get(&pubkey_bytes)?;
321
322        // Decryption: S_i = Y_i^(1/x_i)
323        // Note: This requires modular inverse which is not a group operation
324        let privkey_inverse = Util::mod_inverse(private_key, group_order)?;
325        let decrypted_share =
326            self.group.exp(encrypted_secret_share, &privkey_inverse);
327
328        // Generate DLEQ proof: DLEQ(G, publickey, decrypted_share, encrypted_secret_share)
329        let mut dleq = DLEQ::new(self.group.clone());
330        dleq.init(
331            main_gen.clone(),
332            public_key.clone(),
333            decrypted_share.clone(),
334            encrypted_secret_share.clone(),
335            private_key.clone(),
336            w.clone(),
337        );
338
339        // Compute challenge using group operations
340        let mut challenge_hasher = Sha256::new();
341        challenge_hasher.update(
342            public_key.to_biguint().unwrap().to_str_radix(10).as_bytes(),
343        );
344        challenge_hasher.update(
345            encrypted_secret_share
346                .to_biguint()
347                .unwrap()
348                .to_str_radix(10)
349                .as_bytes(),
350        );
351
352        let a1 = dleq.get_a1();
353        let a2 = dleq.get_a2();
354        challenge_hasher
355            .update(a1.to_biguint().unwrap().to_str_radix(10).as_bytes());
356        challenge_hasher
357            .update(a2.to_biguint().unwrap().to_str_radix(10).as_bytes());
358
359        let challenge_hash = challenge_hasher.finalize();
360        let challenge = self.group.hash_to_scalar(&challenge_hash);
361        dleq.c = Some(challenge.clone());
362
363        // Compute response using scalar arithmetic
364        let response = dleq.get_r()?;
365
366        // Build share box
367        let mut share_box = ShareBox::new();
368        share_box.init(public_key, decrypted_share, challenge, response);
369        Some(share_box)
370    }
371
372    /// Verify a decrypted share (ModpGroup implementation).
373    ///
374    /// # Parameters
375    /// - `sharebox`: The share box containing the decrypted share
376    /// - `distribution_sharebox`: The distribution shares box from the dealer
377    /// - `publickey`: The public key of the participant who created the share
378    pub fn verify_share(
379        &self,
380        sharebox: &ShareBox<ModpGroup>,
381        distribution_sharebox: &DistributionSharesBox<ModpGroup>,
382        publickey: &BigInt,
383    ) -> bool {
384        let main_gen = self.group.generator();
385
386        // Get encrypted share from distribution box
387        let pubkey_bytes = self.group.element_to_bytes(publickey);
388        let encrypted_share =
389            match distribution_sharebox.shares.get(&pubkey_bytes) {
390                Some(s) => s,
391                None => return false,
392            };
393
394        // Verify DLEQ proof using group operations
395        // a_1 = G^r * publickey^c, a_2 = decrypted_share^r * encrypted_share^c
396        let g1_r = self.group.exp(&main_gen, &sharebox.response);
397        let h1_c = self.group.exp(publickey, &sharebox.challenge);
398        let a1_verify = self.group.mul(&g1_r, &h1_c);
399
400        let g2_r = self.group.exp(&sharebox.share, &sharebox.response);
401        let h2_c = self.group.exp(encrypted_share, &sharebox.challenge);
402        let a2_verify = self.group.mul(&g2_r, &h2_c);
403
404        // Compute challenge hash and verify
405        let mut challenge_hasher = Sha256::new();
406        challenge_hasher.update(
407            publickey.to_biguint().unwrap().to_str_radix(10).as_bytes(),
408        );
409        challenge_hasher.update(
410            encrypted_share
411                .to_biguint()
412                .unwrap()
413                .to_str_radix(10)
414                .as_bytes(),
415        );
416        challenge_hasher.update(
417            a1_verify.to_biguint().unwrap().to_str_radix(10).as_bytes(),
418        );
419        challenge_hasher.update(
420            a2_verify.to_biguint().unwrap().to_str_radix(10).as_bytes(),
421        );
422
423        let challenge_hash = challenge_hasher.finalize();
424        let challenge_computed = self.group.hash_to_scalar(&challenge_hash);
425
426        challenge_computed == sharebox.challenge
427    }
428
429    /// Verify distribution shares box (ModpGroup implementation).
430    ///
431    /// Verifies that all encrypted shares are consistent with the commitments.
432    /// This is the public verifiability part of PVSS - anyone can verify the dealer
433    /// didn't cheat.
434    ///
435    /// # Parameters
436    /// - `distribute_sharesbox`: The distribution shares box to verify
437    ///
438    /// # Returns
439    /// `true` if the distribution is valid, `false` otherwise
440    pub fn verify_distribution_shares(
441        &self,
442        distribute_sharesbox: &DistributionSharesBox<ModpGroup>,
443    ) -> bool {
444        let subgroup_gen = self.group.subgroup_generator();
445        let group_order = self.group.order();
446        let mut challenge_hasher = Sha256::new();
447
448        // Verify each participant's encrypted share and accumulate hash
449        for publickey in &distribute_sharesbox.publickeys {
450            let pubkey_bytes = self.group.element_to_bytes(publickey);
451            let position = distribute_sharesbox.positions.get(&pubkey_bytes);
452            let response = distribute_sharesbox.responses.get(&pubkey_bytes);
453            let encrypted_share =
454                distribute_sharesbox.shares.get(&pubkey_bytes);
455
456            if position.is_none()
457                || response.is_none()
458                || encrypted_share.is_none()
459            {
460                return false;
461            }
462
463            // Calculate X_i = ∏_{j=0}^{t-1} C_j^{i^j} using group operations
464            let mut x_val = self.group.identity();
465            let mut exponent = BigInt::one();
466            for j in 0..distribute_sharesbox.commitments.len() {
467                let c_j_pow = self
468                    .group
469                    .exp(&distribute_sharesbox.commitments[j], &exponent);
470                x_val = self.group.mul(&x_val, &c_j_pow);
471                exponent = self
472                    .group
473                    .scalar_mul(&exponent, &BigInt::from(*position.unwrap()))
474                    % group_order;
475            }
476
477            // Verify DLEQ proof for this participant
478            // DLEQ(g, X_i, y_i, Y_i): proves log_g(X_i) = log_{y_i}(Y_i)
479            // a_1 = g^r * X_i^c, a_2 = y_i^r * Y_i^c
480            let g_r = self.group.exp(&subgroup_gen, response.unwrap());
481            let x_c = self.group.exp(&x_val, &distribute_sharesbox.challenge);
482            let a1 = self.group.mul(&g_r, &x_c);
483
484            let y_r = self.group.exp(publickey, response.unwrap());
485            let y_c = self
486                .group
487                .exp(encrypted_share.unwrap(), &distribute_sharesbox.challenge);
488            let a2 = self.group.mul(&y_r, &y_c);
489
490            // Update hash with X_i, Y_i, a_1, a_2
491            challenge_hasher.update(
492                x_val.to_biguint().unwrap().to_str_radix(10).as_bytes(),
493            );
494            challenge_hasher.update(
495                encrypted_share
496                    .unwrap()
497                    .to_biguint()
498                    .unwrap()
499                    .to_str_radix(10)
500                    .as_bytes(),
501            );
502            challenge_hasher
503                .update(a1.to_biguint().unwrap().to_str_radix(10).as_bytes());
504            challenge_hasher
505                .update(a2.to_biguint().unwrap().to_str_radix(10).as_bytes());
506        }
507
508        // Calculate final challenge and check if it matches c
509        let challenge_hash = challenge_hasher.finalize();
510        let computed_challenge = self.group.hash_to_scalar(&challenge_hash);
511
512        computed_challenge == distribute_sharesbox.challenge
513    }
514
515    /// Reconstruct secret from shares (ModpGroup implementation).
516    ///
517    /// # Parameters
518    /// - `share_boxes`: Array of share boxes from participants
519    /// - `distribute_share_box`: The distribution shares box from the dealer
520    pub fn reconstruct(
521        &self,
522        share_boxes: &[ShareBox<ModpGroup>],
523        distribute_share_box: &DistributionSharesBox<ModpGroup>,
524    ) -> Option<BigInt> {
525        use rayon::prelude::*;
526
527        if share_boxes.len() < distribute_share_box.commitments.len() {
528            return None;
529        }
530
531        let group_modulus = self.group.modulus();
532
533        // Build position -> share map
534        let mut shares: BTreeMap<i64, BigInt> = BTreeMap::new();
535        for share_box in share_boxes.iter() {
536            let pubkey_bytes =
537                self.group.element_to_bytes(&share_box.publickey);
538            let position = distribute_share_box.positions.get(&pubkey_bytes)?;
539            shares.insert(*position, share_box.share.clone());
540        }
541
542        // Compute Lagrange factors and G^s = ∏ S_i^λ_i
543        let mut secret = self.group.identity();
544        let values: Vec<i64> = shares.keys().copied().collect();
545        let shares_vec: Vec<(i64, BigInt)> = shares.into_iter().collect();
546        let shares_slice = shares_vec.as_slice();
547
548        let factors: Vec<BigInt> = shares_slice
549            .par_iter()
550            .map(|(position, share)| {
551                self.compute_lagrange_factor(
552                    *position,
553                    share,
554                    &values,
555                    group_modulus,
556                )
557            })
558            .collect();
559
560        // Multiply all factors using group.mul()
561        secret = factors
562            .into_iter()
563            .fold(secret, |acc, factor| self.group.mul(&acc, &factor));
564
565        // Reconstruct secret = H(G^s) XOR U
566        let secret_hash = Sha256::digest(
567            secret.to_biguint().unwrap().to_str_radix(10).as_bytes(),
568        );
569        let hash_biguint = BigUint::from_bytes_be(&secret_hash[..])
570            .mod_floor(&self.group.modulus().to_biguint().unwrap());
571        let decrypted_secret =
572            hash_biguint ^ distribute_share_box.U.to_biguint().unwrap();
573
574        Some(decrypted_secret.to_bigint().unwrap())
575    }
576
577    /// Compute Lagrange factor for secret reconstruction.
578    /// Compute Lagrange factor for secret reconstruction.
579    ///
580    /// Note: Lagrange coefficient computation is pure scalar arithmetic (not group operation),
581    /// but the final exponentiation uses group.exp().
582    fn compute_lagrange_factor(
583        &self,
584        position: i64,
585        share: &BigInt,
586        values: &[i64],
587        group_modulus: &BigInt,
588    ) -> BigInt {
589        use crate::util::Util;
590
591        let lagrange_coefficient =
592            Util::lagrange_coefficient(&position, values);
593
594        // Compute exponent: λ_i (may be fractional, needs modular inverse)
595        let exponent = if lagrange_coefficient.1 == BigInt::from(1) {
596            // Lagrange coefficient is an integer
597            lagrange_coefficient.0.clone() / Util::abs(&lagrange_coefficient.1)
598        } else {
599            // Lagrange coefficient is a proper fraction
600            let mut numerator = lagrange_coefficient.0.to_biguint().unwrap();
601            let mut denominator =
602                Util::abs(&lagrange_coefficient.1).to_biguint().unwrap();
603            let gcd = numerator.gcd(&denominator);
604            numerator /= &gcd;
605            denominator /= &gcd;
606
607            let group_order_minus_1 = group_modulus - BigInt::one();
608            let inverse_denominator = Util::mod_inverse(
609                &denominator.to_bigint().unwrap(),
610                &group_order_minus_1,
611            );
612
613            match inverse_denominator {
614                Some(inv) => {
615                    (numerator.to_bigint().unwrap() * inv) % group_order_minus_1
616                }
617                None => {
618                    // If denominator has no inverse, return identity element
619                    // This should not happen in normal operation with valid shares
620                    BigInt::one()
621                }
622            }
623        };
624
625        // Compute S_i^λ_i using group.exp()
626        let mut factor = self.group.exp(share, &exponent);
627
628        // Handle negative Lagrange coefficient using element_inverse
629        if lagrange_coefficient.0.clone() * lagrange_coefficient.1
630            < BigInt::zero()
631            && let Some(inverse_factor) = self.group.element_inverse(&factor)
632        {
633            factor = inverse_factor;
634        }
635
636        factor
637    }
638}
639
640// Type aliases for convenience
641/// Type alias for MODP group participant (backward compatible)
642pub type ModpParticipant = Participant<ModpGroup>;
643
644#[cfg(test)]
645mod tests {
646    use super::*;
647    use crate::groups::ModpGroup;
648    use crate::participant::Participant;
649    use num_bigint::RandBigInt;
650
651    #[test]
652    fn test_generic_modp_participant_new() {
653        let group = ModpGroup::new();
654        let participant = Participant::with_arc(group);
655        assert_eq!(participant.publickey, Default::default());
656    }
657
658    #[test]
659    fn test_generic_modp_participant_initialize() {
660        let group = ModpGroup::new();
661        let mut participant = Participant::with_arc(group);
662        participant.initialize();
663        let _ = &participant.privatekey;
664        let _ = &participant.publickey;
665    }
666
667    /// End-to-end test for distribute, extract, and reconstruct.
668    #[test]
669    fn test_end_to_end_modp() {
670        use num_bigint::{BigUint, ToBigInt};
671
672        // Setup participants
673        let group = ModpGroup::new();
674        let mut dealer = Participant::with_arc(group.clone());
675        dealer.initialize();
676
677        let mut p1 = Participant::with_arc(group.clone());
678        let mut p2 = Participant::with_arc(group.clone());
679        let mut p3 = Participant::with_arc(group.clone());
680        p1.initialize();
681        p2.initialize();
682        p3.initialize();
683
684        let secret_message = String::from("Hello MPVSS End-to-End Test!");
685        let secret = BigUint::from_bytes_be(secret_message.as_bytes());
686
687        let publickeys = vec![
688            p1.publickey.clone(),
689            p2.publickey.clone(),
690            p3.publickey.clone(),
691        ];
692        let threshold = 3;
693
694        // Distribute secret
695        let dist_box = dealer.distribute_secret(
696            &secret.to_bigint().unwrap(),
697            &publickeys,
698            threshold,
699        );
700
701        // ===== Step 1: Verify distribution =====
702        // Each participant should verify the distribution is valid
703        let verified_by_p1 = dealer.verify_distribution_shares(&dist_box);
704        let verified_by_p2 = dealer.verify_distribution_shares(&dist_box);
705        let verified_by_p3 = dealer.verify_distribution_shares(&dist_box);
706        assert!(verified_by_p1, "P1 should verify distribution as valid");
707        assert!(verified_by_p2, "P2 should verify distribution as valid");
708        assert!(verified_by_p3, "P3 should verify distribution as valid");
709
710        // Verify distribution box structure
711        assert_eq!(dist_box.publickeys.len(), 3, "Should have 3 public keys");
712        assert_eq!(dist_box.commitments.len(), 3, "Should have 3 commitments");
713        assert_eq!(dist_box.shares.len(), 3, "Should have 3 shares");
714        assert_ne!(dist_box.U, BigInt::zero(), "U should not be zero");
715
716        // Generate random witness for share extraction
717        let mut rng = rand::thread_rng();
718        let w: BigInt = rng
719            .gen_biguint_below(&group.modulus().to_biguint().unwrap())
720            .to_bigint()
721            .unwrap();
722
723        // ===== Step 2: Extract shares =====
724        let s1 = p1
725            .extract_secret_share(&dist_box, &p1.privatekey, &w)
726            .unwrap();
727        let s2 = p2
728            .extract_secret_share(&dist_box, &p2.privatekey, &w)
729            .unwrap();
730        let s3 = p3
731            .extract_secret_share(&dist_box, &p3.privatekey, &w)
732            .unwrap();
733
734        // Verify extracted shares structure
735        assert_eq!(s1.publickey, p1.publickey, "P1 publickey should match");
736        assert_ne!(s1.share, BigInt::zero(), "P1 share should not be zero");
737
738        assert_eq!(s2.publickey, p2.publickey, "P2 publickey should match");
739        assert_ne!(s2.share, BigInt::zero(), "P2 share should not be zero");
740
741        assert_eq!(s3.publickey, p3.publickey, "P3 publickey should match");
742        assert_ne!(s3.share, BigInt::zero(), "P3 share should not be zero");
743
744        // ===== Step 3: Verify each extracted share =====
745        // Each participant can verify other participants' shares
746        let p1_verifies_s2 = dealer.verify_share(&s2, &dist_box, &p2.publickey);
747        let p1_verifies_s3 = dealer.verify_share(&s3, &dist_box, &p3.publickey);
748        assert!(p1_verifies_s2, "P1 should verify P2's share as valid");
749        assert!(p1_verifies_s3, "P1 should verify P3's share as valid");
750
751        let p2_verifies_s1 = dealer.verify_share(&s1, &dist_box, &p1.publickey);
752        let p2_verifies_s3 = dealer.verify_share(&s3, &dist_box, &p3.publickey);
753        assert!(p2_verifies_s1, "P2 should verify P1's share as valid");
754        assert!(p2_verifies_s3, "P2 should verify P3's share as valid");
755
756        let p3_verifies_s1 = dealer.verify_share(&s1, &dist_box, &p1.publickey);
757        let p3_verifies_s2 = dealer.verify_share(&s2, &dist_box, &p2.publickey);
758        assert!(p3_verifies_s1, "P3 should verify P1's share as valid");
759        assert!(p3_verifies_s2, "P3 should verify P2's share as valid");
760
761        // ===== Step 4: Reconstruct secret from verified shares =====
762        let shares = vec![s1, s2, s3];
763        let reconstructed = dealer.reconstruct(&shares, &dist_box).unwrap();
764
765        // Verify reconstructed secret matches original
766        let reconstructed_message = String::from_utf8(
767            reconstructed.to_biguint().unwrap().to_bytes_be(),
768        )
769        .unwrap();
770        assert_eq!(
771            reconstructed_message, secret_message,
772            "Reconstructed message should match original"
773        );
774    }
775
776    // ========================================================================
777    // secp256k1 Tests
778    // ========================================================================
779
780    /// End-to-end test for secp256k1: distribute, extract, and reconstruct.
781
782    #[test]
783    fn test_end_to_end_secp256k1() {
784        use num_bigint::{BigUint, ToBigInt};
785
786        // Setup participants
787        let group = Secp256k1Group::new();
788        let mut dealer = Participant::with_arc(group.clone());
789        dealer.initialize();
790
791        let mut p1 = Participant::with_arc(group.clone());
792        let mut p2 = Participant::with_arc(group.clone());
793        let mut p3 = Participant::with_arc(group.clone());
794        p1.initialize();
795        p2.initialize();
796        p3.initialize();
797
798        let secret_message = String::from("Hello secp256k1 PVSS!");
799        let secret = BigUint::from_bytes_be(secret_message.as_bytes());
800
801        let publickeys: Vec<k256::AffinePoint> = vec![
802            p1.publickey.clone(),
803            p2.publickey.clone(),
804            p3.publickey.clone(),
805        ];
806        let threshold = 3;
807
808        // Distribute secret
809        let dist_box = dealer.distribute_secret(
810            &secret.to_bigint().unwrap(),
811            &publickeys,
812            threshold,
813        );
814
815        // Verify distribution
816        assert!(
817            dealer.verify_distribution_shares(&dist_box),
818            "Distribution should be valid"
819        );
820
821        // Generate random witness
822        let w = group.generate_private_key();
823
824        // Extract shares
825        let s1 = p1
826            .extract_secret_share(&dist_box, &p1.privatekey, &w)
827            .unwrap();
828        let s2 = p2
829            .extract_secret_share(&dist_box, &p2.privatekey, &w)
830            .unwrap();
831        let s3 = p3
832            .extract_secret_share(&dist_box, &p3.privatekey, &w)
833            .unwrap();
834
835        // Verify shares
836        assert!(
837            dealer.verify_share(&s1, &dist_box, &p1.publickey),
838            "P1's share should be valid"
839        );
840        assert!(
841            dealer.verify_share(&s3, &dist_box, &p3.publickey),
842            "P3's share should be valid"
843        );
844
845        // Reconstruct from all 3 shares
846        let shares = vec![s1, s2, s3];
847        let reconstructed = dealer.reconstruct(&shares, &dist_box).unwrap();
848
849        // Verify reconstructed secret matches original
850        let reconstructed_message = String::from_utf8(
851            reconstructed.to_biguint().unwrap().to_bytes_be(),
852        )
853        .unwrap();
854        assert_eq!(
855            reconstructed_message, secret_message,
856            "Reconstructed message should match original"
857        );
858    }
859
860    /// Threshold test for secp256k1: 3-of-5 reconstruction.
861
862    #[test]
863    fn test_threshold_secp256k1() {
864        use num_bigint::{BigUint, ToBigInt};
865
866        // Setup 5 participants with threshold 3
867        let group = Secp256k1Group::new();
868        let mut dealer = Participant::with_arc(group.clone());
869        dealer.initialize();
870
871        let mut p1 = Participant::with_arc(group.clone());
872        let mut p2 = Participant::with_arc(group.clone());
873        let mut p3 = Participant::with_arc(group.clone());
874        let mut p4 = Participant::with_arc(group.clone());
875        let mut p5 = Participant::with_arc(group.clone());
876        p1.initialize();
877        p2.initialize();
878        p3.initialize();
879        p4.initialize();
880        p5.initialize();
881
882        let secret_message = String::from("Threshold test secp256k1!");
883        let secret = BigUint::from_bytes_be(secret_message.as_bytes());
884
885        let publickeys: Vec<k256::AffinePoint> = vec![
886            p1.publickey.clone(),
887            p2.publickey.clone(),
888            p3.publickey.clone(),
889            p4.publickey.clone(),
890            p5.publickey.clone(),
891        ];
892        let threshold = 3;
893
894        // Distribute secret
895        let dist_box = dealer.distribute_secret(
896            &secret.to_bigint().unwrap(),
897            &publickeys,
898            threshold,
899        );
900
901        // Verify distribution
902        assert!(
903            dealer.verify_distribution_shares(&dist_box),
904            "Distribution should be valid"
905        );
906
907        // Generate random witness
908        let w = group.generate_private_key();
909
910        // Extract only 3 shares (threshold)
911        let s1 = p1
912            .extract_secret_share(&dist_box, &p1.privatekey, &w)
913            .unwrap();
914        let s3 = p3
915            .extract_secret_share(&dist_box, &p3.privatekey, &w)
916            .unwrap();
917        let s5 = p5
918            .extract_secret_share(&dist_box, &p5.privatekey, &w)
919            .unwrap();
920
921        // Reconstruct from 3 shares
922        let shares = vec![s1, s3, s5];
923        let reconstructed = dealer.reconstruct(&shares, &dist_box).unwrap();
924
925        // Verify reconstructed secret matches original
926        let reconstructed_message = String::from_utf8(
927            reconstructed.to_biguint().unwrap().to_bytes_be(),
928        )
929        .unwrap();
930        assert_eq!(
931            reconstructed_message, secret_message,
932            "Reconstructed message should match original"
933        );
934    }
935
936    /// Basic DLEQ test for secp256k1 to verify scalar conversions.
937
938    #[test]
939    fn test_scalar_arithmetic_secp256k1() {
940        use num_bigint::BigInt;
941
942        let group = Secp256k1Group::new();
943
944        // Test: If s1 = a + b, then s1 * g should equal a*g + b*g
945        let a_bigint = BigInt::from(5u32);
946        let b_bigint = BigInt::from(7u32);
947        let s_bigint = &a_bigint + &b_bigint; // 12
948
949        // Convert to Scalars
950        let a = Scalar::from_repr({
951            let mut fb = FieldBytes::<k256::Secp256k1>::default();
952            let b = a_bigint.to_bytes_be().1;
953            if b.len() < 32 {
954                fb[32 - b.len()..].copy_from_slice(&b);
955            } else {
956                fb.copy_from_slice(&b[..32]);
957            }
958            fb.into()
959        })
960        .unwrap();
961
962        let b = Scalar::from_repr({
963            let mut fb = FieldBytes::<k256::Secp256k1>::default();
964            let b = b_bigint.to_bytes_be().1;
965            if b.len() < 32 {
966                fb[32 - b.len()..].copy_from_slice(&b);
967            } else {
968                fb.copy_from_slice(&b[..32]);
969            }
970            fb.into()
971        })
972        .unwrap();
973
974        let s = Scalar::from_repr({
975            let mut fb = FieldBytes::<k256::Secp256k1>::default();
976            let b = s_bigint.to_bytes_be().1;
977            if b.len() < 32 {
978                fb[32 - b.len()..].copy_from_slice(&b);
979            } else {
980                fb.copy_from_slice(&b[..32]);
981            }
982            fb.into()
983        })
984        .unwrap();
985
986        // Test: s * g == a*g + b*g == (a+b)*g
987        let g = group.generator();
988
989        let a_times_g = group.exp(&g, &a);
990        let b_times_g = group.exp(&g, &b);
991        let s_times_g = group.exp(&g, &s);
992
993        let sum_ab_g = group.mul(&a_times_g, &b_times_g);
994
995        assert_eq!(
996            sum_ab_g, s_times_g,
997            "Scalar arithmetic: (a+b)*g should equal a*g + b*g"
998        );
999    }
1000
1001    /// Basic DLEQ test for secp256k1 to verify scalar conversions.
1002
1003    #[test]
1004    fn test_dleq_basic_secp256k1() {
1005        let group = Secp256k1Group::new();
1006        let mut dealer = Participant::with_arc(group.clone());
1007        dealer.initialize();
1008
1009        // Create a simple DLEQ proof
1010        let alpha = group.generate_private_key();
1011        let w = group.generate_private_key();
1012
1013        // g1 = g, h1 = g^alpha
1014        let g1 = group.generator();
1015        let h1 = group.exp(&g1, &alpha);
1016
1017        // g2 = some public key, h2 = g2^alpha
1018        let mut p2 = Participant::with_arc(group.clone());
1019        p2.initialize();
1020        let g2 = p2.publickey;
1021        let h2 = group.exp(&g2, &alpha);
1022
1023        // Create DLEQ
1024        let mut dleq = DLEQ::new(group.clone());
1025        dleq.init(g1, h1, g2, h2, alpha, w);
1026
1027        // Compute a1, a2
1028        let a1 = dleq.get_a1();
1029        let a2 = dleq.get_a2();
1030
1031        // Compute challenge
1032        use sha2::{Digest, Sha256};
1033        let mut hasher = Sha256::new();
1034        hasher.update(group.element_to_bytes(&h1));
1035        hasher.update(group.element_to_bytes(&h2));
1036        hasher.update(group.element_to_bytes(&a1));
1037        hasher.update(group.element_to_bytes(&a2));
1038        let hash = hasher.finalize();
1039        let challenge = group.hash_to_scalar(&hash);
1040
1041        dleq.c = Some(challenge.clone());
1042        let response = dleq.get_r().unwrap();
1043        dleq.r = Some(response);
1044
1045        // Verify should succeed
1046        assert!(dleq.verify(), "Basic DLEQ proof should verify");
1047    }
1048
1049    /// DLEQ proof verification test for secp256k1.
1050
1051    #[test]
1052    fn test_dleq_proofs_secp256k1() {
1053        use num_bigint::{BigUint, ToBigInt};
1054
1055        let group = Secp256k1Group::new();
1056        let mut dealer = Participant::with_arc(group.clone());
1057        dealer.initialize();
1058
1059        let mut p1 = Participant::with_arc(group.clone());
1060        let mut p2 = Participant::with_arc(group.clone());
1061        p1.initialize();
1062        p2.initialize();
1063
1064        let secret = BigUint::from_bytes_be(b"DLEQ test secp256k1");
1065
1066        let publickeys: Vec<k256::AffinePoint> =
1067            vec![p1.publickey.clone(), p2.publickey.clone()];
1068        let threshold = 2;
1069
1070        // Distribute secret
1071        let dist_box = dealer.distribute_secret(
1072            &secret.to_bigint().unwrap(),
1073            &publickeys,
1074            threshold,
1075        );
1076
1077        // Verify DLEQ proofs
1078        assert!(
1079            dealer.verify_distribution_shares(&dist_box),
1080            "Distribution DLEQ proofs should be valid"
1081        );
1082
1083        // Generate random witness
1084        let w = group.generate_private_key();
1085
1086        // Extract and verify shares
1087        let s1 = p1
1088            .extract_secret_share(&dist_box, &p1.privatekey, &w)
1089            .unwrap();
1090        let s2 = p2
1091            .extract_secret_share(&dist_box, &p2.privatekey, &w)
1092            .unwrap();
1093
1094        // Verify share DLEQ proofs
1095        assert!(
1096            dealer.verify_share(&s1, &dist_box, &p1.publickey),
1097            "P1's DLEQ proof should be valid"
1098        );
1099        assert!(
1100            dealer.verify_share(&s2, &dist_box, &p2.publickey),
1101            "P2's DLEQ proof should be valid"
1102        );
1103    }
1104}
1105
1106// ============================================================================
1107// Secp256k1Group-Specific Implementation
1108// ============================================================================
1109
1110/// Full PVSS implementation for Secp256k1Group (elliptic curve group).
1111///
1112/// This implementation adapts the PVSS scheme for elliptic curve cryptography,
1113/// using the k256 library's Scalar and AffinePoint types.
1114///
1115/// Key differences from ModpGroup:
1116/// - Elements are EC points (AffinePoint) instead of BigInt
1117/// - Scalars are k256::Scalar (32 bytes) instead of BigInt
1118/// - Hashing uses compressed point encoding (33 bytes SEC1 format)
1119/// - No modulus concept (EC groups are prime-order)
1120///
1121/// Note: Uses Vec<u8> (serialized points) as HashMap keys since AffinePoint
1122/// doesn't implement Hash.
1123impl Participant<Secp256k1Group> {
1124    /// Distribute a secret among participants (full implementation for Secp256k1Group).
1125    ///
1126    /// # Parameters
1127    /// - `secret`: The value to be shared (as BigInt for cross-group compatibility)
1128    /// - `publickeys`: Array of public keys (EC points) of each participant
1129    /// - `threshold`: Number of shares needed for reconstruction
1130    ///
1131    /// Returns a `DistributionSharesBox` containing encrypted shares and DLEQ proofs
1132    pub fn distribute_secret(
1133        &mut self,
1134        secret: &BigInt,
1135        publickeys: &[AffinePoint],
1136        threshold: u32,
1137    ) -> DistributionSharesBox<Secp256k1Group> {
1138        assert!(threshold <= publickeys.len() as u32);
1139
1140        // Group generators
1141        let subgroup_gen = self.group.subgroup_generator();
1142        let main_gen = self.group.generator();
1143        let _group_order = self.group.order(); // Stored for API compatibility, actual order from order_as_bigint()
1144
1145        // Generate random polynomial (coefficients are BigInt, converted to Scalar later)
1146        let mut polynomial = Polynomial::new();
1147        // Use BigInt for polynomial arithmetic (compatible with Polynomial module)
1148        // For secp256k1, use order_as_bigint() to get the actual curve order as BigInt
1149
1150        let group_order_bigint = self.group.order_as_bigint().clone();
1151        polynomial.init((threshold - 1) as i32, &group_order_bigint);
1152
1153        // Generate random witness w (scalar)
1154        let w = self.group.generate_private_key();
1155
1156        // Data structures - use Vec<u8> keys (serialized points) since AffinePoint doesn't implement Hash
1157        let mut commitments: Vec<AffinePoint> = Vec::new();
1158        let mut positions: std::collections::HashMap<Vec<u8>, i64> =
1159            std::collections::HashMap::new();
1160        let mut shares: std::collections::HashMap<Vec<u8>, AffinePoint> =
1161            std::collections::HashMap::new();
1162        let mut challenge_hasher = Sha256::new();
1163
1164        let mut sampling_points: std::collections::HashMap<Vec<u8>, Scalar> =
1165            std::collections::HashMap::new();
1166        let mut dleq_w: std::collections::HashMap<Vec<u8>, Scalar> =
1167            std::collections::HashMap::new();
1168        let mut position: i64 = 1;
1169
1170        // Calculate commitments C_j = a_j * g (scalar multiplication)
1171        for j in 0..threshold {
1172            let coeff_bigint = &polynomial.coefficients[j as usize];
1173            // Convert BigInt coefficient to bytes (big-endian) and ensure exactly 32 bytes
1174            // k256 Scalar::from_repr expects big-endian representation
1175            let coeff_bytes = coeff_bigint.to_bytes_be().1;
1176            let mut field_bytes = FieldBytes::<k256::Secp256k1>::default();
1177            if coeff_bytes.len() < 32 {
1178                // Right-align for big-endian (copy to the end of the array)
1179                field_bytes[32 - coeff_bytes.len()..]
1180                    .copy_from_slice(&coeff_bytes);
1181            } else {
1182                field_bytes.copy_from_slice(&coeff_bytes[..32]);
1183            }
1184            let coeff = Scalar::from_repr(field_bytes).unwrap();
1185            let commitment = self.group.exp(&subgroup_gen, &coeff);
1186            commitments.push(commitment);
1187        }
1188
1189        // Calculate encrypted shares for each participant
1190        for pubkey in publickeys.iter() {
1191            let pubkey_bytes = self.group.element_to_bytes(pubkey);
1192            positions.insert(pubkey_bytes.clone(), position);
1193
1194            // P(position) as Scalar
1195            let pos_scalar = BigInt::from(position);
1196            let secret_share_bigint = polynomial.get_value(&pos_scalar);
1197            // CRITICAL: Must take mod order BEFORE converting to Scalar
1198            let secret_share_mod = &secret_share_bigint % &group_order_bigint;
1199            // Use big-endian representation for k256 Scalar
1200            let secret_share_bytes = secret_share_mod.to_bytes_be().1;
1201            let mut field_bytes = FieldBytes::<k256::Secp256k1>::default();
1202            if secret_share_bytes.len() < 32 {
1203                // Right-align for big-endian
1204                field_bytes[32 - secret_share_bytes.len()..]
1205                    .copy_from_slice(&secret_share_bytes);
1206            } else {
1207                field_bytes.copy_from_slice(&secret_share_bytes[..32]);
1208            }
1209            let secret_share = Scalar::from_repr(field_bytes).unwrap();
1210            sampling_points.insert(pubkey_bytes.clone(), secret_share);
1211            dleq_w.insert(pubkey_bytes.clone(), w);
1212
1213            // Calculate X_i = Σ_j (position^j) * C_j (using EC operations)
1214            let mut x_val = self.group.identity();
1215            let mut exponent = Scalar::ONE;
1216            for j in 0..threshold {
1217                // C_j^(i^j) in EC notation = (i^j) * C_j (scalar multiplication)
1218                let c_j_pow =
1219                    self.group.exp(&commitments[j as usize], &exponent);
1220                x_val = self.group.mul(&x_val, &c_j_pow);
1221                // exponent *= position (mod order)
1222                let pos_scalar = Scalar::from(position as u64);
1223                exponent = self.group.scalar_mul(&exponent, &pos_scalar);
1224            }
1225
1226            // Calculate Y_i = secret_share * y_i (encrypted share)
1227            let encrypted_secret_share = self.group.exp(pubkey, &secret_share);
1228            shares.insert(pubkey_bytes.clone(), encrypted_secret_share);
1229
1230            // Generate DLEQ proof: DLEQ(g, X_i, y_i, Y_i)
1231            let mut dleq = DLEQ::new(self.group.clone());
1232            dleq.init(
1233                subgroup_gen,
1234                x_val,
1235                *pubkey,
1236                encrypted_secret_share,
1237                secret_share,
1238                w,
1239            );
1240
1241            // Update challenge hash - use element_to_bytes() for EC points
1242            let a1 = dleq.get_a1();
1243            let a2 = dleq.get_a2();
1244
1245            challenge_hasher.update(self.group.element_to_bytes(&x_val));
1246            challenge_hasher
1247                .update(self.group.element_to_bytes(&encrypted_secret_share));
1248            challenge_hasher.update(self.group.element_to_bytes(&a1));
1249            challenge_hasher.update(self.group.element_to_bytes(&a2));
1250
1251            position += 1;
1252        }
1253
1254        // Compute common challenge
1255        let challenge_hash = challenge_hasher.finalize();
1256        let challenge = self.group.hash_to_scalar(&challenge_hash);
1257
1258        // Compute responses: r_i = w - alpha_i * c
1259        let mut responses: std::collections::HashMap<Vec<u8>, Scalar> =
1260            std::collections::HashMap::new();
1261        for pubkey in publickeys {
1262            let pubkey_bytes = self.group.element_to_bytes(pubkey);
1263            let alpha = sampling_points.get(&pubkey_bytes).unwrap();
1264            let alpha_c = self.group.scalar_mul(alpha, &challenge);
1265            let w_i = dleq_w.get(&pubkey_bytes).unwrap();
1266            let response = self.group.scalar_sub(w_i, &alpha_c);
1267
1268            responses.insert(pubkey_bytes, response);
1269        }
1270
1271        // Compute U = secret XOR H(G^s)
1272        let s_bigint = polynomial.get_value(&BigInt::zero());
1273        let s_bytes = s_bigint.to_bytes_be().1;
1274        let mut field_bytes = FieldBytes::<k256::Secp256k1>::default();
1275        if s_bytes.len() < 32 {
1276            field_bytes[32 - s_bytes.len()..].copy_from_slice(&s_bytes);
1277        } else {
1278            field_bytes.copy_from_slice(&s_bytes[s_bytes.len() - 32..]);
1279        }
1280        let s = Scalar::from_repr(field_bytes).unwrap();
1281        let g_s = self.group.exp(&main_gen, &s);
1282
1283        // Hash the EC point to bytes
1284        let sha256_hash = Sha256::digest(self.group.element_to_bytes(&g_s));
1285        // Convert hash to BigUint and reduce modulo curve order
1286        let mut field_bytes2 = FieldBytes::<k256::Secp256k1>::default();
1287        let hash_len = sha256_hash.len().min(field_bytes2.len());
1288        field_bytes2[32 - hash_len..].copy_from_slice(&sha256_hash[..hash_len]);
1289        let hash_scalar = Scalar::from_repr(field_bytes2).unwrap();
1290        let hash_bytes = hash_scalar.to_bytes();
1291        let hash_biguint = BigUint::from_bytes_be(&hash_bytes);
1292        // For EC, we use the curve order as the modulus for U encoding
1293
1294        let curve_order_bigint = BigUint::from_bytes_be(
1295            &self.group.order_as_bigint().to_bytes_be().1,
1296        );
1297        let hash_reduced = hash_biguint % curve_order_bigint;
1298        let u = secret.to_biguint().unwrap() ^ hash_reduced;
1299
1300        // Build shares box
1301        let mut shares_box = DistributionSharesBox::new();
1302        shares_box.init(
1303            &commitments,
1304            positions,
1305            shares,
1306            publickeys,
1307            &challenge,
1308            responses,
1309            &u.to_bigint().unwrap(),
1310        );
1311        shares_box
1312    }
1313
1314    /// Extract a secret share from the distribution box (Secp256k1Group implementation).
1315    ///
1316    /// # Parameters
1317    /// - `shares_box`: The distribution shares box from the dealer
1318    /// - `private_key`: The participant's private key (Scalar)
1319    /// - `w`: Random witness for DLEQ proof (Scalar)
1320    pub fn extract_secret_share(
1321        &self,
1322        shares_box: &DistributionSharesBox<Secp256k1Group>,
1323        private_key: &Scalar,
1324        w: &Scalar,
1325    ) -> Option<ShareBox<Secp256k1Group>> {
1326        let main_gen = self.group.generator();
1327
1328        // Generate public key from private key using group method
1329        let public_key = self.group.generate_public_key(private_key);
1330
1331        // Get encrypted share from distribution box (serialize key for HashMap lookup)
1332        let public_key_bytes = self.group.element_to_bytes(&public_key);
1333        let encrypted_secret_share =
1334            shares_box.shares.get(&public_key_bytes)?;
1335
1336        // Decryption: S_i = Y_i^(1/x_i) using scalar_inverse
1337        let privkey_inverse = self.group.scalar_inverse(private_key)?;
1338        let decrypted_share =
1339            self.group.exp(encrypted_secret_share, &privkey_inverse);
1340
1341        // Generate DLEQ proof: DLEQ(G, publickey, decrypted_share, encrypted_secret_share)
1342        let mut dleq = DLEQ::new(self.group.clone());
1343        dleq.init(
1344            main_gen,
1345            public_key,
1346            decrypted_share,
1347            *encrypted_secret_share,
1348            *private_key,
1349            *w,
1350        );
1351
1352        // Compute challenge using element_to_bytes() for EC points
1353        let mut challenge_hasher = Sha256::new();
1354        challenge_hasher.update(self.group.element_to_bytes(&public_key));
1355        challenge_hasher
1356            .update(self.group.element_to_bytes(encrypted_secret_share));
1357
1358        let a1 = dleq.get_a1();
1359        let a2 = dleq.get_a2();
1360        challenge_hasher.update(self.group.element_to_bytes(&a1));
1361        challenge_hasher.update(self.group.element_to_bytes(&a2));
1362
1363        let challenge_hash = challenge_hasher.finalize();
1364        let challenge = self.group.hash_to_scalar(&challenge_hash);
1365        dleq.c = Some(challenge);
1366
1367        // Compute response using scalar arithmetic
1368        let response = dleq.get_r()?;
1369
1370        // Build share box
1371        let mut share_box = ShareBox::new();
1372        share_box.init(public_key, decrypted_share, challenge, response);
1373        Some(share_box)
1374    }
1375
1376    /// Verify a decrypted share (Secp256k1Group implementation).
1377    ///
1378    /// # Parameters
1379    /// - `sharebox`: The share box containing the decrypted share
1380    /// - `distribution_sharebox`: The distribution shares box from the dealer
1381    /// - `publickey`: The public key (EC point) of the participant who created the share
1382    pub fn verify_share(
1383        &self,
1384        sharebox: &ShareBox<Secp256k1Group>,
1385        distribution_sharebox: &DistributionSharesBox<Secp256k1Group>,
1386        publickey: &AffinePoint,
1387    ) -> bool {
1388        let main_gen = self.group.generator();
1389
1390        // Get encrypted share from distribution box (serialize key for HashMap lookup)
1391        let publickey_bytes = self.group.element_to_bytes(publickey);
1392        let encrypted_share =
1393            match distribution_sharebox.shares.get(&publickey_bytes) {
1394                Some(s) => s,
1395                None => return false,
1396            };
1397
1398        // Verify DLEQ proof using EC operations
1399        // a_1 = r*G + c*publickey (point addition)
1400        let g1_r = self.group.exp(&main_gen, &sharebox.response);
1401        let h1_c = self.group.exp(publickey, &sharebox.challenge);
1402        let a1_verify = self.group.mul(&g1_r, &h1_c);
1403
1404        // a_2 = r*decrypted_share + c*encrypted_share
1405        let g2_r = self.group.exp(&sharebox.share, &sharebox.response);
1406        let h2_c = self.group.exp(encrypted_share, &sharebox.challenge);
1407        let a2_verify = self.group.mul(&g2_r, &h2_c);
1408
1409        // Compute challenge hash using element_to_bytes() for EC points
1410        let mut challenge_hasher = Sha256::new();
1411        challenge_hasher.update(self.group.element_to_bytes(publickey));
1412        challenge_hasher.update(self.group.element_to_bytes(encrypted_share));
1413        challenge_hasher.update(self.group.element_to_bytes(&a1_verify));
1414        challenge_hasher.update(self.group.element_to_bytes(&a2_verify));
1415
1416        let challenge_hash = challenge_hasher.finalize();
1417        let challenge_computed = self.group.hash_to_scalar(&challenge_hash);
1418
1419        challenge_computed == sharebox.challenge
1420    }
1421
1422    /// Verify distribution shares box (Secp256k1Group implementation).
1423    ///
1424    /// Verifies that all encrypted shares are consistent with the commitments.
1425    /// This is the public verifiability part of PVSS - anyone can verify the dealer
1426    /// didn't cheat.
1427    ///
1428    /// # Parameters
1429    /// - `distribute_sharesbox`: The distribution shares box to verify
1430    ///
1431    /// # Returns
1432    /// `true` if the distribution is valid, `false` otherwise
1433    pub fn verify_distribution_shares(
1434        &self,
1435        distribute_sharesbox: &DistributionSharesBox<Secp256k1Group>,
1436    ) -> bool {
1437        let subgroup_gen = self.group.subgroup_generator();
1438        let mut challenge_hasher = Sha256::new();
1439
1440        // Verify each participant's encrypted share and accumulate hash
1441        for publickey in distribute_sharesbox.publickeys.iter() {
1442            let publickey_bytes = self.group.element_to_bytes(publickey);
1443            let position = distribute_sharesbox.positions.get(&publickey_bytes);
1444            let response = distribute_sharesbox.responses.get(&publickey_bytes);
1445            let encrypted_share =
1446                distribute_sharesbox.shares.get(&publickey_bytes);
1447
1448            if position.is_none()
1449                || response.is_none()
1450                || encrypted_share.is_none()
1451            {
1452                return false;
1453            }
1454
1455            let position = *position.unwrap();
1456            let response = response.unwrap();
1457            let encrypted_share = encrypted_share.unwrap();
1458
1459            // Calculate X_i = Σ_j (position^j) * C_j using EC operations
1460            let mut x_val = self.group.identity();
1461            let mut exponent = Scalar::ONE;
1462            for j in 0..distribute_sharesbox.commitments.len() {
1463                // C_j^(position^j) in EC notation = (position^j) * C_j
1464                let c_j_pow = self
1465                    .group
1466                    .exp(&distribute_sharesbox.commitments[j], &exponent);
1467                x_val = self.group.mul(&x_val, &c_j_pow);
1468                let pos_scalar = Scalar::from(position as u64);
1469                exponent = self.group.scalar_mul(&exponent, &pos_scalar);
1470            }
1471
1472            // Verify DLEQ proof for this participant
1473            // DLEQ(g, X_i, y_i, Y_i): proves log_g(X_i) = log_{y_i}(Y_i)
1474            // a_1 = r*g + c*X_i, a_2 = r*y_i + c*Y_i
1475            let g_r = self.group.exp(&subgroup_gen, response);
1476            let x_c = self.group.exp(&x_val, &distribute_sharesbox.challenge);
1477            let a1 = self.group.mul(&g_r, &x_c);
1478
1479            let y_r = self.group.exp(publickey, response);
1480            let y_c = self
1481                .group
1482                .exp(encrypted_share, &distribute_sharesbox.challenge);
1483            let a2 = self.group.mul(&y_r, &y_c);
1484
1485            // Update hash with X_i, Y_i, a_1, a_2 using element_to_bytes()
1486            challenge_hasher.update(self.group.element_to_bytes(&x_val));
1487            challenge_hasher
1488                .update(self.group.element_to_bytes(encrypted_share));
1489            challenge_hasher.update(self.group.element_to_bytes(&a1));
1490            challenge_hasher.update(self.group.element_to_bytes(&a2));
1491        }
1492
1493        // Calculate final challenge and check if it matches
1494        let challenge_hash = challenge_hasher.finalize();
1495        let computed_challenge = self.group.hash_to_scalar(&challenge_hash);
1496
1497        computed_challenge == distribute_sharesbox.challenge
1498    }
1499
1500    /// Reconstruct secret from shares (Secp256k1Group implementation).
1501    ///
1502    /// # Parameters
1503    /// - `share_boxes`: Array of share boxes from participants
1504    /// - `distribute_share_box`: The distribution shares box from the dealer
1505    ///
1506    /// # Returns
1507    /// `Some(secret)` if reconstruction succeeds, `None` otherwise
1508    pub fn reconstruct(
1509        &self,
1510        share_boxes: &[ShareBox<Secp256k1Group>],
1511        distribute_share_box: &DistributionSharesBox<Secp256k1Group>,
1512    ) -> Option<BigInt> {
1513        use rayon::prelude::*;
1514
1515        if share_boxes.len() < distribute_share_box.commitments.len() {
1516            return None;
1517        }
1518
1519        // Build position -> share map
1520        let mut shares: std::collections::HashMap<i64, AffinePoint> =
1521            std::collections::HashMap::new();
1522        for share_box in share_boxes.iter() {
1523            let publickey_bytes =
1524                self.group.element_to_bytes(&share_box.publickey);
1525            let position =
1526                distribute_share_box.positions.get(&publickey_bytes)?;
1527            shares.insert(*position, share_box.share);
1528        }
1529
1530        // Compute Lagrange factors and G^s = Σ S_i^λ_i
1531        let secret = self.group.identity();
1532        let values: Vec<i64> = shares.keys().copied().collect();
1533        let shares_vec: Vec<(i64, AffinePoint)> = shares.into_iter().collect();
1534        let shares_slice = shares_vec.as_slice();
1535
1536        let factors: Vec<AffinePoint> = shares_slice
1537            .par_iter()
1538            .map(|(position, share)| {
1539                self.compute_lagrange_factor_secp256k1(
1540                    *position, share, &values,
1541                )
1542            })
1543            .collect();
1544
1545        // Add all factors using group.mul() (EC point addition)
1546        let final_secret = factors
1547            .into_iter()
1548            .fold(secret, |acc, factor| self.group.mul(&acc, &factor));
1549
1550        // Reconstruct secret = H(G^s) XOR U
1551        let secret_hash =
1552            Sha256::digest(self.group.element_to_bytes(&final_secret));
1553        // Convert hash to Scalar using from_repr (modular reduction)
1554        let mut field_bytes = FieldBytes::<k256::Secp256k1>::default();
1555        let hash_len = secret_hash.len().min(field_bytes.len());
1556        field_bytes[32 - hash_len..].copy_from_slice(&secret_hash[..hash_len]);
1557        let hash_scalar = Scalar::from_repr(field_bytes).unwrap();
1558        let hash_bytes = hash_scalar.to_bytes();
1559        let hash_biguint = BigUint::from_bytes_be(&hash_bytes);
1560        // For EC, we use the curve order as the modulus for U encoding
1561
1562        let scalar_bytes = self.group.order_as_bigint().to_bytes_be().1;
1563        let curve_order_bigint = BigUint::from_bytes_be(&scalar_bytes);
1564        let hash_reduced = hash_biguint % curve_order_bigint;
1565        let decrypted_secret =
1566            hash_reduced ^ distribute_share_box.U.to_biguint().unwrap();
1567
1568        Some(decrypted_secret.to_bigint().unwrap())
1569    }
1570
1571    /// Compute Lagrange factor for secret reconstruction (Secp256k1Group implementation).
1572    ///
1573    /// This uses pure Scalar arithmetic to avoid BigInt/Scalar conversion issues.
1574    fn compute_lagrange_factor_secp256k1(
1575        &self,
1576        position: i64,
1577        share: &AffinePoint,
1578        values: &[i64],
1579    ) -> AffinePoint {
1580        // λ_i = ∏_{j≠i} j / (j - i)
1581        let mut lambda_num = Scalar::ONE;
1582        let mut lambda_den = Scalar::ONE;
1583        let mut sign = 1i64;
1584
1585        for &j in values {
1586            if j == position {
1587                continue;
1588            }
1589            lambda_num *= Scalar::from(j as u64);
1590            let diff = j - position;
1591            if diff < 0 {
1592                sign *= -1;
1593                lambda_den *= Scalar::from((-diff) as u64);
1594            } else {
1595                lambda_den *= Scalar::from(diff as u64);
1596            }
1597        }
1598
1599        // λ = numerator * denominator^(-1)
1600        let lambda = lambda_num * lambda_den.invert().unwrap();
1601
1602        // Compute share^λ = λ * share (scalar multiplication)
1603        let mut factor = self.group.exp(share, &lambda);
1604
1605        // Handle negative coefficients via point negation
1606        if sign < 0
1607            && let Some(negated) = self.group.element_inverse(&factor)
1608        {
1609            factor = negated;
1610        }
1611
1612        factor
1613    }
1614}
1615
1616// ============================================================================
1617// Ristretto255Group-Specific Implementation
1618// ============================================================================
1619
1620impl Participant<Ristretto255Group> {
1621    /// Distribute a secret among participants (full implementation for Ristretto255Group).
1622    ///
1623    /// # Parameters
1624    /// - `secret`: The value to be shared (as BigInt for cross-group compatibility)
1625    /// - `publickeys`: Array of public keys (Ristretto points) of each participant
1626    /// - `threshold`: Number of shares needed for reconstruction
1627    ///
1628    /// Returns a `DistributionSharesBox` containing encrypted shares and DLEQ proofs
1629    pub fn distribute_secret(
1630        &mut self,
1631        secret: &BigInt,
1632        publickeys: &[RistrettoPoint],
1633        threshold: u32,
1634    ) -> DistributionSharesBox<Ristretto255Group> {
1635        assert!(threshold <= publickeys.len() as u32);
1636
1637        // Group generators
1638        let subgroup_gen = self.group.subgroup_generator();
1639        let main_gen = self.group.generator();
1640        let _group_order = self.group.order(); // Stored for API compatibility
1641
1642        // Generate random polynomial (coefficients are BigInt, converted to Scalar later)
1643        let mut polynomial = Polynomial::new();
1644        let group_order_bigint = self.group.order_as_bigint().clone();
1645        polynomial.init((threshold - 1) as i32, &group_order_bigint);
1646
1647        // Generate random witness w (scalar)
1648        let w = self.group.generate_private_key();
1649
1650        // Data structures - use Vec<u8> keys (serialized points) since RistrettoPoint doesn't implement Hash
1651        let mut commitments: Vec<RistrettoPoint> = Vec::new();
1652        let mut positions: HashMap<Vec<u8>, i64> = HashMap::new();
1653        let mut shares: HashMap<Vec<u8>, RistrettoPoint> = HashMap::new();
1654        let mut challenge_hasher = Sha256::new();
1655
1656        let mut sampling_points: HashMap<Vec<u8>, RistrettoScalar> =
1657            HashMap::new();
1658        let mut dleq_w: HashMap<Vec<u8>, RistrettoScalar> = HashMap::new();
1659        let mut position: i64 = 1;
1660
1661        // Calculate commitments C_j = a_j * g (scalar multiplication)
1662        for j in 0..threshold {
1663            let coeff_bigint = &polynomial.coefficients[j as usize];
1664            // Convert BigInt coefficient to Ristretto Scalar
1665            // CRITICAL: curve25519-dalek uses little-endian, num_bigint uses big-endian
1666            let coeff = Ristretto255Group::bigint_to_scalar(coeff_bigint);
1667            let commitment = self.group.exp(&subgroup_gen, &coeff);
1668            commitments.push(commitment);
1669        }
1670
1671        // Calculate encrypted shares for each participant
1672        for pubkey in publickeys {
1673            let pubkey_bytes = self.group.element_to_bytes(pubkey);
1674            positions.insert(pubkey_bytes.clone(), position);
1675
1676            // P(position) as Scalar
1677            let pos_scalar = BigInt::from(position);
1678            let secret_share_bigint = polynomial.get_value(&pos_scalar);
1679            // CRITICAL: Must take mod order BEFORE converting to Scalar
1680            let secret_share_mod = &secret_share_bigint % &group_order_bigint;
1681            // Convert to Ristretto Scalar (handles endianness)
1682            let secret_share =
1683                Ristretto255Group::bigint_to_scalar(&secret_share_mod);
1684            sampling_points.insert(pubkey_bytes.clone(), secret_share);
1685            dleq_w.insert(pubkey_bytes.clone(), w);
1686
1687            // Calculate X_i = Σ_j (position^j) * C_j (using EC operations)
1688            let mut x_val = self.group.identity();
1689            let mut exponent = RistrettoScalar::ONE;
1690
1691            for j in 0..threshold {
1692                // C_j^(i^j) in EC notation = (i^j) * C_j (scalar multiplication)
1693                let c_j_pow =
1694                    self.group.exp(&commitments[j as usize], &exponent);
1695                x_val = self.group.mul(&x_val, &c_j_pow);
1696
1697                // exponent *= position (mod order)
1698                let pos_scalar = RistrettoScalar::from(position as u64);
1699                exponent = self.group.scalar_mul(&exponent, &pos_scalar);
1700            }
1701
1702            // Calculate Y_i = secret_share * y_i (encrypted share)
1703            let encrypted_secret_share = self.group.exp(pubkey, &secret_share);
1704            shares.insert(pubkey_bytes.clone(), encrypted_secret_share);
1705
1706            // Generate DLEQ proof: DLEQ(g, X_i, y_i, Y_i)
1707            let mut dleq = DLEQ::new(self.group.clone());
1708            dleq.init(
1709                subgroup_gen,
1710                x_val,
1711                *pubkey,
1712                encrypted_secret_share,
1713                secret_share,
1714                w,
1715            );
1716
1717            // Update challenge hash - use element_to_bytes() for EC points
1718            let a1 = dleq.get_a1();
1719            let a2 = dleq.get_a2();
1720
1721            challenge_hasher.update(self.group.element_to_bytes(&x_val));
1722            challenge_hasher
1723                .update(self.group.element_to_bytes(&encrypted_secret_share));
1724            challenge_hasher.update(self.group.element_to_bytes(&a1));
1725            challenge_hasher.update(self.group.element_to_bytes(&a2));
1726
1727            position += 1;
1728        }
1729
1730        // Compute common challenge
1731        let challenge_hash = challenge_hasher.finalize();
1732        let challenge = self.group.hash_to_scalar(&challenge_hash);
1733
1734        // Compute responses: r_i = w - alpha_i * c
1735        let mut responses: HashMap<Vec<u8>, RistrettoScalar> = HashMap::new();
1736        for pubkey in publickeys {
1737            let pubkey_bytes = self.group.element_to_bytes(pubkey);
1738            let alpha = sampling_points.get(&pubkey_bytes).unwrap();
1739            let alpha_c = self.group.scalar_mul(alpha, &challenge);
1740            let w_i = dleq_w.get(&pubkey_bytes).unwrap();
1741            let response = self.group.scalar_sub(w_i, &alpha_c);
1742
1743            responses.insert(pubkey_bytes, response);
1744        }
1745
1746        // Compute U = secret XOR H(G^s)
1747        let s_bigint = polynomial.get_value(&BigInt::zero());
1748        let s = Ristretto255Group::bigint_to_scalar(&s_bigint);
1749        let g_s = self.group.exp(&main_gen, &s);
1750
1751        // Hash the EC point to bytes
1752        let sha256_hash = Sha256::digest(self.group.element_to_bytes(&g_s));
1753        // Convert hash to BigUint and reduce modulo group order
1754        let hash_biguint = BigUint::from_bytes_be(&sha256_hash[..]);
1755        let curve_order_bigint = BigUint::from_bytes_be(
1756            &self.group.order_as_bigint().to_bytes_be().1,
1757        );
1758        let hash_reduced = hash_biguint % curve_order_bigint;
1759        let u = secret.to_biguint().unwrap() ^ hash_reduced;
1760
1761        // Build shares box
1762        let mut shares_box = DistributionSharesBox::new();
1763        shares_box.init(
1764            &commitments,
1765            positions,
1766            shares,
1767            publickeys,
1768            &challenge,
1769            responses,
1770            &u.to_bigint().unwrap(),
1771        );
1772        shares_box
1773    }
1774
1775    /// Extract a secret share from the distribution box (Ristretto255Group implementation).
1776    ///
1777    /// # Parameters
1778    /// - `shares_box`: The distribution shares box from the dealer
1779    /// - `private_key`: The participant's private key (Scalar)
1780    /// - `w`: Random witness for DLEQ proof (Scalar)
1781    pub fn extract_secret_share(
1782        &self,
1783        shares_box: &DistributionSharesBox<Ristretto255Group>,
1784        private_key: &RistrettoScalar,
1785        w: &RistrettoScalar,
1786    ) -> Option<ShareBox<Ristretto255Group>> {
1787        let main_gen = self.group.generator();
1788
1789        // Generate public key from private key using group method
1790        let public_key = self.group.generate_public_key(private_key);
1791
1792        // Get encrypted share from distribution box (serialize key for HashMap lookup)
1793        let public_key_bytes = self.group.element_to_bytes(&public_key);
1794        let encrypted_secret_share =
1795            shares_box.shares.get(&public_key_bytes)?;
1796
1797        // Decryption: S_i = Y_i^(1/x_i) using scalar_inverse
1798        let privkey_inverse = self.group.scalar_inverse(private_key)?;
1799        let decrypted_share =
1800            self.group.exp(encrypted_secret_share, &privkey_inverse);
1801
1802        // Generate DLEQ proof: DLEQ(G, publickey, decrypted_share, encrypted_secret_share)
1803        let mut dleq = DLEQ::new(self.group.clone());
1804        dleq.init(
1805            main_gen,
1806            public_key,
1807            decrypted_share,
1808            *encrypted_secret_share,
1809            *private_key,
1810            *w,
1811        );
1812
1813        // Compute challenge using element_to_bytes() for EC points
1814        let mut challenge_hasher = Sha256::new();
1815        challenge_hasher.update(self.group.element_to_bytes(&public_key));
1816        challenge_hasher
1817            .update(self.group.element_to_bytes(encrypted_secret_share));
1818
1819        let a1 = dleq.get_a1();
1820        let a2 = dleq.get_a2();
1821        challenge_hasher.update(self.group.element_to_bytes(&a1));
1822        challenge_hasher.update(self.group.element_to_bytes(&a2));
1823
1824        let challenge_hash = challenge_hasher.finalize();
1825        let challenge = self.group.hash_to_scalar(&challenge_hash);
1826        dleq.c = Some(challenge);
1827
1828        // Compute response using scalar arithmetic
1829        let response = dleq.get_r()?;
1830
1831        // Build share box
1832        let mut share_box = ShareBox::new();
1833        share_box.init(public_key, decrypted_share, challenge, response);
1834        Some(share_box)
1835    }
1836
1837    /// Verify a decrypted share (Ristretto255Group implementation).
1838    ///
1839    /// # Parameters
1840    /// - `sharebox`: The share box containing the decrypted share
1841    /// - `distribution_sharebox`: The distribution shares box from the dealer
1842    /// - `publickey`: The public key (Ristretto point) of the participant who created the share
1843    pub fn verify_share(
1844        &self,
1845        sharebox: &ShareBox<Ristretto255Group>,
1846        distribution_sharebox: &DistributionSharesBox<Ristretto255Group>,
1847        publickey: &RistrettoPoint,
1848    ) -> bool {
1849        let main_gen = self.group.generator();
1850
1851        // Get encrypted share from distribution box (serialize key for HashMap lookup)
1852        let publickey_bytes = self.group.element_to_bytes(publickey);
1853        let encrypted_share =
1854            match distribution_sharebox.shares.get(&publickey_bytes) {
1855                Some(s) => s,
1856                None => return false,
1857            };
1858
1859        // Verify DLEQ proof using EC operations
1860        // a_1 = r*G + c*publickey (point addition)
1861        let g1_r = self.group.exp(&main_gen, &sharebox.response);
1862        let h1_c = self.group.exp(publickey, &sharebox.challenge);
1863        let a1_verify = self.group.mul(&g1_r, &h1_c);
1864
1865        // a_2 = r*decrypted_share + c*encrypted_share
1866        let g2_r = self.group.exp(&sharebox.share, &sharebox.response);
1867        let h2_c = self.group.exp(encrypted_share, &sharebox.challenge);
1868        let a2_verify = self.group.mul(&g2_r, &h2_c);
1869
1870        // Compute challenge hash using element_to_bytes() for EC points
1871        let mut challenge_hasher = Sha256::new();
1872        challenge_hasher.update(self.group.element_to_bytes(publickey));
1873        challenge_hasher.update(self.group.element_to_bytes(encrypted_share));
1874        challenge_hasher.update(self.group.element_to_bytes(&a1_verify));
1875        challenge_hasher.update(self.group.element_to_bytes(&a2_verify));
1876
1877        let challenge_hash = challenge_hasher.finalize();
1878        let challenge_computed = self.group.hash_to_scalar(&challenge_hash);
1879
1880        challenge_computed == sharebox.challenge
1881    }
1882
1883    /// Verify distribution shares box (Ristretto255Group implementation).
1884    ///
1885    /// Verifies that all encrypted shares are consistent with the commitments.
1886    /// This is the public verifiability part of PVSS - anyone can verify the dealer
1887    /// didn't cheat.
1888    ///
1889    /// # Parameters
1890    /// - `distribute_sharesbox`: The distribution shares box to verify
1891    ///
1892    /// # Returns
1893    /// `true` if the distribution is valid, `false` otherwise
1894    pub fn verify_distribution_shares(
1895        &self,
1896        distribute_sharesbox: &DistributionSharesBox<Ristretto255Group>,
1897    ) -> bool {
1898        let subgroup_gen = self.group.subgroup_generator();
1899        let mut challenge_hasher = Sha256::new();
1900
1901        // Verify each participant's encrypted share and accumulate hash
1902        for publickey in &distribute_sharesbox.publickeys {
1903            let publickey_bytes = self.group.element_to_bytes(publickey);
1904            let position = distribute_sharesbox.positions.get(&publickey_bytes);
1905            let response = distribute_sharesbox.responses.get(&publickey_bytes);
1906            let encrypted_share =
1907                distribute_sharesbox.shares.get(&publickey_bytes);
1908
1909            if position.is_none()
1910                || response.is_none()
1911                || encrypted_share.is_none()
1912            {
1913                return false;
1914            }
1915
1916            let position = *position.unwrap();
1917            let response = response.unwrap();
1918            let encrypted_share = encrypted_share.unwrap();
1919
1920            // Calculate X_i = Σ_j (position^j) * C_j using EC operations
1921            let mut x_val = self.group.identity();
1922            let mut exponent = RistrettoScalar::ONE;
1923            for j in 0..distribute_sharesbox.commitments.len() {
1924                // C_j^(position^j) in EC notation = (position^j) * C_j
1925                let c_j_pow = self
1926                    .group
1927                    .exp(&distribute_sharesbox.commitments[j], &exponent);
1928                x_val = self.group.mul(&x_val, &c_j_pow);
1929                let pos_scalar = RistrettoScalar::from(position as u64);
1930                exponent = self.group.scalar_mul(&exponent, &pos_scalar);
1931            }
1932
1933            // Verify DLEQ proof for this participant
1934            // DLEQ(g, X_i, y_i, Y_i): proves log_g(X_i) = log_{y_i}(Y_i)
1935            // a_1 = r*g + c*X_i, a_2 = r*y_i + c*Y_i
1936            let g_r = self.group.exp(&subgroup_gen, response);
1937            let x_c = self.group.exp(&x_val, &distribute_sharesbox.challenge);
1938            let a1 = self.group.mul(&g_r, &x_c);
1939
1940            let y_r = self.group.exp(publickey, response);
1941            let y_c = self
1942                .group
1943                .exp(encrypted_share, &distribute_sharesbox.challenge);
1944            let a2 = self.group.mul(&y_r, &y_c);
1945
1946            // Update hash with X_i, Y_i, a_1, a_2 using element_to_bytes()
1947            challenge_hasher.update(self.group.element_to_bytes(&x_val));
1948            challenge_hasher
1949                .update(self.group.element_to_bytes(encrypted_share));
1950            challenge_hasher.update(self.group.element_to_bytes(&a1));
1951            challenge_hasher.update(self.group.element_to_bytes(&a2));
1952        }
1953
1954        // Calculate final challenge and check if it matches
1955        let challenge_hash = challenge_hasher.finalize();
1956        let computed_challenge = self.group.hash_to_scalar(&challenge_hash);
1957
1958        computed_challenge == distribute_sharesbox.challenge
1959    }
1960
1961    /// Reconstruct secret from shares (Ristretto255Group implementation).
1962    ///
1963    /// # Parameters
1964    /// - `share_boxes`: Array of share boxes from participants
1965    /// - `distribute_share_box`: The distribution shares box from the dealer
1966    ///
1967    /// # Returns
1968    /// `Some(secret)` if reconstruction succeeds, `None` otherwise
1969    pub fn reconstruct(
1970        &self,
1971        share_boxes: &[ShareBox<Ristretto255Group>],
1972        distribute_share_box: &DistributionSharesBox<Ristretto255Group>,
1973    ) -> Option<BigInt> {
1974        use rayon::prelude::*;
1975
1976        if share_boxes.len() < distribute_share_box.commitments.len() {
1977            return None;
1978        }
1979
1980        // Build position -> share map
1981        let mut shares: HashMap<i64, RistrettoPoint> = HashMap::new();
1982        for share_box in share_boxes.iter() {
1983            let publickey_bytes =
1984                self.group.element_to_bytes(&share_box.publickey);
1985            let position =
1986                distribute_share_box.positions.get(&publickey_bytes)?;
1987            shares.insert(*position, share_box.share);
1988        }
1989
1990        // Compute Lagrange factors and G^s = Σ S_i^λ_i
1991        let secret = self.group.identity();
1992        let values: Vec<i64> = shares.keys().copied().collect();
1993        let shares_vec: Vec<(i64, RistrettoPoint)> =
1994            shares.into_iter().collect();
1995        let shares_slice = shares_vec.as_slice();
1996
1997        let factors: Vec<RistrettoPoint> = shares_slice
1998            .par_iter()
1999            .map(|(position, share)| {
2000                self.compute_lagrange_factor_ristretto(
2001                    *position, share, &values,
2002                )
2003            })
2004            .collect();
2005
2006        // Add all factors using group.mul() (EC point addition)
2007        let final_secret = factors
2008            .into_iter()
2009            .fold(secret, |acc, factor| self.group.mul(&acc, &factor));
2010
2011        // Reconstruct secret = H(G^s) XOR U
2012        let secret_hash =
2013            Sha256::digest(self.group.element_to_bytes(&final_secret));
2014        // Convert hash to BigUint and reduce modulo group order
2015        let hash_biguint = BigUint::from_bytes_be(&secret_hash[..]);
2016        let curve_order_bigint = BigUint::from_bytes_be(
2017            &self.group.order_as_bigint().to_bytes_be().1,
2018        );
2019        let hash_reduced = hash_biguint % curve_order_bigint;
2020        let decrypted_secret =
2021            hash_reduced ^ distribute_share_box.U.to_biguint().unwrap();
2022
2023        Some(decrypted_secret.to_bigint().unwrap())
2024    }
2025
2026    /// Compute Lagrange factor for secret reconstruction (Ristretto255Group implementation).
2027    ///
2028    /// This uses pure Scalar arithmetic to avoid BigInt/Scalar conversion issues.
2029    fn compute_lagrange_factor_ristretto(
2030        &self,
2031        position: i64,
2032        share: &RistrettoPoint,
2033        values: &[i64],
2034    ) -> RistrettoPoint {
2035        // λ_i = ∏_{j≠i} j / (j - i)
2036        let mut lambda_num = RistrettoScalar::ONE;
2037        let mut lambda_den = RistrettoScalar::ONE;
2038        let mut sign = 1i64;
2039
2040        for &j in values {
2041            if j == position {
2042                continue;
2043            }
2044            lambda_num *= RistrettoScalar::from(j as u64);
2045            let diff = j - position;
2046            if diff < 0 {
2047                sign *= -1;
2048                lambda_den *= RistrettoScalar::from((-diff) as u64);
2049            } else {
2050                lambda_den *= RistrettoScalar::from(diff as u64);
2051            }
2052        }
2053
2054        // λ = numerator * denominator^(-1)
2055        // Note: curve25519-dalek Scalar::invert() returns CtOption, convert to Option
2056        let lambda_den_inv = Option::from(lambda_den.invert());
2057        let lambda = match lambda_den_inv {
2058            Some(inv) => lambda_num * inv,
2059            None => {
2060                // This should never happen with valid Lagrange coefficients
2061                RistrettoScalar::ZERO
2062            }
2063        };
2064
2065        // Compute share^λ = λ * share (scalar multiplication)
2066        let mut factor = self.group.exp(share, &lambda);
2067
2068        // Handle negative coefficients via point negation
2069        if sign < 0
2070            && let Some(negated) = self.group.element_inverse(&factor)
2071        {
2072            factor = negated;
2073        }
2074
2075        factor
2076    }
2077}