Skip to main content

primitives/sharing/authenticated/pairwise/
share.rs

1use std::{
2    iter::Sum,
3    marker::PhantomData,
4    mem::MaybeUninit,
5    ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
6};
7
8use itertools::{enumerate, izip, Itertools};
9use rayon::prelude::IntoParallelIterator;
10use serde::{de::DeserializeOwned, Deserialize, Serialize};
11use subtle::{Choice, ConstantTimeEq};
12use typenum::{PartialDiv, Prod, U1, U2, U3, U5};
13use wincode::{
14    io::{Reader, Writer},
15    ReadResult,
16    SchemaRead,
17    SchemaWrite,
18    WriteResult,
19};
20
21use crate::{
22    algebra::{
23        elliptic_curve::{BaseField, Curve, Point, ScalarAsExtension, ScalarField},
24        field::{binary::Gf2_128, mersenne::Mersenne107, FieldElement, SubfieldElement},
25        ops::transpose::transpose,
26    },
27    errors::PrimitiveError,
28    izip_eq,
29    random::{CryptoRngCore, Random, RandomWith},
30    sharing::{
31        authenticated::{GlobalKey, PairwiseAuthKey, PairwiseAuthOpenShare},
32        unauthenticated::AdditiveShares,
33        PlaintextOps,
34        Reconstructible,
35        VerifiableWith,
36    },
37    types::{
38        heap_array::{CurvePoints, FieldElements, SubfieldElements},
39        CollectAll,
40        ConditionallySelectable,
41        HeapArray,
42        PeerIndex,
43        Positive,
44        TryFoldAll,
45    },
46    utils::IntoExactSizeIterator,
47};
48
49/// Generic base for pairwise authenticated shares, analogous to [`PairwiseAuthKey`].
50///
51/// The three type parameters are:
52/// - `V`: value type.
53/// - `B`: MAC and local key (Beta) type, an additive Group.
54/// - `A`: global key (Alpha) type.
55///
56/// The authenticated shares fulfill this relation:
57///
58/// ```MAC(x_i)_j = α_ji · x_i + β_ji  ∀i∈[1..n]  ∀j∈[1..n]∖{i}```
59/// 
60/// where `β_ji` and `α_ji` are the (local and global) keys of value `x_i` held by `P_j`.
61///
62/// As such, the share of party `P_i` contains:
63/// - `x_i`, the unauthenticated share value (s.t. `x = Σ x_i`)
64/// - `{MAC(x_i)_j} ∀j∈[1..n]∖{i}`, the MACs of `x_i` for each of the other n-1 parties.
65/// - `{β_ij} ∀j∈[1..n]∖{i}` and `{α_ij} ∀j∈[1..n]∖{i}`, the (local and global) keys tied to the values&MACs of the other n-1 parties.
66#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(
68    bound = "V: Serialize + DeserializeOwned, A: Serialize + DeserializeOwned, B: Serialize + DeserializeOwned"
69)]
70#[repr(C)]
71pub struct PairwiseAuthShare<V, A, B> {
72    pub(crate) value: V,
73    pub(crate) macs: Box<[B]>,
74    pub(crate) keys: Box<[PairwiseAuthKey<A, B>]>,
75}
76
77impl<V, A, B> SchemaWrite for PairwiseAuthShare<V, A, B>
78where
79    V: SchemaWrite<Src = V>,
80    B: SchemaWrite<Src = B>,
81    PairwiseAuthKey<A, B>: SchemaWrite<Src = PairwiseAuthKey<A, B>>,
82{
83    type Src = Self;
84
85    fn size_of(src: &Self) -> WriteResult<usize> {
86        Ok(V::size_of(&src.value)?
87            + <Box<[B]> as SchemaWrite>::size_of(&src.macs)?
88            + <Box<[PairwiseAuthKey<A, B>]> as SchemaWrite>::size_of(&src.keys)?)
89    }
90
91    fn write(writer: &mut impl Writer, src: &Self) -> WriteResult<()> {
92        V::write(writer, &src.value)?;
93        <Box<[B]> as SchemaWrite>::write(writer, &src.macs)?;
94        <Box<[PairwiseAuthKey<A, B>]> as SchemaWrite>::write(writer, &src.keys)
95    }
96}
97
98impl<'de, V, A, B> SchemaRead<'de> for PairwiseAuthShare<V, A, B>
99where
100    V: SchemaRead<'de, Dst = V>,
101    B: SchemaRead<'de, Dst = B>,
102    PairwiseAuthKey<A, B>: SchemaRead<'de, Dst = PairwiseAuthKey<A, B>>,
103{
104    type Dst = Self;
105
106    fn read(reader: &mut impl Reader<'de>, dst: &mut MaybeUninit<Self>) -> ReadResult<()> {
107        let mut value = MaybeUninit::<V>::uninit();
108        let mut macs = MaybeUninit::<Box<[B]>>::uninit();
109        let mut keys = MaybeUninit::<Box<[PairwiseAuthKey<A, B>]>>::uninit();
110        V::read(reader, &mut value)?;
111        <Box<[B]> as SchemaRead>::read(reader, &mut macs)?;
112        <Box<[PairwiseAuthKey<A, B>]> as SchemaRead>::read(reader, &mut keys)?;
113        // SAFETY: all fields were initialised by the reads above
114        let value = unsafe { value.assume_init() };
115        let macs = unsafe { macs.assume_init() };
116        let keys = unsafe { keys.assume_init() };
117        dst.write(PairwiseAuthShare { value, macs, keys });
118        Ok(())
119    }
120}
121
122impl<V, A, B> PairwiseAuthShare<V, A, B> {
123    /// Constructs a new share, returning an error if the MAC and key slices are
124    /// inconsistent (empty or length-mismatched).
125    pub fn try_new(
126        value: V,
127        macs: Box<[B]>,
128        keys: Box<[PairwiseAuthKey<A, B>]>,
129    ) -> Result<Self, PrimitiveError> {
130        if macs.is_empty() {
131            return Err(PrimitiveError::MinimumLength(2, 0));
132        }
133        if macs.len() != keys.len() {
134            return Err(PrimitiveError::InvalidSize(keys.len(), macs.len()));
135        }
136        Ok(Self { value, macs, keys })
137    }
138
139    /// Returns a reference to the share value.
140    #[inline]
141    pub fn get_value(&self) -> &V {
142        &self.value
143    }
144
145    /// Returns a mutable reference to the share value.
146    #[inline]
147    pub fn get_value_mut(&mut self) -> &mut V {
148        &mut self.value
149    }
150
151    /// Returns a slice of all MACs (one per distant peer).
152    #[inline]
153    pub fn get_macs(&self) -> &[B] {
154        &self.macs
155    }
156
157    /// Returns the MAC for the given peer index, or `None` if out of bounds.
158    #[inline]
159    pub fn get_mac(&self, peer_index: PeerIndex) -> Option<&B> {
160        self.macs.get(peer_index)
161    }
162
163    /// Returns a slice of all keys (one per distant peer).
164    #[inline]
165    pub fn get_keys(&self) -> &[PairwiseAuthKey<A, B>] {
166        &self.keys
167    }
168
169    /// Returns a mutable slice of all keys.
170    #[inline]
171    pub fn get_keys_mut(&mut self) -> &mut [PairwiseAuthKey<A, B>] {
172        &mut self.keys
173    }
174
175    /// Returns the key for the given peer index, or `None` if out of bounds.
176    #[inline]
177    pub fn get_key(&self, peer_index: PeerIndex) -> Option<&PairwiseAuthKey<A, B>> {
178        self.keys.get(peer_index)
179    }
180
181    /// Consumes the share, returning the value.
182    #[inline]
183    pub fn into_value(self) -> V {
184        self.value
185    }
186
187    #[allow(clippy::type_complexity)]
188    /// Consumes the share, returning all the internal data (value, MACs, and keys).
189    #[inline]
190    pub fn into_inner(self) -> (V, Box<[B]>, Box<[PairwiseAuthKey<A, B>]>) {
191        (self.value, self.macs, self.keys)
192    }
193
194    /// Total number of parties (including the local party).
195    #[inline]
196    pub fn n_parties(&self) -> usize {
197        self.macs.len() + 1
198    }
199
200    /// Number of distant parties (excludes the local party).
201    #[inline]
202    pub fn n_distant_parties(&self) -> usize {
203        self.macs.len()
204    }
205
206    /// Returns an iterator over the global keys (alpha values) for each distant peer.
207    #[inline]
208    pub fn get_alphas(&self) -> impl ExactSizeIterator<Item = GlobalKey<A>> + '_ {
209        self.keys.iter().map(|key| key.alpha())
210    }
211
212    /// Returns an iterator over the local keys (beta values) for each distant peer.
213    #[inline]
214    pub fn get_betas(&self) -> impl ExactSizeIterator<Item = &B> + '_ {
215        self.keys.iter().map(|key| key.get_beta())
216    }
217}
218
219/// Alias for batched shares using `HeapArray`-based value/MAC/key types, with the same structure as
220/// [`PairwiseAuthShare`].
221pub type BatchedShare<V, A, B, M> = PairwiseAuthShare<HeapArray<V, M>, A, HeapArray<B, M>>;
222
223/// Pairwise authenticated share `<x_i>` of secret shared `<x>` over a field F.
224/// Alias for [`PairwiseAuthShare<SubfieldElement<F>, FieldElement<F>, FieldElement<F>>`].
225/// See also [`FieldShares<F, M>`] for the batched variant.
226pub type FieldShare<F> = PairwiseAuthShare<SubfieldElement<F>, FieldElement<F>, FieldElement<F>>;
227/// Pairwise authenticated share over F_{2^107-1}. See [`FieldShare<F>`].
228pub type Mersenne107Share = FieldShare<Mersenne107>;
229
230/// Alias for [`PairwiseAuthShare<FieldElement<F>, FieldElement<F>, FieldElement<F>>`], using values
231/// in F_p^r instead of F_p. See [`FieldShare<F>`] for the (more commonly used) subfield variant.
232pub type FieldExtShare<F> = PairwiseAuthShare<FieldElement<F>, FieldElement<F>, FieldElement<F>>;
233
234/// A share of a single point on a curve, with its associated MACs and the keys for all
235/// other parties' shares.
236///
237/// Alias for [`PairwiseAuthShare<Point<C>, ScalarAsExtension<C>, Point<C>>`].
238/// See also [`PointShares<C, M>`] for the batched variant.
239pub type PointShare<C> = PairwiseAuthShare<Point<C>, ScalarAsExtension<C>, Point<C>>;
240
241/// Authenticated batch share `<x_i>` of `M` secret-shared field elements held by `P_i`.
242///
243/// A vectorised counterpart of [`FieldShare<F>`]: same MAC/key structure, but all slots
244/// are stored as fixed-length [`HeapArray`]s of size `M`.
245///
246/// Alias for [`PairwiseAuthShare<SubfieldElements<F,M>, FieldElement<F>, FieldElements<F,M>>`].
247/// See also [`FieldShare<F>`] for the single-value variant.
248pub type FieldShares<F, M> =
249    PairwiseAuthShare<SubfieldElements<F, M>, FieldElement<F>, FieldElements<F, M>>;
250
251/// A share of a batch of `M` curve points, with its associated MACs and the keys for
252/// all other parties' shares.
253///
254/// A vectorised counterpart of [`PointShare<C>`]: same MAC/key structure, but all slots
255/// are stored as fixed-length [`HeapArray`]s of size `M`.
256///
257/// Alias for [`PairwiseAuthShare<CurvePoints<C,M>, ScalarAsExtension<C>, CurvePoints<C,M>>`].
258/// See also [`PointShare<C>`] for the single-value variant.
259pub type PointShares<C, M> =
260    PairwiseAuthShare<CurvePoints<C, M>, ScalarAsExtension<C>, CurvePoints<C, M>>;
261
262// ---- Scalar / BaseField / Bit convenience aliases ----
263
264/// Authenticated share of a scalar field element. Alias for [`FieldShare<ScalarField<C>>`].
265pub type ScalarShare<C> = FieldShare<ScalarField<C>>;
266/// Authenticated share of a base field element. Alias for [`FieldShare<BaseField<C>>`].
267pub type BaseFieldShare<C> = FieldShare<BaseField<C>>;
268/// Authenticated share of a single GF(2^128) bit. Alias for [`FieldShare<Gf2_128>`].
269pub type BitShare = FieldShare<Gf2_128>;
270
271/// Authenticated batch share of scalar field elements. Alias for [`FieldShares<ScalarField<C>,
272/// M>`].
273pub type ScalarShares<C, M> = FieldShares<ScalarField<C>, M>;
274/// Authenticated batch share of base field elements. Alias for [`FieldShares<BaseField<C>, M>`].
275pub type BaseFieldShares<C, M> = FieldShares<BaseField<C>, M>;
276
277// --------------------
278// |   Verification   |
279// --------------------
280
281impl<V, A, B> PairwiseAuthShare<V, A, B> {
282    /// Compute the MAC of a value `x` for ( `α`-global,  `β`-local) keys as:
283    /// `MAC(x) = α * x + β`
284    pub fn compute_mac(value: V, key: &PairwiseAuthKey<A, B>) -> B
285    where
286        A: Clone,
287        B: for<'b> Add<&'b B, Output = B>,
288        for<'a> V: Mul<&'a A, Output = B>,
289    {
290        value * key.get_alpha() + key.get_beta()
291    }
292
293    /// Verify the MAC of a value `x` for ( `α`-global,  `β`-local) keys fulfills:
294    /// `MAC(x) == α * x + β`
295    pub fn verify_mac(key: &PairwiseAuthKey<A, B>, opening: PairwiseAuthOpenShare<V, B>) -> Choice
296    where
297        A: Clone,
298        B: ConstantTimeEq + SubAssign + for<'b> Add<&'b B, Output = B>,
299        for<'a> V: Mul<&'a A, Output = B>,
300    {
301        let PairwiseAuthOpenShare { value, mac } = opening;
302        let expected_mac = Self::compute_mac(value, key);
303        expected_mac.ct_eq(&mac)
304    }
305
306    pub(crate) fn compute_all_pairwise_macs(
307        all_unauth_shares: &[V],
308        all_keys: &[Box<[PairwiseAuthKey<A, B>]>],
309    ) -> Vec<Box<[B]>>
310    where
311        A: Clone,
312        V: Clone,
313        B: for<'b> Add<&'b B, Output = B>,
314        for<'a> V: Mul<&'a A, Output = B>,
315    {
316        let mut all_key_iters = all_keys.iter().map(|k| k.iter()).collect::<Vec<_>>();
317        enumerate(all_unauth_shares.iter())
318            .map(|(i, my_unauth_share)| {
319                enumerate(all_key_iters.iter_mut())
320                    .filter(|(j, _)| *j != i)
321                    .map(|(_, keys_iter)| {
322                        let next_key = keys_iter.next().unwrap();
323                        Self::compute_mac(my_unauth_share.clone(), next_key)
324                    })
325                    .collect()
326            })
327            .collect()
328    }
329}
330
331impl<V, A, B> VerifiableWith for PairwiseAuthShare<V, A, B>
332where
333    V: Clone
334        + PartialEq
335        + Send
336        + Sync
337        + 'static
338        + Serialize
339        + DeserializeOwned
340        + SchemaWrite<Src = V>
341        + for<'de> SchemaRead<'de, Dst = V>,
342    A: Clone,
343    B: Clone
344        + Send
345        + Sync
346        + 'static
347        + Serialize
348        + DeserializeOwned
349        + SchemaWrite<Src = B>
350        + for<'de> SchemaRead<'de, Dst = B>
351        + ConstantTimeEq
352        + SubAssign
353        + for<'b> Add<&'b B, Output = B>,
354    for<'a> V: Add<&'a V, Output = V>,
355    for<'a> V: Mul<&'a A, Output = B>,
356{
357    type VerificationData = ();
358
359    /// Check the MACs of the share received from another peer.
360    #[inline]
361    fn verify_from_peer_with(
362        &self,
363        open_share: PairwiseAuthOpenShare<V, B>,
364        peer: PeerIndex,
365        _verification_data: (),
366    ) -> Result<(), PrimitiveError> {
367        let key = self
368            .get_key(peer)
369            .ok_or(PrimitiveError::InvalidPeerIndex(peer, self.keys.len()))?;
370        if bool::from(PairwiseAuthShare::<V, A, B>::verify_mac(key, open_share)) {
371            Ok(())
372        } else {
373            Err(PrimitiveError::WrongMAC(format!("peer {peer}")).blame(peer))
374        }
375    }
376
377    /// Check the MACs of each share received from all other peers.
378    #[inline]
379    fn verify_with(
380        &self,
381        open_shares: Vec<PairwiseAuthOpenShare<V, B>>,
382        _verification_data: (),
383    ) -> Result<(), PrimitiveError> {
384        enumerate(izip_eq!(open_shares, &self.keys))
385            .map(|(from_peer, (open_share, key))| {
386                if bool::from(PairwiseAuthShare::<V, A, B>::verify_mac(key, open_share)) {
387                    Ok(())
388                } else {
389                    Err(PrimitiveError::WrongMAC(format!("peer {from_peer}")).blame(from_peer))
390                }
391            })
392            .collect_errors()?;
393        Ok(())
394    }
395}
396
397// --------------------------------
398// |   Opening & Reconstruction   |
399// --------------------------------
400
401impl<V, A, B> Reconstructible for PairwiseAuthShare<V, A, B>
402where
403    V: Clone
404        + PartialEq
405        + Send
406        + Sync
407        + 'static
408        + Serialize
409        + DeserializeOwned
410        + SchemaWrite<Src = V>
411        + for<'de> SchemaRead<'de, Dst = V>,
412    A: Clone,
413    B: Clone
414        + Send
415        + Sync
416        + 'static
417        + Serialize
418        + DeserializeOwned
419        + SchemaWrite<Src = B>
420        + for<'de> SchemaRead<'de, Dst = B>
421        + ConstantTimeEq
422        + SubAssign
423        + for<'b> Add<&'b B, Output = B>,
424    for<'a> V: Add<&'a V, Output = V>,
425    for<'a> V: Mul<&'a A, Output = B>,
426{
427    type Opening = PairwiseAuthOpenShare<V, B>;
428    type Value = V;
429
430    /// Open the share towards another peer.
431    fn open_to(&self, peer: PeerIndex) -> Result<PairwiseAuthOpenShare<V, B>, PrimitiveError> {
432        let mac = self
433            .get_mac(peer)
434            .ok_or(PrimitiveError::InvalidPeerIndex(peer, self.macs.len()))?
435            .to_owned();
436        Ok(PairwiseAuthOpenShare::new(self.get_value().to_owned(), mac))
437    }
438
439    /// Open the share towards all other peers.
440    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = PairwiseAuthOpenShare<V, B>> {
441        self.get_macs()
442            .iter()
443            .map(|mac| PairwiseAuthOpenShare::new(self.get_value().to_owned(), mac.to_owned()))
444    }
445
446    /// Reconstruct a secret from openings coming from all other parties.
447    fn reconstruct(&self, openings: Vec<PairwiseAuthOpenShare<V, B>>) -> Result<V, PrimitiveError> {
448        if openings.len() != self.get_keys().len() {
449            return Err(PrimitiveError::InvalidSize(
450                self.get_keys().len(),
451                openings.len(),
452            ));
453        }
454        let reconstruct = enumerate(izip_eq!(openings, &self.keys)).try_fold_all(
455            self.get_value().to_owned(),
456            |mut reconstructed, (from_peer, (open_share, key))| {
457                reconstructed = reconstructed + open_share.get_value();
458                let mac_error = match bool::from(PairwiseAuthShare::<V, A, B>::verify_mac(
459                    key,
460                    open_share.clone(),
461                )) {
462                    true => None,
463                    false => {
464                        Some(PrimitiveError::WrongMAC(format!("peer {from_peer}")).blame(from_peer))
465                    }
466                };
467                (reconstructed, mac_error)
468            },
469        )?;
470        Ok(reconstruct)
471    }
472}
473
474// -------------------------
475// |   Random Generation   |
476// -------------------------
477
478/// Number of parties
479type NParties = usize;
480
481impl<V, A, B> Random for PairwiseAuthShare<V, A, B>
482where
483    V: Random + Clone,
484    A: Random + Clone,
485    B: Random + Clone + ConstantTimeEq + SubAssign + for<'b> Add<&'b B, Output = B>,
486    for<'a> V: Mul<&'a A, Output = B>,
487{
488    fn random(_rng: impl CryptoRngCore) -> Self {
489        unimplemented!(
490            "Type {} does not support `random` since it needs to know `n_parties`. Use `random_with(..., n_parties)` instead.",
491            std::any::type_name::<Self>()
492        )
493    }
494
495    /// Generate one random authenticated share per peer, with consistent MACs and keys
496    /// across all peers.
497    fn random_n<Container: FromIterator<Self>>(
498        mut rng: impl CryptoRngCore,
499        n_parties: usize,
500    ) -> Container {
501        let all_unauth_shares: Vec<_> = V::random_n(&mut rng, n_parties);
502        let all_keys = (0..n_parties)
503            .map(|_| PairwiseAuthKey::<A, B>::random_n(&mut rng, n_parties - 1))
504            .collect::<Vec<_>>();
505        let all_macs = Self::compute_all_pairwise_macs(&all_unauth_shares, &all_keys);
506        izip_eq!(all_unauth_shares, all_macs, all_keys)
507            .map(|(value, macs, keys)| PairwiseAuthShare { value, macs, keys })
508            .collect()
509    }
510}
511
512impl<V, A, B> RandomWith<V> for PairwiseAuthShare<V, A, B>
513where
514    V: AdditiveShares,
515    A: Random + Clone,
516    B: Random + Clone + ConstantTimeEq + SubAssign + for<'b> Add<&'b B, Output = B>,
517    for<'a> V: Mul<&'a A, Output = B>,
518{
519    fn random_with(_rng: impl CryptoRngCore, _data: V) -> Self {
520        unimplemented!(
521            "Type {} does not support `random_with` since it needs to know `n_parties`. Use `random_n_with` instead.",
522            std::any::type_name::<Self>()
523        )
524    }
525
526    /// Secret share a value among n parties, generating an authenticated share for each
527    /// peer with consistent MACs and keys across all peers.
528    fn random_n_with<Container: FromIterator<Self>>(
529        mut rng: impl CryptoRngCore,
530        n_parties: usize,
531        value: V,
532    ) -> Container {
533        let all_unauth_shares = value.to_additive_shares(n_parties, &mut rng);
534        let all_keys = (0..n_parties)
535            .map(|_| PairwiseAuthKey::<A, B>::random_n(&mut rng, n_parties - 1))
536            .collect::<Vec<_>>();
537        let all_macs = Self::compute_all_pairwise_macs(&all_unauth_shares, &all_keys);
538        izip_eq!(all_unauth_shares, all_macs, all_keys)
539            .map(|(value, macs, keys)| PairwiseAuthShare { value, macs, keys })
540            .collect()
541    }
542
543    /// Generate an authenticated share for each peer given its additive share value,
544    /// with consistent MACs and keys across all peers.
545    fn random_n_with_each<Container: FromIterator<Self>>(
546        mut rng: impl CryptoRngCore,
547        unauth_shares: impl IntoExactSizeIterator<Item = V>,
548    ) -> Container {
549        let all_unauth_shares = unauth_shares.into_iter().collect::<Vec<_>>();
550        let n_parties = all_unauth_shares.len();
551        let all_keys = (0..n_parties)
552            .map(|_| PairwiseAuthKey::<A, B>::random_n(&mut rng, n_parties - 1))
553            .collect::<Vec<_>>();
554        let all_macs = Self::compute_all_pairwise_macs(&all_unauth_shares, &all_keys);
555        izip_eq!(all_unauth_shares, all_macs, all_keys)
556            .map(|(value, macs, keys)| PairwiseAuthShare { value, macs, keys })
557            .collect()
558    }
559}
560
561impl<V, A, B> RandomWith<NParties> for PairwiseAuthShare<V, A, B>
562where
563    V: Random,
564    A: Random + Clone,
565    B: Random,
566{
567    /// Generate a random authenticated share with random MACs and keys for all other parties.
568    fn random_with(mut rng: impl CryptoRngCore, n_parties: NParties) -> Self {
569        PairwiseAuthShare {
570            value: V::random(&mut rng),
571            macs: B::random_n(&mut rng, n_parties - 1),
572            keys: PairwiseAuthKey::<A, B>::random_n(&mut rng, n_parties - 1),
573        }
574    }
575}
576
577impl<V, A, B> RandomWith<(NParties, V)> for PairwiseAuthShare<V, A, B>
578where
579    V: Clone,
580    A: Random + Clone,
581    B: Random,
582{
583    /// Generate a random authenticated share with a given value and random MACs and keys.
584    fn random_with(mut rng: impl CryptoRngCore, (n_parties, value): (NParties, V)) -> Self {
585        PairwiseAuthShare {
586            value,
587            macs: B::random_n(&mut rng, n_parties - 1),
588            keys: PairwiseAuthKey::<A, B>::random_n(&mut rng, n_parties - 1),
589        }
590    }
591}
592
593impl<V, A, B> RandomWith<Vec<GlobalKey<A>>> for PairwiseAuthShare<V, A, B>
594where
595    V: Random + Clone,
596    A: Random + Clone,
597    B: Random + Clone + ConstantTimeEq + SubAssign + for<'b> Add<&'b B, Output = B>,
598    for<'a> V: Mul<&'a A, Output = B>,
599{
600    /// Generate a random authenticated share whose keys are derived from the given global keys.
601    fn random_with(mut rng: impl CryptoRngCore, alphas: Vec<GlobalKey<A>>) -> Self {
602        let value = V::random(&mut rng);
603        let keys: Box<[_]> = PairwiseAuthKey::<A, B>::random_n_with_each(&mut rng, alphas);
604        let macs: Box<[B]> = keys
605            .iter()
606            .map(|key| Self::compute_mac(value.clone(), key))
607            .collect();
608        PairwiseAuthShare { value, macs, keys }
609    }
610
611    /// Generate one authenticated share per peer, deriving each party's keys from their global
612    /// keys.
613    fn random_n_with_each<Container: FromIterator<Self>>(
614        mut rng: impl CryptoRngCore,
615        all_alphas: impl IntoExactSizeIterator<Item = Vec<GlobalKey<A>>>,
616    ) -> Container {
617        let all_alphas = all_alphas.into_iter();
618        let all_unauth_shares: Vec<_> = V::random_n(&mut rng, all_alphas.len());
619        let all_keys = all_alphas
620            .into_iter()
621            .map(|my_alphas| PairwiseAuthKey::<A, B>::random_n_with_each(&mut rng, my_alphas))
622            .collect::<Vec<_>>();
623        let all_macs = Self::compute_all_pairwise_macs(&all_unauth_shares, &all_keys);
624        izip_eq!(all_unauth_shares, all_macs, all_keys)
625            .map(|(value, macs, keys)| PairwiseAuthShare { value, macs, keys })
626            .collect()
627    }
628}
629
630impl<V, A, B> RandomWith<(V, Vec<GlobalKey<A>>)> for PairwiseAuthShare<V, A, B>
631where
632    V: Clone,
633    A: Random + Clone,
634    B: Random + Clone + ConstantTimeEq + SubAssign + for<'b> Add<&'b B, Output = B>,
635    for<'a> V: Mul<&'a A, Output = B>,
636{
637    /// Generate a random authenticated share with a given value whose keys are derived from alphas.
638    fn random_with(mut rng: impl CryptoRngCore, (value, alphas): (V, Vec<GlobalKey<A>>)) -> Self {
639        let keys: Box<[_]> = PairwiseAuthKey::<A, B>::random_n_with_each(&mut rng, alphas);
640        let macs: Box<[B]> = keys
641            .iter()
642            .map(|key| Self::compute_mac(value.clone(), key))
643            .collect();
644        PairwiseAuthShare { value, macs, keys }
645    }
646
647    /// Generate one authenticated share per peer from (value, alphas) pairs,
648    /// computing MACs consistently across parties.
649    fn random_n_with_each<Container: FromIterator<Self>>(
650        mut rng: impl CryptoRngCore,
651        unauth_shares_and_alphas: impl IntoExactSizeIterator<Item = (V, Vec<GlobalKey<A>>)>,
652    ) -> Container {
653        let (all_unauth_shares, all_keys): (Vec<_>, Vec<_>) = unauth_shares_and_alphas
654            .into_iter()
655            .map(|(value, my_alphas)| {
656                (
657                    value,
658                    PairwiseAuthKey::<A, B>::random_n_with_each(&mut rng, my_alphas),
659                )
660            })
661            .unzip();
662        let all_macs = Self::compute_all_pairwise_macs(&all_unauth_shares, &all_keys);
663        izip_eq!(all_unauth_shares, all_macs, all_keys)
664            .map(|(value, macs, keys)| PairwiseAuthShare { value, macs, keys })
665            .collect()
666    }
667}
668
669impl<V, A, B> RandomWith<(V, Vec<Vec<GlobalKey<A>>>)> for PairwiseAuthShare<V, A, B>
670where
671    V: AdditiveShares,
672    A: Random + Clone,
673    B: Random + Clone + ConstantTimeEq + SubAssign + for<'b> Add<&'b B, Output = B>,
674    for<'a> V: Mul<&'a A, Output = B>,
675{
676    fn random_with(_rng: impl CryptoRngCore, _: (V, Vec<Vec<GlobalKey<A>>>)) -> Self {
677        unimplemented!(
678            "Cannot discern what alpha/global key to use for this peer. Use `random_n_with` instead."
679        )
680    }
681
682    /// Secret share a value among n parties, each supplied with their own list of global keys.
683    fn random_n_with<Container: FromIterator<Self>>(
684        mut rng: impl CryptoRngCore,
685        n_parties: usize,
686        (secret_value, all_alphas): (V, Vec<Vec<GlobalKey<A>>>),
687    ) -> Container {
688        assert_eq!(
689            all_alphas.len(),
690            n_parties,
691            "Number of alphas must match the number of parties"
692        );
693        let all_unauth_shares = secret_value.to_additive_shares(all_alphas.len(), &mut rng);
694        let all_keys = all_alphas
695            .into_iter()
696            .map(|my_alphas| PairwiseAuthKey::<A, B>::random_n_with_each(&mut rng, my_alphas))
697            .collect::<Vec<_>>();
698        let all_macs = Self::compute_all_pairwise_macs(&all_unauth_shares, &all_keys);
699        izip_eq!(all_unauth_shares, all_macs, all_keys)
700            .map(|(value, macs, keys)| PairwiseAuthShare { value, macs, keys })
701            .collect()
702    }
703}
704
705// --------------
706// | Arithmetic |
707// --------------
708
709// === Addition === //
710
711#[macros::op_variants(owned, borrowed, flipped_commutative)]
712impl<'a, V, A, B> Add<&'a PairwiseAuthShare<V, A, B>> for PairwiseAuthShare<V, A, B>
713where
714    for<'v> V: Add<&'v V, Output = V>,
715    for<'b> B: Add<&'b B, Output = B>,
716    for<'k> PairwiseAuthKey<A, B>: Add<&'k PairwiseAuthKey<A, B>, Output = PairwiseAuthKey<A, B>>,
717{
718    type Output = PairwiseAuthShare<V, A, B>;
719
720    #[inline]
721    fn add(self, other: &'a PairwiseAuthShare<V, A, B>) -> Self::Output {
722        PairwiseAuthShare {
723            value: self.value + &other.value,
724            macs: izip_eq!(self.macs, &other.macs)
725                .map(|(mac_i, mac_j)| mac_i + mac_j)
726                .collect(),
727            keys: izip_eq!(self.keys, &other.keys)
728                .map(|(key_i, key_j)| key_i + key_j)
729                .collect(),
730        }
731    }
732}
733
734#[macros::op_variants(owned)]
735impl<'a, V, A, B> AddAssign<&'a PairwiseAuthShare<V, A, B>> for PairwiseAuthShare<V, A, B>
736where
737    for<'v> V: AddAssign<&'v V>,
738    for<'b> B: AddAssign<&'b B>,
739    for<'k> PairwiseAuthKey<A, B>: AddAssign<&'k PairwiseAuthKey<A, B>>,
740{
741    #[inline]
742    fn add_assign(&mut self, other: &'a PairwiseAuthShare<V, A, B>) {
743        self.value += &other.value;
744        izip_eq!(&mut self.macs, &other.macs).for_each(|(mac_i, mac_j)| *mac_i += mac_j);
745        izip_eq!(&mut self.keys, &other.keys).for_each(|(key_i, key_j)| *key_i += key_j);
746    }
747}
748
749impl<'a, V, A, B> Sum<&'a PairwiseAuthShare<V, A, B>> for PairwiseAuthShare<V, A, B>
750where
751    PairwiseAuthShare<V, A, B>: Clone + Default + AddAssign<&'a PairwiseAuthShare<V, A, B>>,
752{
753    #[inline]
754    fn sum<I: Iterator<Item = &'a PairwiseAuthShare<V, A, B>>>(mut iter: I) -> Self {
755        let first = iter.next().cloned().unwrap_or_default();
756        iter.fold(first, |mut acc, item| {
757            acc += item;
758            acc
759        })
760    }
761}
762
763impl<V, A, B> Sum for PairwiseAuthShare<V, A, B>
764where
765    for<'v> V: AddAssign<&'v V>,
766    for<'b> B: AddAssign<&'b B>,
767    for<'k> PairwiseAuthKey<A, B>: AddAssign<&'k PairwiseAuthKey<A, B>>,
768    PairwiseAuthShare<V, A, B>: Default,
769{
770    #[inline]
771    fn sum<I: Iterator<Item = Self>>(mut iter: I) -> Self {
772        let first = iter.next().unwrap_or_default();
773        iter.fold(first, |mut acc, item| {
774            acc += &item;
775            acc
776        })
777    }
778}
779
780// === Subtraction === //
781
782#[macros::op_variants(owned, borrowed, flipped)]
783impl<'a, V, A, B> Sub<&'a PairwiseAuthShare<V, A, B>> for PairwiseAuthShare<V, A, B>
784where
785    for<'v> V: Sub<&'v V, Output = V>,
786    for<'b> B: Sub<&'b B, Output = B>,
787    for<'k> PairwiseAuthKey<A, B>: Sub<&'k PairwiseAuthKey<A, B>, Output = PairwiseAuthKey<A, B>>,
788{
789    type Output = PairwiseAuthShare<V, A, B>;
790
791    #[inline]
792    fn sub(self, other: &'a PairwiseAuthShare<V, A, B>) -> Self::Output {
793        PairwiseAuthShare {
794            value: self.value - &other.value,
795            macs: izip_eq!(self.macs, &other.macs)
796                .map(|(mac_i, mac_j)| mac_i - mac_j)
797                .collect(),
798            keys: izip_eq!(self.keys, &other.keys)
799                .map(|(key_i, key_j)| key_i - key_j)
800                .collect(),
801        }
802    }
803}
804
805#[macros::op_variants(owned)]
806impl<'a, V, A, B> SubAssign<&'a PairwiseAuthShare<V, A, B>> for PairwiseAuthShare<V, A, B>
807where
808    for<'v> V: SubAssign<&'v V>,
809    for<'b> B: SubAssign<&'b B>,
810    for<'k> PairwiseAuthKey<A, B>: SubAssign<&'k PairwiseAuthKey<A, B>>,
811{
812    #[inline]
813    fn sub_assign(&mut self, other: &'a PairwiseAuthShare<V, A, B>) {
814        self.value -= &other.value;
815        izip_eq!(&mut self.macs, &other.macs).for_each(|(mac_i, mac_j)| *mac_i -= mac_j);
816        izip_eq!(&mut self.keys, &other.keys).for_each(|(key_i, key_j)| *key_i -= key_j);
817    }
818}
819
820// === Constant multiplication === //
821
822#[macros::op_variants(borrowed)]
823impl<'a, V, V2, A, B, B2, Const> Mul<&'a Const> for PairwiseAuthShare<V, A, B>
824where
825    for<'v> V: Mul<&'v Const, Output = V2>,
826    for<'b> B: Mul<&'b Const, Output = B2>,
827{
828    type Output = PairwiseAuthShare<V2, A, B2>;
829    #[inline]
830    fn mul(self, other: &'a Const) -> Self::Output {
831        PairwiseAuthShare {
832            value: self.value * other,
833            macs: self
834                .macs
835                .into_vec()
836                .into_iter()
837                .map(|mac| mac * other)
838                .collect(),
839            keys: self
840                .keys
841                .into_vec()
842                .into_iter()
843                .map(|key| key * other)
844                .collect(),
845        }
846    }
847}
848
849// === MulAssign === //
850
851impl<'a, V, A, B, Const> MulAssign<&'a Const> for PairwiseAuthShare<V, A, B>
852where
853    for<'v> V: MulAssign<&'v Const>,
854    for<'b> B: MulAssign<&'b Const>,
855{
856    #[inline]
857    fn mul_assign(&mut self, other: &'a Const) {
858        self.value *= other;
859        izip_eq!(&mut self.keys).for_each(|key| *key *= other);
860        izip_eq!(&mut self.macs).for_each(|mac| *mac *= other);
861    }
862}
863
864// === Negation === //
865
866#[macros::op_variants(borrowed)]
867impl<V, A, B> Neg for PairwiseAuthShare<V, A, B>
868where
869    V: Neg<Output = V>,
870    B: Neg<Output = B>,
871    PairwiseAuthKey<A, B>: Neg<Output = PairwiseAuthKey<A, B>>,
872{
873    type Output = PairwiseAuthShare<V, A, B>;
874
875    #[inline]
876    fn neg(self) -> Self::Output {
877        let PairwiseAuthShare { value, macs, keys } = self;
878        PairwiseAuthShare {
879            value: -value,
880            keys: keys.into_vec().into_iter().map(|key| -key).collect(),
881            macs: macs.into_vec().into_iter().map(|mac| -mac).collect(),
882        }
883    }
884}
885
886// === Constant addition / subtraction === //
887
888impl<V, A, B> PlaintextOps for PairwiseAuthShare<V, A, B>
889where
890    PairwiseAuthShare<V, A, B>: Reconstructible<Value = V>,
891    V: Clone + for<'a> AddAssign<&'a V> + for<'a> SubAssign<&'a V>,
892    A: Clone,
893    B: Clone + for<'b> AddAssign<&'b B> + for<'b> SubAssign<&'b B> + ConstantTimeEq,
894    for<'a> V: Mul<&'a A, Output = B>,
895{
896    /// If this is the first peer, adds the plaintext to the value;
897    /// otherwise adjusts the first key's beta: `β₀ -= α₀ · ptx`.
898    #[inline]
899    fn add_plaintext(mut self, ptx: &V, is_first_peer: bool) -> Self {
900        if is_first_peer {
901            self.value += ptx;
902        } else {
903            let key0 = self.keys.get_mut(0).expect("Missing key 0");
904            key0.beta -= &(ptx.to_owned() * &key0.alpha);
905        }
906        self
907    }
908
909    /// If this is the first peer, subtracts the plaintext from the value;
910    /// otherwise adjusts the first key's beta: `β₀ += α₀ · ptx`.
911    #[inline]
912    fn sub_plaintext(mut self, ptx: &V, is_first_peer: bool) -> Self {
913        if is_first_peer {
914            self.value -= ptx;
915        } else {
916            let key0 = self.keys.get_mut(0).expect("Missing key 0");
917            key0.beta += &(ptx.to_owned() * &key0.alpha);
918        }
919        self
920    }
921}
922
923// ---------------------------------------
924// |  Constant time Selection / Equality |
925// ---------------------------------------
926
927impl<V, A, B> ConstantTimeEq for PairwiseAuthShare<V, A, B>
928where
929    V: ConstantTimeEq,
930    B: ConstantTimeEq,
931    PairwiseAuthKey<A, B>: ConstantTimeEq,
932{
933    #[inline]
934    fn ct_eq(&self, other: &Self) -> Choice {
935        self.value.ct_eq(&other.value) & self.macs.ct_eq(&other.macs) & self.keys.ct_eq(&other.keys)
936    }
937}
938
939impl<V, A, B> ConditionallySelectable for PairwiseAuthShare<V, A, B>
940where
941    V: ConditionallySelectable,
942    B: ConditionallySelectable,
943    PairwiseAuthKey<A, B>: ConditionallySelectable,
944{
945    #[inline]
946    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
947        PairwiseAuthShare {
948            value: V::conditional_select(&a.value, &b.value, choice),
949            macs: izip_eq!(&a.macs, &b.macs)
950                .map(|(a_mac, b_mac)| B::conditional_select(a_mac, b_mac, choice))
951                .collect(),
952            keys: izip_eq!(&a.keys, &b.keys)
953                .map(|(a_key, b_key)| {
954                    PairwiseAuthKey::<A, B>::conditional_select(a_key, b_key, choice)
955                })
956                .collect(),
957        }
958    }
959}
960
961/// Owning iterator over the element-wise single-element shares of a batched
962/// [`PairwiseAuthShare`] such as `FieldShares<F, M>` or `PointShares<C, M>`.
963///
964/// Replaces the per-type `FieldSharesIterator` and `PointSharesIterator`.
965#[derive(Clone, Debug)]
966pub struct BatchedSharesIterator<VIter: Iterator, MacIter: Iterator, A, BIter: Iterator> {
967    remaining: usize,
968    value: VIter,
969    macs: Vec<MacIter>,
970    betas: Vec<BIter>,
971    alphas: Vec<GlobalKey<A>>,
972}
973
974impl<VIter: Iterator, MacIter: Iterator, A: Clone, BIter: Iterator<Item = MacIter::Item>> Iterator
975    for BatchedSharesIterator<VIter, MacIter, A, BIter>
976{
977    type Item = PairwiseAuthShare<VIter::Item, A, MacIter::Item>;
978
979    fn next(&mut self) -> Option<Self::Item> {
980        if self.remaining == 0 {
981            return None;
982        }
983        let value = self.value.next()?;
984        let macs: Box<[_]> = self
985            .macs
986            .iter_mut()
987            .map(|it| it.next())
988            .collect::<Option<_>>()?;
989        let keys: Box<[_]> = izip!(&mut self.betas, &self.alphas)
990            .map(|(beta_iter, alpha)| {
991                beta_iter
992                    .next()
993                    .map(|beta| PairwiseAuthKey::new(alpha.clone(), beta))
994            })
995            .collect::<Option<_>>()?;
996        self.remaining -= 1;
997        Some(PairwiseAuthShare { value, macs, keys })
998    }
999
1000    fn size_hint(&self) -> (usize, Option<usize>) {
1001        (self.remaining, Some(self.remaining))
1002    }
1003}
1004
1005impl<VIter: Iterator, MacIter: Iterator, A: Clone, BIter: Iterator<Item = MacIter::Item>>
1006    ExactSizeIterator for BatchedSharesIterator<VIter, MacIter, A, BIter>
1007{
1008    fn len(&self) -> usize {
1009        self.remaining
1010    }
1011}
1012
1013// ---------------------------------------------------------------
1014// |  IntoIterator for PairwiseAuthShare                        |
1015// ---------------------------------------------------------------
1016
1017impl<V, A, B> IntoIterator for PairwiseAuthShare<V, A, B>
1018where
1019    V: IntoIterator,
1020    V::IntoIter: ExactSizeIterator,
1021    B: IntoIterator,
1022    A: Clone,
1023{
1024    type Item = PairwiseAuthShare<V::Item, A, B::Item>;
1025    type IntoIter = BatchedSharesIterator<V::IntoIter, B::IntoIter, A, B::IntoIter>;
1026
1027    fn into_iter(self) -> Self::IntoIter {
1028        let PairwiseAuthShare { value, macs, keys } = self;
1029        let value_iter = value.into_iter();
1030        let remaining = value_iter.len();
1031        let macs = macs.into_vec().into_iter().map(|m| m.into_iter()).collect();
1032        let (betas, alphas): (Vec<_>, Vec<_>) = keys
1033            .into_vec()
1034            .into_iter()
1035            .map(|k| (k.beta.into_iter(), k.alpha))
1036            .unzip();
1037        BatchedSharesIterator {
1038            remaining,
1039            value: value_iter,
1040            macs,
1041            betas,
1042            alphas,
1043        }
1044    }
1045}
1046
1047impl<V, A, B> IntoIterator for &PairwiseAuthShare<V, A, B>
1048where
1049    V: Clone + IntoIterator,
1050    V::IntoIter: ExactSizeIterator,
1051    B: Clone + IntoIterator,
1052    A: Clone,
1053{
1054    type Item = PairwiseAuthShare<V::Item, A, B::Item>;
1055    type IntoIter = BatchedSharesIterator<V::IntoIter, B::IntoIter, A, B::IntoIter>;
1056
1057    fn into_iter(self) -> Self::IntoIter {
1058        let value_iter = self.value.clone().into_iter();
1059        let remaining = value_iter.len();
1060        let macs = self.macs.iter().map(|m| m.clone().into_iter()).collect();
1061        let (betas, alphas): (Vec<_>, Vec<_>) = self
1062            .keys
1063            .iter()
1064            .map(|k| (k.beta.clone().into_iter(), k.alpha.clone()))
1065            .unzip();
1066        BatchedSharesIterator {
1067            remaining,
1068            value: value_iter,
1069            macs,
1070            betas,
1071            alphas,
1072        }
1073    }
1074}
1075
1076// -----------------------------------------------------------------
1077// |  FromIterator: collect single-element shares into a batch     |
1078// -----------------------------------------------------------------
1079
1080impl<ItemV, A: Clone, ItemB, V, B> FromIterator<PairwiseAuthShare<ItemV, A, ItemB>>
1081    for PairwiseAuthShare<V, A, B>
1082where
1083    V: FromIterator<ItemV>,
1084    B: FromIterator<ItemB>,
1085{
1086    fn from_iter<T: IntoIterator<Item = PairwiseAuthShare<ItemV, A, ItemB>>>(iter: T) -> Self {
1087        let (values, macs, keys): (Vec<_>, Vec<Vec<_>>, Vec<Vec<_>>) = iter
1088            .into_iter()
1089            .map(|s| (s.value, s.macs.into_vec(), s.keys.into_vec()))
1090            .multiunzip();
1091        let macs: Box<[B]> = transpose(macs)
1092            .into_iter()
1093            .map(|pm| pm.into_iter().collect::<B>())
1094            .collect::<Vec<_>>()
1095            .into();
1096        let keys: Box<[PairwiseAuthKey<A, B>]> = transpose(keys)
1097            .into_iter()
1098            .map(|peer_keys: Vec<PairwiseAuthKey<A, ItemB>>| {
1099                let alpha = peer_keys[0].get_alpha().to_owned();
1100                let betas: B = peer_keys.into_iter().map(|k| k.beta).collect();
1101                PairwiseAuthKey::new(alpha.into(), betas)
1102            })
1103            .collect::<Vec<_>>()
1104            .into();
1105        PairwiseAuthShare {
1106            value: values.into_iter().collect::<V>(),
1107            macs,
1108            keys,
1109        }
1110    }
1111}
1112
1113// -------------------------------------------------------
1114// |  From<single-element share> for U1-sized batch      |
1115// -------------------------------------------------------
1116
1117impl<ItemV, A, ItemB> From<PairwiseAuthShare<ItemV, A, ItemB>>
1118    for BatchedShare<ItemV, A, ItemB, U1>
1119{
1120    fn from(share: PairwiseAuthShare<ItemV, A, ItemB>) -> Self {
1121        PairwiseAuthShare {
1122            value: HeapArray::from(share.value),
1123            macs: share
1124                .macs
1125                .into_vec()
1126                .into_iter()
1127                .map(HeapArray::from)
1128                .collect(),
1129            keys: share
1130                .keys
1131                .into_vec()
1132                .into_iter()
1133                .map(PairwiseAuthKey::from)
1134                .collect(),
1135        }
1136    }
1137}
1138
1139// -------------------------------------------------------
1140// |  From<HeapArray<single, N>> for batched             |
1141// -------------------------------------------------------
1142
1143impl<ItemV, A: Clone, ItemB, V, B, N: Positive>
1144    From<HeapArray<PairwiseAuthShare<ItemV, A, ItemB>, N>> for PairwiseAuthShare<V, A, B>
1145where
1146    V: FromIterator<ItemV>,
1147    B: FromIterator<ItemB>,
1148{
1149    fn from(shares: HeapArray<PairwiseAuthShare<ItemV, A, ItemB>, N>) -> Self {
1150        shares.into_iter().collect()
1151    }
1152}
1153
1154// --------------------------------
1155// |  IntoParallelIterator        |
1156// --------------------------------
1157
1158impl<ItemV: Send, A: Clone + Send, ItemB: Send, M: Positive> IntoParallelIterator
1159    for BatchedShare<ItemV, A, ItemB, M>
1160where
1161    PairwiseAuthShare<ItemV, A, ItemB>: Send,
1162{
1163    type Item = PairwiseAuthShare<ItemV, A, ItemB>;
1164    type Iter = rayon::vec::IntoIter<Self::Item>;
1165
1166    fn into_par_iter(self) -> Self::Iter {
1167        // Convert HeapArray → Vec (using the existing From impl) to get an
1168        // ExactSizeIterator without needing to add a bounds chain for the
1169        // derive_more-generated HeapArray: IntoIterator impl.
1170        let PairwiseAuthShare { value, macs, keys } = self;
1171        let value_iter = Vec::from(value).into_iter();
1172        let remaining = value_iter.len();
1173        let mac_iters: Vec<_> = macs
1174            .into_vec()
1175            .into_iter()
1176            .map(|m| Vec::from(m).into_iter())
1177            .collect();
1178        let (betas, alphas): (Vec<_>, Vec<_>) = keys
1179            .into_vec()
1180            .into_iter()
1181            .map(|k| (Vec::from(k.beta).into_iter(), k.alpha))
1182            .unzip();
1183        BatchedSharesIterator {
1184            remaining,
1185            value: value_iter,
1186            macs: mac_iters,
1187            betas,
1188            alphas,
1189        }
1190        .collect::<Vec<_>>()
1191        .into_par_iter()
1192    }
1193}
1194
1195// --------------------------
1196// |  Batched Operations    |
1197// --------------------------
1198
1199impl<V: Copy, A: Clone, B: Copy, M: Positive> BatchedShare<V, A, B, M> {
1200    #[allow(clippy::type_complexity)]
1201    /// Splits a batched share of size M into two smaller batched shares (M1, M2),
1202    /// where M = M1 + M2.
1203    pub fn split<M1, M2>(self) -> (BatchedShare<V, A, B, M1>, BatchedShare<V, A, B, M2>)
1204    where
1205        M1: Positive,
1206        M2: Positive + Add<M1, Output = M>,
1207    {
1208        let PairwiseAuthShare { value, macs, keys } = self;
1209        let (v1, v2) = value.split::<M1, M2>();
1210        let (macs1, macs2): (Vec<_>, Vec<_>) = macs
1211            .into_vec()
1212            .into_iter()
1213            .map(|m| m.split::<M1, M2>())
1214            .unzip();
1215        let (keys1, keys2): (Vec<_>, Vec<_>) = keys
1216            .into_vec()
1217            .into_iter()
1218            .map(|k| k.split::<M1, M2>())
1219            .unzip();
1220        (
1221            PairwiseAuthShare::try_new(v1, macs1.into(), keys1.into()).unwrap(),
1222            PairwiseAuthShare::try_new(v2, macs2.into(), keys2.into()).unwrap(),
1223        )
1224    }
1225
1226    #[allow(clippy::type_complexity)]
1227    /// Splits a batched share of size M into two smaller batched shares (M1, M2),
1228    /// where M = M1 + M2.
1229    pub fn split_halves<MDiv2>(self) -> (BatchedShare<V, A, B, MDiv2>, BatchedShare<V, A, B, MDiv2>)
1230    where
1231        MDiv2: Positive + Mul<U2, Output = M>,
1232    {
1233        let PairwiseAuthShare { value, macs, keys } = self;
1234        let (v1, v2) = value.split_halves::<MDiv2>();
1235        let (macs1, macs2): (Vec<_>, Vec<_>) = macs
1236            .into_vec()
1237            .into_iter()
1238            .map(|m| m.split_halves::<MDiv2>())
1239            .unzip();
1240        let (keys1, keys2): (Vec<_>, Vec<_>) = keys
1241            .into_vec()
1242            .into_iter()
1243            .map(|k| k.split_halves::<MDiv2>())
1244            .unzip();
1245        (
1246            PairwiseAuthShare::try_new(v1, macs1.into(), keys1.into()).unwrap(),
1247            PairwiseAuthShare::try_new(v2, macs2.into(), keys2.into()).unwrap(),
1248        )
1249    }
1250
1251    #[allow(clippy::type_complexity)]
1252    /// Merges two batched shares of sizes M/2 into a larger batched share of size M.
1253    pub fn merge_halves(this: Self, other: Self) -> BatchedShare<V, A, B, Prod<M, U2>>
1254    where
1255        M: Mul<U2, Output: Positive>,
1256        A: PartialEq,
1257    {
1258        let PairwiseAuthShare {
1259            value: v1,
1260            macs: m1,
1261            keys: k1,
1262        } = this;
1263        let PairwiseAuthShare {
1264            value: v2,
1265            macs: m2,
1266            keys: k2,
1267        } = other;
1268        let value = HeapArray::merge_halves(v1, v2);
1269        let macs: Box<[_]> = izip_eq!(m1, m2)
1270            .map(|(a, b)| HeapArray::merge_halves(a, b))
1271            .collect();
1272        let keys: Box<[_]> = izip_eq!(k1, k2)
1273            .map(|(a, b)| PairwiseAuthKey::merge_halves(a, b))
1274            .collect();
1275        PairwiseAuthShare::try_new(value, macs, keys).unwrap()
1276    }
1277
1278    #[allow(clippy::type_complexity)]
1279    /// Splits a batched share of size M into three smaller batched shares (M1, M2, M3),
1280    /// where M = M1 + M2 + M3.
1281    pub fn split_thirds<MDiv3>(
1282        self,
1283    ) -> (
1284        BatchedShare<V, A, B, MDiv3>,
1285        BatchedShare<V, A, B, MDiv3>,
1286        BatchedShare<V, A, B, MDiv3>,
1287    )
1288    where
1289        MDiv3: Positive + Mul<U3, Output = M>,
1290    {
1291        let PairwiseAuthShare { value, macs, keys } = self;
1292        let (v1, v2, v3) = value.split_thirds::<MDiv3>();
1293        let (macs1, macs2, macs3): (Vec<_>, Vec<_>, Vec<_>) = macs
1294            .into_vec()
1295            .into_iter()
1296            .map(|m| m.split_thirds::<MDiv3>())
1297            .multiunzip();
1298        let (keys1, keys2, keys3): (Vec<_>, Vec<_>, Vec<_>) = keys
1299            .into_vec()
1300            .into_iter()
1301            .map(|k| k.split_thirds::<MDiv3>())
1302            .multiunzip();
1303        (
1304            PairwiseAuthShare::try_new(v1, macs1.into(), keys1.into()).unwrap(),
1305            PairwiseAuthShare::try_new(v2, macs2.into(), keys2.into()).unwrap(),
1306            PairwiseAuthShare::try_new(v3, macs3.into(), keys3.into()).unwrap(),
1307        )
1308    }
1309
1310    #[allow(clippy::type_complexity)]
1311    /// Splits a batched share of size M into five smaller batched shares of size M/5.
1312    pub fn split_fifths<MDiv5>(
1313        self,
1314    ) -> (
1315        BatchedShare<V, A, B, MDiv5>,
1316        BatchedShare<V, A, B, MDiv5>,
1317        BatchedShare<V, A, B, MDiv5>,
1318        BatchedShare<V, A, B, MDiv5>,
1319        BatchedShare<V, A, B, MDiv5>,
1320    )
1321    where
1322        MDiv5: Positive + Mul<U5, Output = M>,
1323    {
1324        fn split_array<T: Copy, M: Positive, MDiv5: Positive + Mul<U5, Output = M>>(
1325            arr: HeapArray<T, M>,
1326        ) -> (
1327            HeapArray<T, MDiv5>,
1328            HeapArray<T, MDiv5>,
1329            HeapArray<T, MDiv5>,
1330            HeapArray<T, MDiv5>,
1331            HeapArray<T, MDiv5>,
1332        ) {
1333            let n = MDiv5::USIZE;
1334            let (a, rest) = arr.split_at(n);
1335            let (b, rest) = rest.split_at(n);
1336            let (c, rest) = rest.split_at(n);
1337            let (d, e) = rest.split_at(n);
1338            (
1339                HeapArray::<T, MDiv5>::try_from(a.to_vec()).unwrap(),
1340                HeapArray::<T, MDiv5>::try_from(b.to_vec()).unwrap(),
1341                HeapArray::<T, MDiv5>::try_from(c.to_vec()).unwrap(),
1342                HeapArray::<T, MDiv5>::try_from(d.to_vec()).unwrap(),
1343                HeapArray::<T, MDiv5>::try_from(e.to_vec()).unwrap(),
1344            )
1345        }
1346
1347        let PairwiseAuthShare { value, macs, keys } = self;
1348        let (v1, v2, v3, v4, v5) = split_array::<V, M, MDiv5>(value);
1349        let (macs1, macs2, macs3, macs4, macs5): (Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = macs
1350            .into_vec()
1351            .into_iter()
1352            .map(split_array::<B, M, MDiv5>)
1353            .multiunzip();
1354        let (keys1, keys2, keys3, keys4, keys5): (Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = keys
1355            .into_vec()
1356            .into_iter()
1357            .map(|k| {
1358                let PairwiseAuthKey { alpha, beta } = k;
1359                let (b1, b2, b3, b4, b5) = split_array::<B, M, MDiv5>(beta);
1360                (
1361                    PairwiseAuthKey::new(alpha.clone(), b1),
1362                    PairwiseAuthKey::new(alpha.clone(), b2),
1363                    PairwiseAuthKey::new(alpha.clone(), b3),
1364                    PairwiseAuthKey::new(alpha.clone(), b4),
1365                    PairwiseAuthKey::new(alpha, b5),
1366                )
1367            })
1368            .multiunzip();
1369        (
1370            PairwiseAuthShare::try_new(v1, macs1.into(), keys1.into()).unwrap(),
1371            PairwiseAuthShare::try_new(v2, macs2.into(), keys2.into()).unwrap(),
1372            PairwiseAuthShare::try_new(v3, macs3.into(), keys3.into()).unwrap(),
1373            PairwiseAuthShare::try_new(v4, macs4.into(), keys4.into()).unwrap(),
1374            PairwiseAuthShare::try_new(v5, macs5.into(), keys5.into()).unwrap(),
1375        )
1376    }
1377
1378    #[allow(clippy::type_complexity)]
1379    /// Merges three batched shares of sizes M/3 into a larger batched share of size M.
1380    pub fn merge_thirds(
1381        first: Self,
1382        second: Self,
1383        third: Self,
1384    ) -> BatchedShare<V, A, B, Prod<M, U3>>
1385    where
1386        M: Mul<U3, Output: Positive>,
1387        A: PartialEq,
1388    {
1389        let PairwiseAuthShare {
1390            value: v1,
1391            macs: m1,
1392            keys: k1,
1393        } = first;
1394        let PairwiseAuthShare {
1395            value: v2,
1396            macs: m2,
1397            keys: k2,
1398        } = second;
1399        let PairwiseAuthShare {
1400            value: v3,
1401            macs: m3,
1402            keys: k3,
1403        } = third;
1404        let value = HeapArray::merge_thirds(v1, v2, v3);
1405        let macs: Box<[_]> = izip_eq!(m1, m2, m3)
1406            .map(|(a, b, c)| HeapArray::merge_thirds(a, b, c))
1407            .collect();
1408        let keys: Box<[_]> = izip_eq!(k1, k2, k3)
1409            .map(|(a, b, c)| PairwiseAuthKey::merge_thirds(a, b, c))
1410            .collect();
1411        PairwiseAuthShare::try_new(value, macs, keys).unwrap()
1412    }
1413
1414    #[allow(clippy::type_complexity)]
1415    /// Creates an iterator that yields fixed-size batched chunks of size `CS`.
1416    pub fn chunks<CS: Positive>(
1417        &self,
1418    ) -> BatchedSharesChunks<
1419        <HeapArray<V, M> as IntoIterator>::IntoIter,
1420        <HeapArray<B, M> as IntoIterator>::IntoIter,
1421        A,
1422        <HeapArray<B, M> as IntoIterator>::IntoIter,
1423        CS,
1424    >
1425    where
1426        M: PartialDiv<CS>,
1427    {
1428        BatchedSharesChunks {
1429            inner: self.into_iter(),
1430            remaining_chunks: M::USIZE / CS::USIZE,
1431            _ds: PhantomData,
1432        }
1433    }
1434
1435    /// Swaps elements at positions `i` and `j` in-place across
1436    /// value, macs, and key betas arrays.
1437    pub fn swap(&mut self, i: usize, j: usize) {
1438        self.value.swap(i, j);
1439        for mac in self.macs.iter_mut() {
1440            mac.swap(i, j);
1441        }
1442        for key in self.keys.iter_mut() {
1443            key.beta.swap(i, j);
1444        }
1445    }
1446}
1447
1448// ---------------------------------------------------------------
1449// |  BatchedSharesChunks — chunk-level iterator               |
1450// ---------------------------------------------------------------
1451
1452/// An iterator over fixed-size chunks of a batched [`PairwiseAuthShare`].
1453pub struct BatchedSharesChunks<VIter: Iterator, MacIter: Iterator, A, BIter: Iterator, DS: Positive>
1454{
1455    inner: BatchedSharesIterator<VIter, MacIter, A, BIter>,
1456    remaining_chunks: usize,
1457    _ds: PhantomData<DS>,
1458}
1459
1460impl<VIter: Iterator, MacIter: Iterator, A: Clone, BIter: Iterator, DS: Positive>
1461    BatchedSharesChunks<VIter, MacIter, A, BIter, DS>
1462{
1463    pub fn len(&self) -> usize {
1464        self.remaining_chunks
1465    }
1466
1467    pub fn is_empty(&self) -> bool {
1468        self.remaining_chunks == 0
1469    }
1470}
1471
1472impl<
1473        VIter: Iterator,
1474        MacIter: Iterator,
1475        A: Clone,
1476        BIter: Iterator<Item = MacIter::Item>,
1477        DS: Positive,
1478    > Iterator for BatchedSharesChunks<VIter, MacIter, A, BIter, DS>
1479where
1480    HeapArray<VIter::Item, DS>: FromIterator<VIter::Item>,
1481    HeapArray<MacIter::Item, DS>: FromIterator<MacIter::Item>,
1482{
1483    type Item = BatchedShare<VIter::Item, A, MacIter::Item, DS>;
1484
1485    fn next(&mut self) -> Option<Self::Item> {
1486        if self.remaining_chunks == 0 {
1487            return None;
1488        }
1489        let chunk: Vec<_> = self.inner.by_ref().take(DS::to_usize()).collect();
1490        if chunk.is_empty() {
1491            return None;
1492        }
1493        self.remaining_chunks -= 1;
1494        Some(chunk.into_iter().collect())
1495    }
1496
1497    fn size_hint(&self) -> (usize, Option<usize>) {
1498        (self.remaining_chunks, Some(self.remaining_chunks))
1499    }
1500}
1501
1502impl<
1503        VIter: Iterator,
1504        MacIter: Iterator,
1505        A: Clone,
1506        BIter: Iterator<Item = MacIter::Item>,
1507        DS: Positive,
1508    > ExactSizeIterator for BatchedSharesChunks<VIter, MacIter, A, BIter, DS>
1509where
1510    HeapArray<VIter::Item, DS>: FromIterator<VIter::Item>,
1511    HeapArray<MacIter::Item, DS>: FromIterator<MacIter::Item>,
1512{
1513    fn len(&self) -> usize {
1514        self.remaining_chunks
1515    }
1516}
1517
1518// ------------------------------------------------------------------
1519// |  Reconstructible for HeapArray<Share, N>                       |
1520// ------------------------------------------------------------------
1521
1522/// Reconstruction protocol for a length-`N` array of [`PairwiseAuthShare`]s.
1523/// Each element is independently reconstructed and the secrets are collected.
1524impl<V, A, B, N: Positive> Reconstructible for HeapArray<PairwiseAuthShare<V, A, B>, N>
1525where
1526    PairwiseAuthShare<V, A, B>: Reconstructible,
1527    <PairwiseAuthShare<V, A, B> as Reconstructible>::Opening: Clone,
1528{
1529    type Opening = HeapArray<<PairwiseAuthShare<V, A, B> as Reconstructible>::Opening, N>;
1530    type Value = HeapArray<<PairwiseAuthShare<V, A, B> as Reconstructible>::Value, N>;
1531
1532    fn open_to(&self, for_peer: PeerIndex) -> Result<Self::Opening, PrimitiveError> {
1533        self.into_iter()
1534            .map(|s| s.open_to(for_peer))
1535            .collect::<Result<_, _>>()
1536    }
1537
1538    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening> {
1539        let per_share: Vec<Vec<<PairwiseAuthShare<V, A, B> as Reconstructible>::Opening>> = self
1540            .into_iter()
1541            .map(|s| s.open_to_all_others().collect())
1542            .collect();
1543        transpose(per_share)
1544            .into_iter()
1545            .map(|col| col.try_into().expect("size mismatch in open_to_all_others"))
1546    }
1547
1548    fn reconstruct(&self, openings: Vec<Self::Opening>) -> Result<Self::Value, PrimitiveError> {
1549        self.iter()
1550            .enumerate()
1551            .map(|(i, share)| {
1552                let my_openings: Vec<_> = openings
1553                    .iter()
1554                    .map(|o| o.get(i).cloned())
1555                    .collect::<Option<_>>()
1556                    .ok_or_else(|| {
1557                        PrimitiveError::InvalidParameters(
1558                            "Opening is missing for some share.".to_string(),
1559                        )
1560                    })?;
1561                share.reconstruct(my_openings)
1562            })
1563            .collect::<Result<_, _>>()
1564    }
1565}
1566
1567// --- Type conversions --- //
1568
1569impl<C: Curve> From<ScalarShare<C>> for PointShare<C> {
1570    #[inline]
1571    fn from(scalar_share: ScalarShare<C>) -> Self {
1572        scalar_share * &Point::<C>::generator()
1573    }
1574}
1575
1576impl<C: Curve, M: Positive> From<ScalarShares<C, M>> for PointShares<C, M> {
1577    #[inline]
1578    fn from(scalar_shares: ScalarShares<C, M>) -> Self {
1579        scalar_shares * &Point::<C>::generator()
1580    }
1581}
1582
1583#[cfg(test)]
1584mod tests {
1585    use std::{fmt::Debug, ops::Div};
1586
1587    use typenum::{U2, U3};
1588
1589    use super::*;
1590    use crate::{random::test_rng, sharing::Verifiable};
1591
1592    // ─── Marker Trait: ShareValue ────────────────────────────────────────────
1593    /// Bundle of all bounds required on the **value type `V`** by the test suite.
1594    ///
1595    /// Blanket-implemented for every type satisfying the constituent bounds, so
1596    /// callers only need to write `V: ShareValue<A, B, Const>`.
1597    pub(super) trait ShareValue<A, B, Const>:
1598        Clone
1599        + PartialEq
1600        + Debug
1601        + Random
1602        + AdditiveShares
1603        + ConstantTimeEq
1604        + ConditionallySelectable
1605        + Neg<Output = Self>
1606        + for<'a> Sub<&'a Self, Output = Self>
1607        + for<'a> AddAssign<&'a Self>
1608        + for<'a> Mul<&'a A, Output = B>
1609        + for<'a> Mul<&'a Const, Output = Self>
1610        + for<'a> MulAssign<&'a Const>
1611        + Send
1612        + Sync
1613        + 'static
1614        + Serialize
1615        + DeserializeOwned
1616        + SchemaWrite<Src = Self>
1617        + for<'de> SchemaRead<'de, Dst = Self>
1618    {
1619    }
1620
1621    impl<V, A, B, Const> ShareValue<A, B, Const> for V where
1622        V: Clone
1623            + PartialEq
1624            + Debug
1625            + Random
1626            + AdditiveShares
1627            + ConstantTimeEq
1628            + ConditionallySelectable
1629            + Neg<Output = V>
1630            + for<'a> Sub<&'a V, Output = V>
1631            + for<'a> AddAssign<&'a V>
1632            + for<'a> Mul<&'a A, Output = B>
1633            + for<'a> Mul<&'a Const, Output = V>
1634            + for<'a> MulAssign<&'a Const>
1635            + Send
1636            + Sync
1637            + 'static
1638            + Serialize
1639            + DeserializeOwned
1640            + SchemaWrite<Src = V>
1641            + for<'de> SchemaRead<'de, Dst = V>
1642    {
1643    }
1644
1645    // ─── Marker Trait: ShareKey ──────────────────────────────────────────────
1646    /// Bundle of all bounds required on the **global-key inner type `A`** by the
1647    /// test suite.
1648    pub(super) trait ShareKey:
1649        Clone + PartialEq + Debug + Random + ConstantTimeEq + ConditionallySelectable
1650    {
1651    }
1652
1653    impl<A> ShareKey for A where
1654        A: Clone + PartialEq + Debug + Random + ConstantTimeEq + ConditionallySelectable
1655    {
1656    }
1657
1658    // ─── Marker Trait: ShareMac ──────────────────────────────────────────────
1659    /// Bundle of all bounds required on the **MAC / local-key type `B`** by the
1660    /// test suite.
1661    pub(super) trait ShareMac<V, A, Const>:
1662        Clone
1663        + PartialEq
1664        + Debug
1665        + Random
1666        + ConstantTimeEq
1667        + ConditionallySelectable
1668        + SubAssign
1669        + Neg<Output = Self>
1670        + for<'b> Add<&'b Self, Output = Self>
1671        + for<'b> AddAssign<&'b Self>
1672        + for<'b> Sub<&'b Self, Output = Self>
1673        + for<'b> SubAssign<&'b Self>
1674        + for<'c> Mul<&'c Const, Output = Self>
1675        + for<'c> MulAssign<&'c Const>
1676        + Send
1677        + Sync
1678        + 'static
1679        + Serialize
1680        + DeserializeOwned
1681        + SchemaWrite<Src = Self>
1682        + for<'de> SchemaRead<'de, Dst = Self>
1683    {
1684    }
1685
1686    impl<B, V, A, Const> ShareMac<V, A, Const> for B where
1687        B: Clone
1688            + PartialEq
1689            + Debug
1690            + Random
1691            + ConstantTimeEq
1692            + ConditionallySelectable
1693            + SubAssign
1694            + Neg<Output = B>
1695            + for<'b> Add<&'b B, Output = B>
1696            + for<'b> AddAssign<&'b B>
1697            + for<'b> Sub<&'b B, Output = B>
1698            + for<'b> SubAssign<&'b B>
1699            + for<'c> Mul<&'c Const, Output = B>
1700            + for<'c> MulAssign<&'c Const>
1701            + Send
1702            + Sync
1703            + 'static
1704            + Serialize
1705            + DeserializeOwned
1706            + SchemaWrite<Src = B>
1707            + for<'de> SchemaRead<'de, Dst = B>
1708    {
1709    }
1710
1711    // ─── Batched Marker Trait: BatchedShareItem ──────────────────────────────
1712    /// Bounds on the **element type `IV`** inside a batched value
1713    /// `HeapArray<IV, M>`. Extends the general per-element requirements.
1714    ///
1715    /// Used exclusively by the batched-share test helpers.
1716    pub(super) trait BatchedShareItem<A, IB>:
1717        Copy
1718        + Clone
1719        + PartialEq
1720        + Debug
1721        + Random
1722        + ConstantTimeEq
1723        + ConditionallySelectable
1724        + for<'a> Mul<&'a A, Output = IB>
1725    {
1726    }
1727
1728    impl<IV, A, IB> BatchedShareItem<A, IB> for IV where
1729        IV: Copy
1730            + Clone
1731            + PartialEq
1732            + Debug
1733            + Random
1734            + ConstantTimeEq
1735            + ConditionallySelectable
1736            + for<'a> Mul<&'a A, Output = IB>
1737    {
1738    }
1739
1740    // ─── Batched Marker Trait: BatchedShareMacItem ───────────────────────────
1741    /// Bounds on the **element MAC type `IB`** inside a batched MAC
1742    /// `HeapArray<IB, M>`. Extends the general per-element requirements.
1743    pub(super) trait BatchedShareMacItem<IV, A>:
1744        Copy + Clone + PartialEq + Debug + Random + ConstantTimeEq + ConditionallySelectable
1745    {
1746    }
1747
1748    impl<IB, IV, A> BatchedShareMacItem<IV, A> for IB where
1749        IB: Copy + Clone + PartialEq + Debug + Random + ConstantTimeEq + ConditionallySelectable
1750    {
1751    }
1752
1753    // ─── Generic Pairwise Test Functions ─────────────────────────────────────
1754
1755    pub(super) fn test_open_to<V, A, B, Const>(n_parties: usize)
1756    where
1757        V: ShareValue<A, B, Const>,
1758        A: ShareKey,
1759        B: ShareMac<V, A, Const>,
1760        Const: Random,
1761    {
1762        let mut rng = test_rng();
1763        let share = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
1764        for i in 0..n_parties - 1 {
1765            let open = share.open_to(i).unwrap();
1766            assert_eq!(open.get_value(), &share.value);
1767            assert_eq!(open.get_mac(), &share.macs[i]);
1768        }
1769    }
1770
1771    pub(super) fn test_random<V, A, B, Const>(n_parties: usize)
1772    where
1773        V: ShareValue<A, B, Const>,
1774        A: ShareKey,
1775        B: ShareMac<V, A, Const>,
1776        Const: Random,
1777    {
1778        let mut rng = test_rng();
1779        let share = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
1780        assert_eq!(share.get_macs().len(), n_parties - 1);
1781        assert_eq!(share.get_keys().len(), n_parties - 1);
1782        let value = V::random(&mut rng);
1783        let share_with_value =
1784            PairwiseAuthShare::<V, A, B>::random_with(&mut rng, (n_parties, value.clone()));
1785        assert_eq!(share_with_value.get_value(), &value);
1786        assert_eq!(share_with_value.get_macs().len(), n_parties - 1);
1787        assert_eq!(share_with_value.get_keys().len(), n_parties - 1);
1788    }
1789
1790    pub(super) fn test_random_vec_and_reconstruct<V, A, B, Const>(n_parties: usize)
1791    where
1792        V: ShareValue<A, B, Const>,
1793        A: ShareKey,
1794        B: ShareMac<V, A, Const>,
1795        Const: Random,
1796    {
1797        let mut rng = test_rng();
1798        let shares: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n(&mut rng, n_parties);
1799        assert_eq!(shares.len(), n_parties);
1800        for share in &shares {
1801            assert_eq!(share.get_macs().len(), n_parties - 1);
1802            assert_eq!(share.get_keys().len(), n_parties - 1);
1803        }
1804        let unauth: Vec<_> = shares.iter().map(|s| s.get_value().clone()).collect();
1805        let expected = V::from_additive_shares(&unauth);
1806        let reconstructed = PairwiseAuthShare::<V, A, B>::reconstruct_all(shares).unwrap();
1807        assert_eq!(reconstructed, expected);
1808
1809        let value = V::random(&mut rng);
1810        let shares: Vec<_> =
1811            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, value.clone());
1812        let reconstructed = PairwiseAuthShare::<V, A, B>::reconstruct_all(shares).unwrap();
1813        assert_eq!(reconstructed, value);
1814    }
1815
1816    pub(super) fn test_random_vec_with_global_key_and_reconstruct<V, A, B, Const>(n_parties: usize)
1817    where
1818        V: ShareValue<A, B, Const>,
1819        A: ShareKey,
1820        B: ShareMac<V, A, Const>,
1821        Const: Random,
1822    {
1823        let mut rng = test_rng();
1824        // n shares, each built from n-1 per-peer global keys
1825        let alphas: Vec<Vec<GlobalKey<A>>> = (0..n_parties)
1826            .map(|_| GlobalKey::<A>::random_n::<Vec<_>>(&mut rng, n_parties - 1))
1827            .collect();
1828        let shares_from_alphas: Vec<_> =
1829            PairwiseAuthShare::<V, A, B>::random_n_with_each(&mut rng, alphas.clone());
1830        assert_eq!(shares_from_alphas.len(), n_parties);
1831        for (share, my_alphas) in izip_eq!(&shares_from_alphas, &alphas) {
1832            assert_eq!(share.get_macs().len(), n_parties - 1);
1833            assert_eq!(share.get_keys().len(), n_parties - 1);
1834            assert_eq!(share.get_alphas().collect::<Vec<_>>(), *my_alphas);
1835        }
1836        let _ = PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_from_alphas).unwrap();
1837
1838        // from a value + per-party alpha lists
1839        let value = V::random(&mut rng);
1840        let alphas: Vec<Vec<GlobalKey<A>>> = (0..n_parties)
1841            .map(|_| GlobalKey::<A>::random_n::<Vec<_>>(&mut rng, n_parties - 1))
1842            .collect();
1843        let shares_from_value_and_alphas: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n_with(
1844            &mut rng,
1845            n_parties,
1846            (value.clone(), alphas.clone()),
1847        );
1848        assert_eq!(shares_from_value_and_alphas.len(), n_parties);
1849        for (share, my_alphas) in izip_eq!(&shares_from_value_and_alphas, &alphas) {
1850            assert_eq!(share.get_macs().len(), n_parties - 1);
1851            assert_eq!(share.get_keys().len(), n_parties - 1);
1852            assert_eq!(share.get_alphas().collect::<Vec<_>>(), *my_alphas);
1853        }
1854        let reconstructed =
1855            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_from_value_and_alphas).unwrap();
1856        assert_eq!(reconstructed, value);
1857
1858        // from additive shares + per-party alpha lists
1859        let value = V::random(&mut rng);
1860        let unauth = value.to_additive_shares(n_parties, &mut rng);
1861        let alphas: Vec<Vec<GlobalKey<A>>> = (0..n_parties)
1862            .map(|_| GlobalKey::<A>::random_n::<Vec<_>>(&mut rng, n_parties - 1))
1863            .collect();
1864        let shares_from_unauth: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n_with_each(
1865            &mut rng,
1866            izip_eq!(unauth, alphas.clone()),
1867        );
1868        assert_eq!(shares_from_unauth.len(), n_parties);
1869        for (share, my_alphas) in izip_eq!(&shares_from_unauth, &alphas) {
1870            assert_eq!(share.get_macs().len(), n_parties - 1);
1871            assert_eq!(share.get_keys().len(), n_parties - 1);
1872            assert_eq!(share.get_alphas().collect::<Vec<_>>(), *my_alphas);
1873        }
1874        let reconstructed =
1875            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_from_unauth).unwrap();
1876        assert_eq!(reconstructed, value);
1877    }
1878
1879    pub(super) fn test_verify_mac<V, A, B, Const>(n_parties: usize)
1880    where
1881        V: ShareValue<A, B, Const>,
1882        A: ShareKey,
1883        B: ShareMac<V, A, Const>,
1884        Const: Random,
1885    {
1886        let mut rng = test_rng();
1887        let shares: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n(&mut rng, n_parties);
1888        for i in 0..shares.len() {
1889            for j in 0..shares.len() {
1890                if i == j {
1891                    continue;
1892                }
1893                let open = shares[j].open_to(i - (i > j) as usize).unwrap();
1894                shares[i].verify_from(open, j - (j > i) as usize).unwrap();
1895            }
1896        }
1897        PairwiseAuthShare::<V, A, B>::verify_all(shares).unwrap();
1898    }
1899
1900    pub(super) fn test_add<V, A, B, Const>(n_parties: usize)
1901    where
1902        V: ShareValue<A, B, Const>,
1903        A: ShareKey,
1904        B: ShareMac<V, A, Const>,
1905        Const: Random,
1906    {
1907        let mut rng = test_rng();
1908        let alphas: Vec<Vec<GlobalKey<A>>> = (0..n_parties)
1909            .map(|_| GlobalKey::<A>::random_n::<Vec<_>>(&mut rng, n_parties - 1))
1910            .collect();
1911        let a = V::random(&mut rng);
1912        let b = V::random(&mut rng);
1913        let shares_a: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n_with(
1914            &mut rng,
1915            n_parties,
1916            (a.clone(), alphas.clone()),
1917        );
1918        let shares_b: Vec<_> =
1919            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, (b.clone(), alphas));
1920        let expected = a.clone() + &b;
1921
1922        let sum: Vec<_> = izip_eq!(&shares_a, &shares_b)
1923            .map(|(sa, sb)| sa + sb)
1924            .collect();
1925        assert_eq!(
1926            PairwiseAuthShare::<V, A, B>::reconstruct_all(sum).unwrap(),
1927            expected
1928        );
1929
1930        let sum: Vec<_> = izip_eq!(&shares_a, shares_b.clone())
1931            .map(|(sa, sb)| sa + sb)
1932            .collect();
1933        assert_eq!(
1934            PairwiseAuthShare::<V, A, B>::reconstruct_all(sum).unwrap(),
1935            expected
1936        );
1937
1938        let sum: Vec<_> = izip_eq!(shares_a.clone(), &shares_b)
1939            .map(|(sa, sb)| sa + sb)
1940            .collect();
1941        assert_eq!(
1942            PairwiseAuthShare::<V, A, B>::reconstruct_all(sum).unwrap(),
1943            expected
1944        );
1945
1946        let sum: Vec<_> = izip_eq!(shares_a.clone(), shares_b.clone())
1947            .map(|(sa, sb)| sa + sb)
1948            .collect();
1949        assert_eq!(
1950            PairwiseAuthShare::<V, A, B>::reconstruct_all(sum).unwrap(),
1951            expected
1952        );
1953
1954        let mut shares_a_mut = shares_a.clone();
1955        izip_eq!(&mut shares_a_mut, &shares_b).for_each(|(sa, sb)| *sa += sb);
1956        assert_eq!(
1957            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_mut).unwrap(),
1958            expected
1959        );
1960
1961        let mut shares_a_mut = shares_a;
1962        izip_eq!(&mut shares_a_mut, shares_b).for_each(|(sa, sb)| *sa += sb);
1963        assert_eq!(
1964            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_mut).unwrap(),
1965            expected
1966        );
1967    }
1968
1969    pub(super) fn test_add_plaintext<V, A, B, Const>(n_parties: usize)
1970    where
1971        V: ShareValue<A, B, Const>,
1972        A: ShareKey,
1973        B: ShareMac<V, A, Const>,
1974        Const: Random,
1975    {
1976        let mut rng = test_rng();
1977        let a = V::random(&mut rng);
1978        let k = V::random(&mut rng);
1979        let shares_a: Vec<_> =
1980            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, a.clone());
1981
1982        let shares_plus_k_ref: Vec<_> = shares_a
1983            .iter()
1984            .enumerate()
1985            .map(|(i, share_a)| share_a.to_owned().add_plaintext(&k, i == 0))
1986            .collect();
1987        assert_eq!(
1988            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_plus_k_ref).unwrap(),
1989            a.clone() + &k,
1990        );
1991
1992        let shares_plus_k: Vec<_> = shares_a
1993            .into_iter()
1994            .enumerate()
1995            .map(|(i, share_a)| share_a.to_owned().add_plaintext(&k, i == 0))
1996            .collect();
1997        assert_eq!(
1998            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_plus_k).unwrap(),
1999            a + &k,
2000        );
2001    }
2002
2003    pub(super) fn test_mul_constant<V, A, B, Const>(n_parties: usize)
2004    where
2005        V: ShareValue<A, B, Const>,
2006        A: ShareKey,
2007        B: ShareMac<V, A, Const>,
2008        Const: Random + PartialEq + Debug,
2009    {
2010        let mut rng = test_rng();
2011        let a = V::random(&mut rng);
2012        let k = Const::random(&mut rng);
2013        let shares_a: Vec<_> =
2014            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, a.clone());
2015
2016        let shares_a_times_k: Vec<_> = izip_eq!(shares_a.clone()).map(|sa| sa * &k).collect();
2017        assert_eq!(
2018            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_times_k).unwrap(),
2019            a.clone() * &k,
2020        );
2021
2022        let mut shares_a_mut = shares_a;
2023        izip_eq!(&mut shares_a_mut).for_each(|sa| *sa *= &k);
2024        assert_eq!(
2025            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_mut).unwrap(),
2026            a * &k,
2027        );
2028    }
2029
2030    pub(super) fn test_sub<V, A, B, Const>(n_parties: usize)
2031    where
2032        V: ShareValue<A, B, Const>,
2033        A: ShareKey,
2034        B: ShareMac<V, A, Const>,
2035        Const: Random,
2036    {
2037        let mut rng = test_rng();
2038        let alphas: Vec<Vec<GlobalKey<A>>> = (0..n_parties)
2039            .map(|_| GlobalKey::<A>::random_n::<Vec<_>>(&mut rng, n_parties - 1))
2040            .collect();
2041        let a = V::random(&mut rng);
2042        let b = V::random(&mut rng);
2043        let shares_a: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n_with(
2044            &mut rng,
2045            n_parties,
2046            (a.clone(), alphas.clone()),
2047        );
2048        let shares_b: Vec<_> =
2049            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, (b.clone(), alphas));
2050        let expected = a.clone() - &b;
2051
2052        let diff: Vec<_> = izip_eq!(&shares_a, &shares_b)
2053            .map(|(sa, sb)| sa - sb)
2054            .collect();
2055        assert_eq!(
2056            PairwiseAuthShare::<V, A, B>::reconstruct_all(diff).unwrap(),
2057            expected
2058        );
2059
2060        let diff: Vec<_> = izip_eq!(&shares_a, shares_b.clone())
2061            .map(|(sa, sb)| sa - sb)
2062            .collect();
2063        assert_eq!(
2064            PairwiseAuthShare::<V, A, B>::reconstruct_all(diff).unwrap(),
2065            expected
2066        );
2067
2068        let diff: Vec<_> = izip_eq!(shares_a.clone(), &shares_b)
2069            .map(|(sa, sb)| sa - sb)
2070            .collect();
2071        assert_eq!(
2072            PairwiseAuthShare::<V, A, B>::reconstruct_all(diff).unwrap(),
2073            expected
2074        );
2075
2076        let diff: Vec<_> = izip_eq!(shares_a.clone(), shares_b.clone())
2077            .map(|(sa, sb)| sa - sb)
2078            .collect();
2079        assert_eq!(
2080            PairwiseAuthShare::<V, A, B>::reconstruct_all(diff).unwrap(),
2081            expected
2082        );
2083
2084        let mut shares_a_mut = shares_a.clone();
2085        izip_eq!(&mut shares_a_mut, &shares_b).for_each(|(sa, sb)| *sa -= sb);
2086        assert_eq!(
2087            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_mut).unwrap(),
2088            expected
2089        );
2090
2091        let mut shares_a_mut = shares_a;
2092        izip_eq!(&mut shares_a_mut, shares_b).for_each(|(sa, sb)| *sa -= sb);
2093        assert_eq!(
2094            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_mut).unwrap(),
2095            expected
2096        );
2097    }
2098
2099    pub(super) fn test_neg<V, A, B, Const>(n_parties: usize)
2100    where
2101        V: ShareValue<A, B, Const>,
2102        A: ShareKey,
2103        B: ShareMac<V, A, Const>,
2104        Const: Random,
2105    {
2106        let mut rng = test_rng();
2107        let a = V::random(&mut rng);
2108        let shares_a: Vec<_> =
2109            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, a.clone());
2110
2111        let neg_ref: Vec<_> = shares_a.iter().map(|sa| -sa).collect();
2112        assert_eq!(
2113            PairwiseAuthShare::<V, A, B>::reconstruct_all(neg_ref).unwrap(),
2114            -a.clone(),
2115        );
2116        let neg: Vec<_> = shares_a.into_iter().map(|sa| -sa).collect();
2117        assert_eq!(
2118            PairwiseAuthShare::<V, A, B>::reconstruct_all(neg).unwrap(),
2119            -a,
2120        );
2121    }
2122
2123    pub(super) fn test_conditional_select<V, A, B, Const>(n_parties: usize)
2124    where
2125        V: ShareValue<A, B, Const>,
2126        A: ShareKey,
2127        B: ShareMac<V, A, Const>,
2128        Const: Random,
2129    {
2130        let mut rng = test_rng();
2131        let shares_a = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
2132        let shares_b = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
2133        let selected = PairwiseAuthShare::<V, A, B>::conditional_select(
2134            &shares_a,
2135            &shares_b,
2136            Choice::from(0u8),
2137        );
2138        assert_eq!(selected, shares_a);
2139        let selected = PairwiseAuthShare::<V, A, B>::conditional_select(
2140            &shares_a,
2141            &shares_b,
2142            Choice::from(1u8),
2143        );
2144        assert_eq!(selected, shares_b);
2145    }
2146
2147    pub(super) fn test_ct_eq<V, A, B, Const>(n_parties: usize)
2148    where
2149        V: ShareValue<A, B, Const>,
2150        A: ShareKey,
2151        B: ShareMac<V, A, Const>,
2152        Const: Random,
2153    {
2154        let mut rng = test_rng();
2155        let shares_a = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
2156        let shares_b = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
2157        assert!(Into::<bool>::into(shares_a.ct_eq(&shares_a.clone())));
2158        assert!(Into::<bool>::into(shares_b.ct_eq(&shares_b.clone())));
2159        assert!(!Into::<bool>::into(shares_a.ct_eq(&shares_b)));
2160        assert!(!Into::<bool>::into(shares_b.ct_eq(&shares_a)));
2161    }
2162
2163    // ─── Generic Batched Test Functions ──────────────────────────────────────
2164
2165    pub(super) fn test_split_halves<IV, A, IB, M, MDiv2>(n_parties: usize)
2166    where
2167        IV: BatchedShareItem<A, IB>,
2168        A: ShareKey,
2169        IB: BatchedShareMacItem<IV, A>,
2170        M: Positive + Div<U2, Output = MDiv2>,
2171        MDiv2: Positive + Mul<U2, Output = M>,
2172    {
2173        let mut rng = test_rng();
2174        let m_div2 = MDiv2::to_usize();
2175        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2176        let (shares1, shares2) = shares.clone().split_halves::<MDiv2>();
2177
2178        assert_eq!(shares1.get_value().len(), m_div2);
2179        assert_eq!(shares2.get_value().len(), m_div2);
2180        assert_eq!(shares1.get_value().as_ref(), &shares.get_value()[..m_div2]);
2181        assert_eq!(shares2.get_value().as_ref(), &shares.get_value()[m_div2..]);
2182
2183        izip_eq!(&shares1.macs, &shares2.macs, &shares.macs).for_each(|(mac1, mac2, mac)| {
2184            assert_eq!(mac1.as_ref(), &mac[..m_div2]);
2185            assert_eq!(mac2.as_ref(), &mac[m_div2..]);
2186        });
2187        izip_eq!(&shares1.keys, &shares2.keys, &shares.keys).for_each(|(key1, key2, key)| {
2188            assert_eq!(key1.alpha, key.alpha);
2189            assert_eq!(key2.alpha, key.alpha);
2190            assert_eq!(key1.beta.as_ref(), &key.beta[..m_div2]);
2191            assert_eq!(key2.beta.as_ref(), &key.beta[m_div2..]);
2192        });
2193
2194        let merged = BatchedShare::<IV, A, IB, MDiv2>::merge_halves(shares1, shares2);
2195        assert_eq!(merged, shares);
2196    }
2197
2198    pub(super) fn test_split_thirds<IV, A, IB, M, MDiv3>(n_parties: usize)
2199    where
2200        IV: BatchedShareItem<A, IB>,
2201        A: ShareKey,
2202        IB: BatchedShareMacItem<IV, A>,
2203        M: Positive,
2204        MDiv3: Positive + Mul<U3, Output = M>,
2205        M: Mul<U3, Output: Positive>,
2206    {
2207        let mut rng = test_rng();
2208        let m_div3 = MDiv3::to_usize();
2209        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2210        let (shares1, shares2, shares3) = shares.clone().split_thirds::<MDiv3>();
2211
2212        assert_eq!(shares1.get_value().len(), m_div3);
2213        assert_eq!(shares2.get_value().len(), m_div3);
2214        assert_eq!(shares3.get_value().len(), m_div3);
2215        assert_eq!(shares1.get_value().as_ref(), &shares.get_value()[..m_div3]);
2216        assert_eq!(
2217            shares2.get_value().as_ref(),
2218            &shares.get_value()[m_div3..(2 * m_div3)]
2219        );
2220        assert_eq!(
2221            shares3.get_value().as_ref(),
2222            &shares.get_value()[(2 * m_div3)..]
2223        );
2224
2225        izip_eq!(&shares1.macs, &shares2.macs, &shares3.macs, &shares.macs).for_each(
2226            |(mac1, mac2, mac3, mac)| {
2227                assert_eq!(mac1.as_ref(), &mac[..m_div3]);
2228                assert_eq!(mac2.as_ref(), &mac[m_div3..(2 * m_div3)]);
2229                assert_eq!(mac3.as_ref(), &mac[(2 * m_div3)..(3 * m_div3)]);
2230            },
2231        );
2232        izip_eq!(&shares1.keys, &shares2.keys, &shares3.keys, &shares.keys).for_each(
2233            |(key1, key2, key3, key)| {
2234                assert_eq!(key1.alpha, key.alpha);
2235                assert_eq!(key2.alpha, key.alpha);
2236                assert_eq!(key3.alpha, key.alpha);
2237                assert_eq!(key1.beta.as_ref(), &key.beta[..m_div3]);
2238                assert_eq!(key2.beta.as_ref(), &key.beta[m_div3..(2 * m_div3)]);
2239                assert_eq!(key3.beta.as_ref(), &key.beta[(2 * m_div3)..(3 * m_div3)]);
2240            },
2241        );
2242
2243        let merged = BatchedShare::<IV, A, IB, MDiv3>::merge_thirds(shares1, shares2, shares3);
2244        assert_eq!(merged, shares);
2245    }
2246
2247    pub(super) fn test_into_iter<IV, A, IB, M>(n_parties: usize)
2248    where
2249        IV: BatchedShareItem<A, IB>,
2250        A: ShareKey,
2251        IB: BatchedShareMacItem<IV, A>,
2252        M: Positive,
2253    {
2254        let mut rng = test_rng();
2255        let m = M::to_usize();
2256        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2257        let mut iter = shares.clone().into_iter();
2258        assert_eq!(iter.len(), m);
2259        for i in 0..m {
2260            assert_eq!(iter.len(), m - i);
2261            let share = iter.next().unwrap();
2262            assert_eq!(share.value, shares.get_value()[i]);
2263            for j in 0..n_parties - 1 {
2264                assert_eq!(share.macs[j], shares.macs[j][i]);
2265                assert_eq!(share.keys[j].alpha, shares.keys[j].alpha);
2266                assert_eq!(share.keys[j].beta, shares.keys[j].beta[i]);
2267            }
2268        }
2269    }
2270
2271    pub(super) fn test_from_iterator<IV, A, IB, M>(n_parties: usize)
2272    where
2273        IV: BatchedShareItem<A, IB>,
2274        A: ShareKey,
2275        IB: BatchedShareMacItem<IV, A>,
2276        M: Positive,
2277    {
2278        let mut rng = test_rng();
2279        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2280        let collected: Vec<_> = shares.clone().into_iter().collect();
2281        let from_iterator: BatchedShare<IV, A, IB, M> = collected.into_iter().collect();
2282        assert_eq!(shares, from_iterator);
2283    }
2284
2285    pub(super) fn test_from_iterator_unequal_sizes<IV, A, IB, M>(n_parties: usize)
2286    where
2287        IV: BatchedShareItem<A, IB>,
2288        A: ShareKey,
2289        IB: BatchedShareMacItem<IV, A>,
2290        M: Positive,
2291        BatchedShare<IV, A, IB, M>: RandomWith<usize>,
2292    {
2293        let mut rng = test_rng();
2294        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2295        let mut collected: Vec<_> = shares.into_iter().collect();
2296        collected.pop();
2297        let _: BatchedShare<IV, A, IB, M> = collected.into_iter().collect();
2298    }
2299
2300    pub(super) fn test_chunks<IV, A, IB, M, CS>(n_parties: usize)
2301    where
2302        IV: BatchedShareItem<A, IB>,
2303        A: ShareKey,
2304        IB: BatchedShareMacItem<IV, A>,
2305        M: Positive + typenum::PartialDiv<CS>,
2306        CS: Positive,
2307        BatchedShare<IV, A, IB, M>: RandomWith<usize> + Clone + Debug,
2308    {
2309        let mut rng = test_rng();
2310        let m = M::to_usize();
2311        let chunk_size = CS::to_usize();
2312        let n_chunks = m / chunk_size;
2313        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2314        let mut chunks_iter = shares.chunks::<CS>();
2315        assert_eq!(chunks_iter.len(), n_chunks);
2316        for i in 0..n_chunks {
2317            assert_eq!(chunks_iter.len(), n_chunks - i);
2318            let chunk = chunks_iter.next().unwrap();
2319            assert_eq!(chunk.value.len(), chunk_size);
2320            for j in 0..chunk_size {
2321                assert_eq!(chunk.value[j], shares.get_value()[i * chunk_size + j]);
2322            }
2323            for (mac_chunk, macs) in izip_eq!(&chunk.macs, &shares.macs) {
2324                for j in 0..chunk_size {
2325                    assert_eq!(mac_chunk[j], macs[i * chunk_size + j]);
2326                }
2327            }
2328            for (key_chunk, keys) in izip_eq!(&chunk.keys, &shares.keys) {
2329                assert_eq!(key_chunk.alpha, keys.alpha);
2330                for j in 0..chunk_size {
2331                    assert_eq!(key_chunk.beta[j], keys.beta[i * chunk_size + j]);
2332                }
2333            }
2334        }
2335        assert!(chunks_iter.next().is_none());
2336    }
2337
2338    // ─── Concrete Test Sub-modules ────────────────────────────────────────────
2339
2340    mod field_share_tests {
2341        use crate::algebra::{
2342            elliptic_curve::{Curve25519Ristretto as C, ScalarAsExtension, ScalarField},
2343            field::SubfieldElement,
2344        };
2345
2346        // ScalarShare<C> = PairwiseAuthShare<SubfieldElement<F>, FieldElement<F>, FieldElement<F>>
2347        // where F = ScalarField<C>.
2348        type V = SubfieldElement<ScalarField<C>>;
2349        type A = ScalarAsExtension<C>; // = FieldElement<ScalarField<C>>
2350        type B = ScalarAsExtension<C>;
2351        type Const = SubfieldElement<ScalarField<C>>;
2352        const N: usize = 3;
2353
2354        #[test]
2355        fn test_open_to() {
2356            super::test_open_to::<V, A, B, Const>(N);
2357        }
2358        #[test]
2359        fn test_random() {
2360            super::test_random::<V, A, B, Const>(N);
2361        }
2362        #[test]
2363        fn test_random_vec_and_reconstruct() {
2364            super::test_random_vec_and_reconstruct::<V, A, B, Const>(N);
2365        }
2366        #[test]
2367        fn test_random_vec_with_global_key_and_reconstruct() {
2368            super::test_random_vec_with_global_key_and_reconstruct::<V, A, B, Const>(N);
2369        }
2370        #[test]
2371        fn test_verify_mac() {
2372            super::test_verify_mac::<V, A, B, Const>(N);
2373        }
2374        #[test]
2375        fn test_add() {
2376            super::test_add::<V, A, B, Const>(N);
2377        }
2378        #[test]
2379        fn test_add_plaintext() {
2380            super::test_add_plaintext::<V, A, B, Const>(N);
2381        }
2382        #[test]
2383        fn test_mul_constant() {
2384            super::test_mul_constant::<V, A, B, Const>(N);
2385        }
2386        #[test]
2387        fn test_sub() {
2388            super::test_sub::<V, A, B, Const>(N);
2389        }
2390        #[test]
2391        fn test_neg() {
2392            super::test_neg::<V, A, B, Const>(N);
2393        }
2394        #[test]
2395        fn test_conditional_select() {
2396            super::test_conditional_select::<V, A, B, Const>(N);
2397        }
2398        #[test]
2399        fn test_ct_eq() {
2400            super::test_ct_eq::<V, A, B, Const>(N);
2401        }
2402    }
2403
2404    mod point_share_tests {
2405        use crate::algebra::{
2406            elliptic_curve::{Curve25519Ristretto as C, Point, ScalarAsExtension, ScalarField},
2407            field::SubfieldElement,
2408        };
2409
2410        // PointShare<C> = PairwiseAuthShare<Point<C>, ScalarAsExtension<C>, Point<C>>
2411        type V = Point<C>;
2412        type A = ScalarAsExtension<C>;
2413        type B = Point<C>;
2414        type Const = SubfieldElement<ScalarField<C>>; // = Scalar<C>
2415        const N: usize = 3;
2416
2417        #[test]
2418        fn test_open_to() {
2419            super::test_open_to::<V, A, B, Const>(N);
2420        }
2421        #[test]
2422        fn test_random() {
2423            super::test_random::<V, A, B, Const>(N);
2424        }
2425        #[test]
2426        fn test_random_vec_and_reconstruct() {
2427            super::test_random_vec_and_reconstruct::<V, A, B, Const>(N);
2428        }
2429        #[test]
2430        fn test_random_vec_with_global_key_and_reconstruct() {
2431            super::test_random_vec_with_global_key_and_reconstruct::<V, A, B, Const>(N);
2432        }
2433        #[test]
2434        fn test_verify_mac() {
2435            super::test_verify_mac::<V, A, B, Const>(N);
2436        }
2437        #[test]
2438        fn test_add() {
2439            super::test_add::<V, A, B, Const>(N);
2440        }
2441        #[test]
2442        fn test_add_plaintext() {
2443            super::test_add_plaintext::<V, A, B, Const>(N);
2444        }
2445        #[test]
2446        fn test_mul_constant() {
2447            super::test_mul_constant::<V, A, B, Const>(N);
2448        }
2449        #[test]
2450        fn test_sub() {
2451            super::test_sub::<V, A, B, Const>(N);
2452        }
2453        #[test]
2454        fn test_neg() {
2455            super::test_neg::<V, A, B, Const>(N);
2456        }
2457        #[test]
2458        fn test_conditional_select() {
2459            super::test_conditional_select::<V, A, B, Const>(N);
2460        }
2461        #[test]
2462        fn test_ct_eq() {
2463            super::test_ct_eq::<V, A, B, Const>(N);
2464        }
2465    }
2466
2467    mod field_shares_tests {
2468        use std::ops::Div;
2469
2470        use typenum::{U12, U2, U3, U4};
2471
2472        use crate::{
2473            algebra::{
2474                elliptic_curve::{Curve25519Ristretto as C, ScalarAsExtension, ScalarField},
2475                field::SubfieldElement,
2476            },
2477            types::heap_array::{FieldElements, SubfieldElements},
2478        };
2479
2480        // ScalarShares<C, M> = PairwiseAuthShare<SubfieldElements<F, M>, FieldElement<F>,
2481        // FieldElements<F, M>> where F = ScalarField<C>.
2482        type F = ScalarField<C>;
2483        type M = U12;
2484        type IV = SubfieldElement<F>;
2485        type A = ScalarAsExtension<C>; // = FieldElement<F>
2486        type IB = ScalarAsExtension<C>; // = FieldElement<F>
2487        type V = SubfieldElements<F, M>;
2488        type B = FieldElements<F, M>;
2489        type Const = V; // SubfieldElements — same as V
2490        const N: usize = 3;
2491
2492        // ── General (pairwise) tests ─────────────────────────────────
2493
2494        #[test]
2495        fn test_open_to() {
2496            super::test_open_to::<V, A, B, Const>(N);
2497        }
2498        #[test]
2499        fn test_random() {
2500            super::test_random::<V, A, B, Const>(N);
2501        }
2502        #[test]
2503        fn test_random_vec_and_reconstruct() {
2504            super::test_random_vec_and_reconstruct::<V, A, B, Const>(N);
2505        }
2506        #[test]
2507        fn test_random_vec_with_global_key_and_reconstruct() {
2508            super::test_random_vec_with_global_key_and_reconstruct::<V, A, B, Const>(N);
2509        }
2510        #[test]
2511        fn test_verify_mac() {
2512            super::test_verify_mac::<V, A, B, Const>(N);
2513        }
2514        #[test]
2515        fn test_add() {
2516            super::test_add::<V, A, B, Const>(N);
2517        }
2518        #[test]
2519        fn test_add_plaintext() {
2520            super::test_add_plaintext::<V, A, B, Const>(N);
2521        }
2522        #[test]
2523        fn test_mul_constant() {
2524            super::test_mul_constant::<V, A, B, Const>(N);
2525        }
2526        #[test]
2527        fn test_sub() {
2528            super::test_sub::<V, A, B, Const>(N);
2529        }
2530        #[test]
2531        fn test_neg() {
2532            super::test_neg::<V, A, B, Const>(N);
2533        }
2534        #[test]
2535        fn test_conditional_select() {
2536            super::test_conditional_select::<V, A, B, Const>(N);
2537        }
2538        #[test]
2539        fn test_ct_eq() {
2540            super::test_ct_eq::<V, A, B, Const>(N);
2541        }
2542
2543        // ── Batched-specific tests ───────────────────────────────────
2544
2545        type MDiv2 = <U12 as Div<U2>>::Output;
2546        type MDiv3 = <U12 as Div<U3>>::Output;
2547
2548        #[test]
2549        fn test_split_halves() {
2550            super::test_split_halves::<IV, A, IB, M, MDiv2>(N);
2551        }
2552        #[test]
2553        fn test_split_thirds() {
2554            super::test_split_thirds::<IV, A, IB, M, MDiv3>(N);
2555        }
2556        #[test]
2557        fn test_into_iter() {
2558            super::test_into_iter::<IV, A, IB, M>(N);
2559        }
2560        #[test]
2561        fn test_from_iterator() {
2562            super::test_from_iterator::<IV, A, IB, M>(N);
2563        }
2564        #[test]
2565        #[should_panic]
2566        fn test_from_iterator_unequal_sizes() {
2567            super::test_from_iterator_unequal_sizes::<IV, A, IB, M>(N);
2568        }
2569        #[test]
2570        fn test_chunks() {
2571            super::test_chunks::<IV, A, IB, M, U4>(N);
2572        }
2573    }
2574
2575    mod point_shares_tests {
2576        use std::ops::Div;
2577
2578        use typenum::{U12, U2, U3, U4};
2579
2580        use crate::{
2581            algebra::elliptic_curve::{Curve25519Ristretto as C, Point, ScalarAsExtension},
2582            types::heap_array::{CurvePoints, Scalars},
2583        };
2584
2585        // PointShares<C, M> = PairwiseAuthShare<CurvePoints<C, M>, ScalarAsExtension<C>,
2586        // CurvePoints<C, M>>
2587        type M = U12;
2588        type IV = Point<C>;
2589        type A = ScalarAsExtension<C>;
2590        type IB = Point<C>;
2591        type V = CurvePoints<C, M>;
2592        type B = CurvePoints<C, M>;
2593        type Const = Scalars<C, M>;
2594        const N: usize = 3;
2595
2596        // ── General (pairwise) tests ─────────────────────────────────
2597
2598        #[test]
2599        fn test_open_to() {
2600            super::test_open_to::<V, A, B, Const>(N);
2601        }
2602        #[test]
2603        fn test_random() {
2604            super::test_random::<V, A, B, Const>(N);
2605        }
2606        #[test]
2607        fn test_random_vec_and_reconstruct() {
2608            super::test_random_vec_and_reconstruct::<V, A, B, Const>(N);
2609        }
2610        #[test]
2611        fn test_random_vec_with_global_key_and_reconstruct() {
2612            super::test_random_vec_with_global_key_and_reconstruct::<V, A, B, Const>(N);
2613        }
2614        #[test]
2615        fn test_verify_mac() {
2616            super::test_verify_mac::<V, A, B, Const>(N);
2617        }
2618        #[test]
2619        fn test_add() {
2620            super::test_add::<V, A, B, Const>(N);
2621        }
2622        #[test]
2623        fn test_add_plaintext() {
2624            super::test_add_plaintext::<V, A, B, Const>(N);
2625        }
2626        #[test]
2627        fn test_mul_constant() {
2628            super::test_mul_constant::<V, A, B, Const>(N);
2629        }
2630        #[test]
2631        fn test_sub() {
2632            super::test_sub::<V, A, B, Const>(N);
2633        }
2634        #[test]
2635        fn test_neg() {
2636            super::test_neg::<V, A, B, Const>(N);
2637        }
2638        #[test]
2639        fn test_conditional_select() {
2640            super::test_conditional_select::<V, A, B, Const>(N);
2641        }
2642        #[test]
2643        fn test_ct_eq() {
2644            super::test_ct_eq::<V, A, B, Const>(N);
2645        }
2646
2647        // ── Batched-specific tests ───────────────────────────────────
2648
2649        type MDiv2 = <U12 as Div<U2>>::Output;
2650        type MDiv3 = <U12 as Div<U3>>::Output;
2651
2652        #[test]
2653        fn test_split_halves() {
2654            super::test_split_halves::<IV, A, IB, M, MDiv2>(N);
2655        }
2656        #[test]
2657        fn test_split_thirds() {
2658            super::test_split_thirds::<IV, A, IB, M, MDiv3>(N);
2659        }
2660        #[test]
2661        fn test_into_iter() {
2662            super::test_into_iter::<IV, A, IB, M>(N);
2663        }
2664        #[test]
2665        fn test_from_iterator() {
2666            super::test_from_iterator::<IV, A, IB, M>(N);
2667        }
2668        #[test]
2669        #[should_panic]
2670        fn test_from_iterator_unequal_sizes() {
2671            super::test_from_iterator_unequal_sizes::<IV, A, IB, M>(N);
2672        }
2673        #[test]
2674        fn test_chunks() {
2675            super::test_chunks::<IV, A, IB, M, U4>(N);
2676        }
2677    }
2678}