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};
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    /// Merges three batched shares of sizes M/3 into a larger batched share of size M.
1312    pub fn merge_thirds(
1313        first: Self,
1314        second: Self,
1315        third: Self,
1316    ) -> BatchedShare<V, A, B, Prod<M, U3>>
1317    where
1318        M: Mul<U3, Output: Positive>,
1319        A: PartialEq,
1320    {
1321        let PairwiseAuthShare {
1322            value: v1,
1323            macs: m1,
1324            keys: k1,
1325        } = first;
1326        let PairwiseAuthShare {
1327            value: v2,
1328            macs: m2,
1329            keys: k2,
1330        } = second;
1331        let PairwiseAuthShare {
1332            value: v3,
1333            macs: m3,
1334            keys: k3,
1335        } = third;
1336        let value = HeapArray::merge_thirds(v1, v2, v3);
1337        let macs: Box<[_]> = izip_eq!(m1, m2, m3)
1338            .map(|(a, b, c)| HeapArray::merge_thirds(a, b, c))
1339            .collect();
1340        let keys: Box<[_]> = izip_eq!(k1, k2, k3)
1341            .map(|(a, b, c)| PairwiseAuthKey::merge_thirds(a, b, c))
1342            .collect();
1343        PairwiseAuthShare::try_new(value, macs, keys).unwrap()
1344    }
1345
1346    #[allow(clippy::type_complexity)]
1347    /// Creates an iterator that yields fixed-size batched chunks of size `CS`.
1348    pub fn chunks<CS: Positive>(
1349        &self,
1350    ) -> BatchedSharesChunks<
1351        <HeapArray<V, M> as IntoIterator>::IntoIter,
1352        <HeapArray<B, M> as IntoIterator>::IntoIter,
1353        A,
1354        <HeapArray<B, M> as IntoIterator>::IntoIter,
1355        CS,
1356    >
1357    where
1358        M: PartialDiv<CS>,
1359    {
1360        BatchedSharesChunks {
1361            inner: self.into_iter(),
1362            remaining_chunks: M::USIZE / CS::USIZE,
1363            _ds: PhantomData,
1364        }
1365    }
1366
1367    /// Swaps elements at positions `i` and `j` in-place across
1368    /// value, macs, and key betas arrays.
1369    pub fn swap(&mut self, i: usize, j: usize) {
1370        self.value.swap(i, j);
1371        for mac in self.macs.iter_mut() {
1372            mac.swap(i, j);
1373        }
1374        for key in self.keys.iter_mut() {
1375            key.beta.swap(i, j);
1376        }
1377    }
1378}
1379
1380// ---------------------------------------------------------------
1381// |  BatchedSharesChunks — chunk-level iterator               |
1382// ---------------------------------------------------------------
1383
1384/// An iterator over fixed-size chunks of a batched [`PairwiseAuthShare`].
1385pub struct BatchedSharesChunks<VIter: Iterator, MacIter: Iterator, A, BIter: Iterator, DS: Positive>
1386{
1387    inner: BatchedSharesIterator<VIter, MacIter, A, BIter>,
1388    remaining_chunks: usize,
1389    _ds: PhantomData<DS>,
1390}
1391
1392impl<VIter: Iterator, MacIter: Iterator, A: Clone, BIter: Iterator, DS: Positive>
1393    BatchedSharesChunks<VIter, MacIter, A, BIter, DS>
1394{
1395    pub fn len(&self) -> usize {
1396        self.remaining_chunks
1397    }
1398
1399    pub fn is_empty(&self) -> bool {
1400        self.remaining_chunks == 0
1401    }
1402}
1403
1404impl<
1405        VIter: Iterator,
1406        MacIter: Iterator,
1407        A: Clone,
1408        BIter: Iterator<Item = MacIter::Item>,
1409        DS: Positive,
1410    > Iterator for BatchedSharesChunks<VIter, MacIter, A, BIter, DS>
1411where
1412    HeapArray<VIter::Item, DS>: FromIterator<VIter::Item>,
1413    HeapArray<MacIter::Item, DS>: FromIterator<MacIter::Item>,
1414{
1415    type Item = BatchedShare<VIter::Item, A, MacIter::Item, DS>;
1416
1417    fn next(&mut self) -> Option<Self::Item> {
1418        if self.remaining_chunks == 0 {
1419            return None;
1420        }
1421        let chunk: Vec<_> = self.inner.by_ref().take(DS::to_usize()).collect();
1422        if chunk.is_empty() {
1423            return None;
1424        }
1425        self.remaining_chunks -= 1;
1426        Some(chunk.into_iter().collect())
1427    }
1428
1429    fn size_hint(&self) -> (usize, Option<usize>) {
1430        (self.remaining_chunks, Some(self.remaining_chunks))
1431    }
1432}
1433
1434impl<
1435        VIter: Iterator,
1436        MacIter: Iterator,
1437        A: Clone,
1438        BIter: Iterator<Item = MacIter::Item>,
1439        DS: Positive,
1440    > ExactSizeIterator for BatchedSharesChunks<VIter, MacIter, A, BIter, DS>
1441where
1442    HeapArray<VIter::Item, DS>: FromIterator<VIter::Item>,
1443    HeapArray<MacIter::Item, DS>: FromIterator<MacIter::Item>,
1444{
1445    fn len(&self) -> usize {
1446        self.remaining_chunks
1447    }
1448}
1449
1450// ------------------------------------------------------------------
1451// |  Reconstructible for HeapArray<Share, N>                       |
1452// ------------------------------------------------------------------
1453
1454/// Reconstruction protocol for a length-`N` array of [`PairwiseAuthShare`]s.
1455/// Each element is independently reconstructed and the secrets are collected.
1456impl<V, A, B, N: Positive> Reconstructible for HeapArray<PairwiseAuthShare<V, A, B>, N>
1457where
1458    PairwiseAuthShare<V, A, B>: Reconstructible,
1459    <PairwiseAuthShare<V, A, B> as Reconstructible>::Opening: Clone,
1460{
1461    type Opening = HeapArray<<PairwiseAuthShare<V, A, B> as Reconstructible>::Opening, N>;
1462    type Value = HeapArray<<PairwiseAuthShare<V, A, B> as Reconstructible>::Value, N>;
1463
1464    fn open_to(&self, for_peer: PeerIndex) -> Result<Self::Opening, PrimitiveError> {
1465        self.into_iter()
1466            .map(|s| s.open_to(for_peer))
1467            .collect::<Result<_, _>>()
1468    }
1469
1470    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening> {
1471        let per_share: Vec<Vec<<PairwiseAuthShare<V, A, B> as Reconstructible>::Opening>> = self
1472            .into_iter()
1473            .map(|s| s.open_to_all_others().collect())
1474            .collect();
1475        transpose(per_share)
1476            .into_iter()
1477            .map(|col| col.try_into().expect("size mismatch in open_to_all_others"))
1478    }
1479
1480    fn reconstruct(&self, openings: Vec<Self::Opening>) -> Result<Self::Value, PrimitiveError> {
1481        self.iter()
1482            .enumerate()
1483            .map(|(i, share)| {
1484                let my_openings: Vec<_> = openings
1485                    .iter()
1486                    .map(|o| o.get(i).cloned())
1487                    .collect::<Option<_>>()
1488                    .ok_or_else(|| {
1489                        PrimitiveError::InvalidParameters(
1490                            "Opening is missing for some share.".to_string(),
1491                        )
1492                    })?;
1493                share.reconstruct(my_openings)
1494            })
1495            .collect::<Result<_, _>>()
1496    }
1497}
1498
1499// --- Type conversions --- //
1500
1501impl<C: Curve> From<ScalarShare<C>> for PointShare<C> {
1502    #[inline]
1503    fn from(scalar_share: ScalarShare<C>) -> Self {
1504        scalar_share * &Point::<C>::generator()
1505    }
1506}
1507
1508impl<C: Curve, M: Positive> From<ScalarShares<C, M>> for PointShares<C, M> {
1509    #[inline]
1510    fn from(scalar_shares: ScalarShares<C, M>) -> Self {
1511        scalar_shares * &Point::<C>::generator()
1512    }
1513}
1514
1515#[cfg(test)]
1516mod tests {
1517    use std::{fmt::Debug, ops::Div};
1518
1519    use typenum::{U2, U3};
1520
1521    use super::*;
1522    use crate::{random::test_rng, sharing::Verifiable};
1523
1524    // ─── Marker Trait: ShareValue ────────────────────────────────────────────
1525    /// Bundle of all bounds required on the **value type `V`** by the test suite.
1526    ///
1527    /// Blanket-implemented for every type satisfying the constituent bounds, so
1528    /// callers only need to write `V: ShareValue<A, B, Const>`.
1529    pub(super) trait ShareValue<A, B, Const>:
1530        Clone
1531        + PartialEq
1532        + Debug
1533        + Random
1534        + AdditiveShares
1535        + ConstantTimeEq
1536        + ConditionallySelectable
1537        + Neg<Output = Self>
1538        + for<'a> Sub<&'a Self, Output = Self>
1539        + for<'a> AddAssign<&'a Self>
1540        + for<'a> Mul<&'a A, Output = B>
1541        + for<'a> Mul<&'a Const, Output = Self>
1542        + for<'a> MulAssign<&'a Const>
1543        + Send
1544        + Sync
1545        + 'static
1546        + Serialize
1547        + DeserializeOwned
1548        + SchemaWrite<Src = Self>
1549        + for<'de> SchemaRead<'de, Dst = Self>
1550    {
1551    }
1552
1553    impl<V, A, B, Const> ShareValue<A, B, Const> for V where
1554        V: Clone
1555            + PartialEq
1556            + Debug
1557            + Random
1558            + AdditiveShares
1559            + ConstantTimeEq
1560            + ConditionallySelectable
1561            + Neg<Output = V>
1562            + for<'a> Sub<&'a V, Output = V>
1563            + for<'a> AddAssign<&'a V>
1564            + for<'a> Mul<&'a A, Output = B>
1565            + for<'a> Mul<&'a Const, Output = V>
1566            + for<'a> MulAssign<&'a Const>
1567            + Send
1568            + Sync
1569            + 'static
1570            + Serialize
1571            + DeserializeOwned
1572            + SchemaWrite<Src = V>
1573            + for<'de> SchemaRead<'de, Dst = V>
1574    {
1575    }
1576
1577    // ─── Marker Trait: ShareKey ──────────────────────────────────────────────
1578    /// Bundle of all bounds required on the **global-key inner type `A`** by the
1579    /// test suite.
1580    pub(super) trait ShareKey:
1581        Clone + PartialEq + Debug + Random + ConstantTimeEq + ConditionallySelectable
1582    {
1583    }
1584
1585    impl<A> ShareKey for A where
1586        A: Clone + PartialEq + Debug + Random + ConstantTimeEq + ConditionallySelectable
1587    {
1588    }
1589
1590    // ─── Marker Trait: ShareMac ──────────────────────────────────────────────
1591    /// Bundle of all bounds required on the **MAC / local-key type `B`** by the
1592    /// test suite.
1593    pub(super) trait ShareMac<V, A, Const>:
1594        Clone
1595        + PartialEq
1596        + Debug
1597        + Random
1598        + ConstantTimeEq
1599        + ConditionallySelectable
1600        + SubAssign
1601        + Neg<Output = Self>
1602        + for<'b> Add<&'b Self, Output = Self>
1603        + for<'b> AddAssign<&'b Self>
1604        + for<'b> Sub<&'b Self, Output = Self>
1605        + for<'b> SubAssign<&'b Self>
1606        + for<'c> Mul<&'c Const, Output = Self>
1607        + for<'c> MulAssign<&'c Const>
1608        + Send
1609        + Sync
1610        + 'static
1611        + Serialize
1612        + DeserializeOwned
1613        + SchemaWrite<Src = Self>
1614        + for<'de> SchemaRead<'de, Dst = Self>
1615    {
1616    }
1617
1618    impl<B, V, A, Const> ShareMac<V, A, Const> for B where
1619        B: Clone
1620            + PartialEq
1621            + Debug
1622            + Random
1623            + ConstantTimeEq
1624            + ConditionallySelectable
1625            + SubAssign
1626            + Neg<Output = B>
1627            + for<'b> Add<&'b B, Output = B>
1628            + for<'b> AddAssign<&'b B>
1629            + for<'b> Sub<&'b B, Output = B>
1630            + for<'b> SubAssign<&'b B>
1631            + for<'c> Mul<&'c Const, Output = B>
1632            + for<'c> MulAssign<&'c Const>
1633            + Send
1634            + Sync
1635            + 'static
1636            + Serialize
1637            + DeserializeOwned
1638            + SchemaWrite<Src = B>
1639            + for<'de> SchemaRead<'de, Dst = B>
1640    {
1641    }
1642
1643    // ─── Batched Marker Trait: BatchedShareItem ──────────────────────────────
1644    /// Bounds on the **element type `IV`** inside a batched value
1645    /// `HeapArray<IV, M>`. Extends the general per-element requirements.
1646    ///
1647    /// Used exclusively by the batched-share test helpers.
1648    pub(super) trait BatchedShareItem<A, IB>:
1649        Copy
1650        + Clone
1651        + PartialEq
1652        + Debug
1653        + Random
1654        + ConstantTimeEq
1655        + ConditionallySelectable
1656        + for<'a> Mul<&'a A, Output = IB>
1657    {
1658    }
1659
1660    impl<IV, A, IB> BatchedShareItem<A, IB> for IV where
1661        IV: Copy
1662            + Clone
1663            + PartialEq
1664            + Debug
1665            + Random
1666            + ConstantTimeEq
1667            + ConditionallySelectable
1668            + for<'a> Mul<&'a A, Output = IB>
1669    {
1670    }
1671
1672    // ─── Batched Marker Trait: BatchedShareMacItem ───────────────────────────
1673    /// Bounds on the **element MAC type `IB`** inside a batched MAC
1674    /// `HeapArray<IB, M>`. Extends the general per-element requirements.
1675    pub(super) trait BatchedShareMacItem<IV, A>:
1676        Copy + Clone + PartialEq + Debug + Random + ConstantTimeEq + ConditionallySelectable
1677    {
1678    }
1679
1680    impl<IB, IV, A> BatchedShareMacItem<IV, A> for IB where
1681        IB: Copy + Clone + PartialEq + Debug + Random + ConstantTimeEq + ConditionallySelectable
1682    {
1683    }
1684
1685    // ─── Generic Pairwise Test Functions ─────────────────────────────────────
1686
1687    pub(super) fn test_open_to<V, A, B, Const>(n_parties: usize)
1688    where
1689        V: ShareValue<A, B, Const>,
1690        A: ShareKey,
1691        B: ShareMac<V, A, Const>,
1692        Const: Random,
1693    {
1694        let mut rng = test_rng();
1695        let share = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
1696        for i in 0..n_parties - 1 {
1697            let open = share.open_to(i).unwrap();
1698            assert_eq!(open.get_value(), &share.value);
1699            assert_eq!(open.get_mac(), &share.macs[i]);
1700        }
1701    }
1702
1703    pub(super) fn test_random<V, A, B, Const>(n_parties: usize)
1704    where
1705        V: ShareValue<A, B, Const>,
1706        A: ShareKey,
1707        B: ShareMac<V, A, Const>,
1708        Const: Random,
1709    {
1710        let mut rng = test_rng();
1711        let share = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
1712        assert_eq!(share.get_macs().len(), n_parties - 1);
1713        assert_eq!(share.get_keys().len(), n_parties - 1);
1714        let value = V::random(&mut rng);
1715        let share_with_value =
1716            PairwiseAuthShare::<V, A, B>::random_with(&mut rng, (n_parties, value.clone()));
1717        assert_eq!(share_with_value.get_value(), &value);
1718        assert_eq!(share_with_value.get_macs().len(), n_parties - 1);
1719        assert_eq!(share_with_value.get_keys().len(), n_parties - 1);
1720    }
1721
1722    pub(super) fn test_random_vec_and_reconstruct<V, A, B, Const>(n_parties: usize)
1723    where
1724        V: ShareValue<A, B, Const>,
1725        A: ShareKey,
1726        B: ShareMac<V, A, Const>,
1727        Const: Random,
1728    {
1729        let mut rng = test_rng();
1730        let shares: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n(&mut rng, n_parties);
1731        assert_eq!(shares.len(), n_parties);
1732        for share in &shares {
1733            assert_eq!(share.get_macs().len(), n_parties - 1);
1734            assert_eq!(share.get_keys().len(), n_parties - 1);
1735        }
1736        let unauth: Vec<_> = shares.iter().map(|s| s.get_value().clone()).collect();
1737        let expected = V::from_additive_shares(&unauth);
1738        let reconstructed = PairwiseAuthShare::<V, A, B>::reconstruct_all(shares).unwrap();
1739        assert_eq!(reconstructed, expected);
1740
1741        let value = V::random(&mut rng);
1742        let shares: Vec<_> =
1743            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, value.clone());
1744        let reconstructed = PairwiseAuthShare::<V, A, B>::reconstruct_all(shares).unwrap();
1745        assert_eq!(reconstructed, value);
1746    }
1747
1748    pub(super) fn test_random_vec_with_global_key_and_reconstruct<V, A, B, Const>(n_parties: usize)
1749    where
1750        V: ShareValue<A, B, Const>,
1751        A: ShareKey,
1752        B: ShareMac<V, A, Const>,
1753        Const: Random,
1754    {
1755        let mut rng = test_rng();
1756        // n shares, each built from n-1 per-peer global keys
1757        let alphas: Vec<Vec<GlobalKey<A>>> = (0..n_parties)
1758            .map(|_| GlobalKey::<A>::random_n::<Vec<_>>(&mut rng, n_parties - 1))
1759            .collect();
1760        let shares_from_alphas: Vec<_> =
1761            PairwiseAuthShare::<V, A, B>::random_n_with_each(&mut rng, alphas.clone());
1762        assert_eq!(shares_from_alphas.len(), n_parties);
1763        for (share, my_alphas) in izip_eq!(&shares_from_alphas, &alphas) {
1764            assert_eq!(share.get_macs().len(), n_parties - 1);
1765            assert_eq!(share.get_keys().len(), n_parties - 1);
1766            assert_eq!(share.get_alphas().collect::<Vec<_>>(), *my_alphas);
1767        }
1768        let _ = PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_from_alphas).unwrap();
1769
1770        // from a value + per-party alpha lists
1771        let value = V::random(&mut rng);
1772        let alphas: Vec<Vec<GlobalKey<A>>> = (0..n_parties)
1773            .map(|_| GlobalKey::<A>::random_n::<Vec<_>>(&mut rng, n_parties - 1))
1774            .collect();
1775        let shares_from_value_and_alphas: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n_with(
1776            &mut rng,
1777            n_parties,
1778            (value.clone(), alphas.clone()),
1779        );
1780        assert_eq!(shares_from_value_and_alphas.len(), n_parties);
1781        for (share, my_alphas) in izip_eq!(&shares_from_value_and_alphas, &alphas) {
1782            assert_eq!(share.get_macs().len(), n_parties - 1);
1783            assert_eq!(share.get_keys().len(), n_parties - 1);
1784            assert_eq!(share.get_alphas().collect::<Vec<_>>(), *my_alphas);
1785        }
1786        let reconstructed =
1787            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_from_value_and_alphas).unwrap();
1788        assert_eq!(reconstructed, value);
1789
1790        // from additive shares + per-party alpha lists
1791        let value = V::random(&mut rng);
1792        let unauth = value.to_additive_shares(n_parties, &mut rng);
1793        let alphas: Vec<Vec<GlobalKey<A>>> = (0..n_parties)
1794            .map(|_| GlobalKey::<A>::random_n::<Vec<_>>(&mut rng, n_parties - 1))
1795            .collect();
1796        let shares_from_unauth: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n_with_each(
1797            &mut rng,
1798            izip_eq!(unauth, alphas.clone()),
1799        );
1800        assert_eq!(shares_from_unauth.len(), n_parties);
1801        for (share, my_alphas) in izip_eq!(&shares_from_unauth, &alphas) {
1802            assert_eq!(share.get_macs().len(), n_parties - 1);
1803            assert_eq!(share.get_keys().len(), n_parties - 1);
1804            assert_eq!(share.get_alphas().collect::<Vec<_>>(), *my_alphas);
1805        }
1806        let reconstructed =
1807            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_from_unauth).unwrap();
1808        assert_eq!(reconstructed, value);
1809    }
1810
1811    pub(super) fn test_verify_mac<V, A, B, Const>(n_parties: usize)
1812    where
1813        V: ShareValue<A, B, Const>,
1814        A: ShareKey,
1815        B: ShareMac<V, A, Const>,
1816        Const: Random,
1817    {
1818        let mut rng = test_rng();
1819        let shares: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n(&mut rng, n_parties);
1820        for i in 0..shares.len() {
1821            for j in 0..shares.len() {
1822                if i == j {
1823                    continue;
1824                }
1825                let open = shares[j].open_to(i - (i > j) as usize).unwrap();
1826                shares[i].verify_from(open, j - (j > i) as usize).unwrap();
1827            }
1828        }
1829        PairwiseAuthShare::<V, A, B>::verify_all(shares).unwrap();
1830    }
1831
1832    pub(super) fn test_add<V, A, B, Const>(n_parties: usize)
1833    where
1834        V: ShareValue<A, B, Const>,
1835        A: ShareKey,
1836        B: ShareMac<V, A, Const>,
1837        Const: Random,
1838    {
1839        let mut rng = test_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 a = V::random(&mut rng);
1844        let b = V::random(&mut rng);
1845        let shares_a: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n_with(
1846            &mut rng,
1847            n_parties,
1848            (a.clone(), alphas.clone()),
1849        );
1850        let shares_b: Vec<_> =
1851            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, (b.clone(), alphas));
1852        let expected = a.clone() + &b;
1853
1854        let sum: Vec<_> = izip_eq!(&shares_a, &shares_b)
1855            .map(|(sa, sb)| sa + sb)
1856            .collect();
1857        assert_eq!(
1858            PairwiseAuthShare::<V, A, B>::reconstruct_all(sum).unwrap(),
1859            expected
1860        );
1861
1862        let sum: Vec<_> = izip_eq!(&shares_a, shares_b.clone())
1863            .map(|(sa, sb)| sa + sb)
1864            .collect();
1865        assert_eq!(
1866            PairwiseAuthShare::<V, A, B>::reconstruct_all(sum).unwrap(),
1867            expected
1868        );
1869
1870        let sum: Vec<_> = izip_eq!(shares_a.clone(), &shares_b)
1871            .map(|(sa, sb)| sa + sb)
1872            .collect();
1873        assert_eq!(
1874            PairwiseAuthShare::<V, A, B>::reconstruct_all(sum).unwrap(),
1875            expected
1876        );
1877
1878        let sum: Vec<_> = izip_eq!(shares_a.clone(), shares_b.clone())
1879            .map(|(sa, sb)| sa + sb)
1880            .collect();
1881        assert_eq!(
1882            PairwiseAuthShare::<V, A, B>::reconstruct_all(sum).unwrap(),
1883            expected
1884        );
1885
1886        let mut shares_a_mut = shares_a.clone();
1887        izip_eq!(&mut shares_a_mut, &shares_b).for_each(|(sa, sb)| *sa += sb);
1888        assert_eq!(
1889            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_mut).unwrap(),
1890            expected
1891        );
1892
1893        let mut shares_a_mut = shares_a;
1894        izip_eq!(&mut shares_a_mut, shares_b).for_each(|(sa, sb)| *sa += sb);
1895        assert_eq!(
1896            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_mut).unwrap(),
1897            expected
1898        );
1899    }
1900
1901    pub(super) fn test_add_plaintext<V, A, B, Const>(n_parties: usize)
1902    where
1903        V: ShareValue<A, B, Const>,
1904        A: ShareKey,
1905        B: ShareMac<V, A, Const>,
1906        Const: Random,
1907    {
1908        let mut rng = test_rng();
1909        let a = V::random(&mut rng);
1910        let k = V::random(&mut rng);
1911        let shares_a: Vec<_> =
1912            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, a.clone());
1913
1914        let shares_plus_k_ref: Vec<_> = shares_a
1915            .iter()
1916            .enumerate()
1917            .map(|(i, share_a)| share_a.to_owned().add_plaintext(&k, i == 0))
1918            .collect();
1919        assert_eq!(
1920            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_plus_k_ref).unwrap(),
1921            a.clone() + &k,
1922        );
1923
1924        let shares_plus_k: Vec<_> = shares_a
1925            .into_iter()
1926            .enumerate()
1927            .map(|(i, share_a)| share_a.to_owned().add_plaintext(&k, i == 0))
1928            .collect();
1929        assert_eq!(
1930            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_plus_k).unwrap(),
1931            a + &k,
1932        );
1933    }
1934
1935    pub(super) fn test_mul_constant<V, A, B, Const>(n_parties: usize)
1936    where
1937        V: ShareValue<A, B, Const>,
1938        A: ShareKey,
1939        B: ShareMac<V, A, Const>,
1940        Const: Random + PartialEq + Debug,
1941    {
1942        let mut rng = test_rng();
1943        let a = V::random(&mut rng);
1944        let k = Const::random(&mut rng);
1945        let shares_a: Vec<_> =
1946            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, a.clone());
1947
1948        let shares_a_times_k: Vec<_> = izip_eq!(shares_a.clone()).map(|sa| sa * &k).collect();
1949        assert_eq!(
1950            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_times_k).unwrap(),
1951            a.clone() * &k,
1952        );
1953
1954        let mut shares_a_mut = shares_a;
1955        izip_eq!(&mut shares_a_mut).for_each(|sa| *sa *= &k);
1956        assert_eq!(
1957            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_mut).unwrap(),
1958            a * &k,
1959        );
1960    }
1961
1962    pub(super) fn test_sub<V, A, B, Const>(n_parties: usize)
1963    where
1964        V: ShareValue<A, B, Const>,
1965        A: ShareKey,
1966        B: ShareMac<V, A, Const>,
1967        Const: Random,
1968    {
1969        let mut rng = test_rng();
1970        let alphas: Vec<Vec<GlobalKey<A>>> = (0..n_parties)
1971            .map(|_| GlobalKey::<A>::random_n::<Vec<_>>(&mut rng, n_parties - 1))
1972            .collect();
1973        let a = V::random(&mut rng);
1974        let b = V::random(&mut rng);
1975        let shares_a: Vec<_> = PairwiseAuthShare::<V, A, B>::random_n_with(
1976            &mut rng,
1977            n_parties,
1978            (a.clone(), alphas.clone()),
1979        );
1980        let shares_b: Vec<_> =
1981            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, (b.clone(), alphas));
1982        let expected = a.clone() - &b;
1983
1984        let diff: Vec<_> = izip_eq!(&shares_a, &shares_b)
1985            .map(|(sa, sb)| sa - sb)
1986            .collect();
1987        assert_eq!(
1988            PairwiseAuthShare::<V, A, B>::reconstruct_all(diff).unwrap(),
1989            expected
1990        );
1991
1992        let diff: Vec<_> = izip_eq!(&shares_a, shares_b.clone())
1993            .map(|(sa, sb)| sa - sb)
1994            .collect();
1995        assert_eq!(
1996            PairwiseAuthShare::<V, A, B>::reconstruct_all(diff).unwrap(),
1997            expected
1998        );
1999
2000        let diff: Vec<_> = izip_eq!(shares_a.clone(), &shares_b)
2001            .map(|(sa, sb)| sa - sb)
2002            .collect();
2003        assert_eq!(
2004            PairwiseAuthShare::<V, A, B>::reconstruct_all(diff).unwrap(),
2005            expected
2006        );
2007
2008        let diff: Vec<_> = izip_eq!(shares_a.clone(), shares_b.clone())
2009            .map(|(sa, sb)| sa - sb)
2010            .collect();
2011        assert_eq!(
2012            PairwiseAuthShare::<V, A, B>::reconstruct_all(diff).unwrap(),
2013            expected
2014        );
2015
2016        let mut shares_a_mut = shares_a.clone();
2017        izip_eq!(&mut shares_a_mut, &shares_b).for_each(|(sa, sb)| *sa -= sb);
2018        assert_eq!(
2019            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_mut).unwrap(),
2020            expected
2021        );
2022
2023        let mut shares_a_mut = shares_a;
2024        izip_eq!(&mut shares_a_mut, shares_b).for_each(|(sa, sb)| *sa -= sb);
2025        assert_eq!(
2026            PairwiseAuthShare::<V, A, B>::reconstruct_all(shares_a_mut).unwrap(),
2027            expected
2028        );
2029    }
2030
2031    pub(super) fn test_neg<V, A, B, Const>(n_parties: usize)
2032    where
2033        V: ShareValue<A, B, Const>,
2034        A: ShareKey,
2035        B: ShareMac<V, A, Const>,
2036        Const: Random,
2037    {
2038        let mut rng = test_rng();
2039        let a = V::random(&mut rng);
2040        let shares_a: Vec<_> =
2041            PairwiseAuthShare::<V, A, B>::random_n_with(&mut rng, n_parties, a.clone());
2042
2043        let neg_ref: Vec<_> = shares_a.iter().map(|sa| -sa).collect();
2044        assert_eq!(
2045            PairwiseAuthShare::<V, A, B>::reconstruct_all(neg_ref).unwrap(),
2046            -a.clone(),
2047        );
2048        let neg: Vec<_> = shares_a.into_iter().map(|sa| -sa).collect();
2049        assert_eq!(
2050            PairwiseAuthShare::<V, A, B>::reconstruct_all(neg).unwrap(),
2051            -a,
2052        );
2053    }
2054
2055    pub(super) fn test_conditional_select<V, A, B, Const>(n_parties: usize)
2056    where
2057        V: ShareValue<A, B, Const>,
2058        A: ShareKey,
2059        B: ShareMac<V, A, Const>,
2060        Const: Random,
2061    {
2062        let mut rng = test_rng();
2063        let shares_a = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
2064        let shares_b = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
2065        let selected = PairwiseAuthShare::<V, A, B>::conditional_select(
2066            &shares_a,
2067            &shares_b,
2068            Choice::from(0u8),
2069        );
2070        assert_eq!(selected, shares_a);
2071        let selected = PairwiseAuthShare::<V, A, B>::conditional_select(
2072            &shares_a,
2073            &shares_b,
2074            Choice::from(1u8),
2075        );
2076        assert_eq!(selected, shares_b);
2077    }
2078
2079    pub(super) fn test_ct_eq<V, A, B, Const>(n_parties: usize)
2080    where
2081        V: ShareValue<A, B, Const>,
2082        A: ShareKey,
2083        B: ShareMac<V, A, Const>,
2084        Const: Random,
2085    {
2086        let mut rng = test_rng();
2087        let shares_a = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
2088        let shares_b = PairwiseAuthShare::<V, A, B>::random_with(&mut rng, n_parties);
2089        assert!(Into::<bool>::into(shares_a.ct_eq(&shares_a.clone())));
2090        assert!(Into::<bool>::into(shares_b.ct_eq(&shares_b.clone())));
2091        assert!(!Into::<bool>::into(shares_a.ct_eq(&shares_b)));
2092        assert!(!Into::<bool>::into(shares_b.ct_eq(&shares_a)));
2093    }
2094
2095    // ─── Generic Batched Test Functions ──────────────────────────────────────
2096
2097    pub(super) fn test_split_halves<IV, A, IB, M, MDiv2>(n_parties: usize)
2098    where
2099        IV: BatchedShareItem<A, IB>,
2100        A: ShareKey,
2101        IB: BatchedShareMacItem<IV, A>,
2102        M: Positive + Div<U2, Output = MDiv2>,
2103        MDiv2: Positive + Mul<U2, Output = M>,
2104    {
2105        let mut rng = test_rng();
2106        let m_div2 = MDiv2::to_usize();
2107        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2108        let (shares1, shares2) = shares.clone().split_halves::<MDiv2>();
2109
2110        assert_eq!(shares1.get_value().len(), m_div2);
2111        assert_eq!(shares2.get_value().len(), m_div2);
2112        assert_eq!(shares1.get_value().as_ref(), &shares.get_value()[..m_div2]);
2113        assert_eq!(shares2.get_value().as_ref(), &shares.get_value()[m_div2..]);
2114
2115        izip_eq!(&shares1.macs, &shares2.macs, &shares.macs).for_each(|(mac1, mac2, mac)| {
2116            assert_eq!(mac1.as_ref(), &mac[..m_div2]);
2117            assert_eq!(mac2.as_ref(), &mac[m_div2..]);
2118        });
2119        izip_eq!(&shares1.keys, &shares2.keys, &shares.keys).for_each(|(key1, key2, key)| {
2120            assert_eq!(key1.alpha, key.alpha);
2121            assert_eq!(key2.alpha, key.alpha);
2122            assert_eq!(key1.beta.as_ref(), &key.beta[..m_div2]);
2123            assert_eq!(key2.beta.as_ref(), &key.beta[m_div2..]);
2124        });
2125
2126        let merged = BatchedShare::<IV, A, IB, MDiv2>::merge_halves(shares1, shares2);
2127        assert_eq!(merged, shares);
2128    }
2129
2130    pub(super) fn test_split_thirds<IV, A, IB, M, MDiv3>(n_parties: usize)
2131    where
2132        IV: BatchedShareItem<A, IB>,
2133        A: ShareKey,
2134        IB: BatchedShareMacItem<IV, A>,
2135        M: Positive,
2136        MDiv3: Positive + Mul<U3, Output = M>,
2137        M: Mul<U3, Output: Positive>,
2138    {
2139        let mut rng = test_rng();
2140        let m_div3 = MDiv3::to_usize();
2141        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2142        let (shares1, shares2, shares3) = shares.clone().split_thirds::<MDiv3>();
2143
2144        assert_eq!(shares1.get_value().len(), m_div3);
2145        assert_eq!(shares2.get_value().len(), m_div3);
2146        assert_eq!(shares3.get_value().len(), m_div3);
2147        assert_eq!(shares1.get_value().as_ref(), &shares.get_value()[..m_div3]);
2148        assert_eq!(
2149            shares2.get_value().as_ref(),
2150            &shares.get_value()[m_div3..(2 * m_div3)]
2151        );
2152        assert_eq!(
2153            shares3.get_value().as_ref(),
2154            &shares.get_value()[(2 * m_div3)..]
2155        );
2156
2157        izip_eq!(&shares1.macs, &shares2.macs, &shares3.macs, &shares.macs).for_each(
2158            |(mac1, mac2, mac3, mac)| {
2159                assert_eq!(mac1.as_ref(), &mac[..m_div3]);
2160                assert_eq!(mac2.as_ref(), &mac[m_div3..(2 * m_div3)]);
2161                assert_eq!(mac3.as_ref(), &mac[(2 * m_div3)..(3 * m_div3)]);
2162            },
2163        );
2164        izip_eq!(&shares1.keys, &shares2.keys, &shares3.keys, &shares.keys).for_each(
2165            |(key1, key2, key3, key)| {
2166                assert_eq!(key1.alpha, key.alpha);
2167                assert_eq!(key2.alpha, key.alpha);
2168                assert_eq!(key3.alpha, key.alpha);
2169                assert_eq!(key1.beta.as_ref(), &key.beta[..m_div3]);
2170                assert_eq!(key2.beta.as_ref(), &key.beta[m_div3..(2 * m_div3)]);
2171                assert_eq!(key3.beta.as_ref(), &key.beta[(2 * m_div3)..(3 * m_div3)]);
2172            },
2173        );
2174
2175        let merged = BatchedShare::<IV, A, IB, MDiv3>::merge_thirds(shares1, shares2, shares3);
2176        assert_eq!(merged, shares);
2177    }
2178
2179    pub(super) fn test_into_iter<IV, A, IB, M>(n_parties: usize)
2180    where
2181        IV: BatchedShareItem<A, IB>,
2182        A: ShareKey,
2183        IB: BatchedShareMacItem<IV, A>,
2184        M: Positive,
2185    {
2186        let mut rng = test_rng();
2187        let m = M::to_usize();
2188        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2189        let mut iter = shares.clone().into_iter();
2190        assert_eq!(iter.len(), m);
2191        for i in 0..m {
2192            assert_eq!(iter.len(), m - i);
2193            let share = iter.next().unwrap();
2194            assert_eq!(share.value, shares.get_value()[i]);
2195            for j in 0..n_parties - 1 {
2196                assert_eq!(share.macs[j], shares.macs[j][i]);
2197                assert_eq!(share.keys[j].alpha, shares.keys[j].alpha);
2198                assert_eq!(share.keys[j].beta, shares.keys[j].beta[i]);
2199            }
2200        }
2201    }
2202
2203    pub(super) fn test_from_iterator<IV, A, IB, M>(n_parties: usize)
2204    where
2205        IV: BatchedShareItem<A, IB>,
2206        A: ShareKey,
2207        IB: BatchedShareMacItem<IV, A>,
2208        M: Positive,
2209    {
2210        let mut rng = test_rng();
2211        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2212        let collected: Vec<_> = shares.clone().into_iter().collect();
2213        let from_iterator: BatchedShare<IV, A, IB, M> = collected.into_iter().collect();
2214        assert_eq!(shares, from_iterator);
2215    }
2216
2217    pub(super) fn test_from_iterator_unequal_sizes<IV, A, IB, M>(n_parties: usize)
2218    where
2219        IV: BatchedShareItem<A, IB>,
2220        A: ShareKey,
2221        IB: BatchedShareMacItem<IV, A>,
2222        M: Positive,
2223        BatchedShare<IV, A, IB, M>: RandomWith<usize>,
2224    {
2225        let mut rng = test_rng();
2226        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2227        let mut collected: Vec<_> = shares.into_iter().collect();
2228        collected.pop();
2229        let _: BatchedShare<IV, A, IB, M> = collected.into_iter().collect();
2230    }
2231
2232    pub(super) fn test_chunks<IV, A, IB, M, CS>(n_parties: usize)
2233    where
2234        IV: BatchedShareItem<A, IB>,
2235        A: ShareKey,
2236        IB: BatchedShareMacItem<IV, A>,
2237        M: Positive + typenum::PartialDiv<CS>,
2238        CS: Positive,
2239        BatchedShare<IV, A, IB, M>: RandomWith<usize> + Clone + Debug,
2240    {
2241        let mut rng = test_rng();
2242        let m = M::to_usize();
2243        let chunk_size = CS::to_usize();
2244        let n_chunks = m / chunk_size;
2245        let shares = BatchedShare::<IV, A, IB, M>::random_with(&mut rng, n_parties);
2246        let mut chunks_iter = shares.chunks::<CS>();
2247        assert_eq!(chunks_iter.len(), n_chunks);
2248        for i in 0..n_chunks {
2249            assert_eq!(chunks_iter.len(), n_chunks - i);
2250            let chunk = chunks_iter.next().unwrap();
2251            assert_eq!(chunk.value.len(), chunk_size);
2252            for j in 0..chunk_size {
2253                assert_eq!(chunk.value[j], shares.get_value()[i * chunk_size + j]);
2254            }
2255            for (mac_chunk, macs) in izip_eq!(&chunk.macs, &shares.macs) {
2256                for j in 0..chunk_size {
2257                    assert_eq!(mac_chunk[j], macs[i * chunk_size + j]);
2258                }
2259            }
2260            for (key_chunk, keys) in izip_eq!(&chunk.keys, &shares.keys) {
2261                assert_eq!(key_chunk.alpha, keys.alpha);
2262                for j in 0..chunk_size {
2263                    assert_eq!(key_chunk.beta[j], keys.beta[i * chunk_size + j]);
2264                }
2265            }
2266        }
2267        assert!(chunks_iter.next().is_none());
2268    }
2269
2270    // ─── Concrete Test Sub-modules ────────────────────────────────────────────
2271
2272    mod field_share_tests {
2273        use crate::algebra::{
2274            elliptic_curve::{Curve25519Ristretto as C, ScalarAsExtension, ScalarField},
2275            field::SubfieldElement,
2276        };
2277
2278        // ScalarShare<C> = PairwiseAuthShare<SubfieldElement<F>, FieldElement<F>, FieldElement<F>>
2279        // where F = ScalarField<C>.
2280        type V = SubfieldElement<ScalarField<C>>;
2281        type A = ScalarAsExtension<C>; // = FieldElement<ScalarField<C>>
2282        type B = ScalarAsExtension<C>;
2283        type Const = SubfieldElement<ScalarField<C>>;
2284        const N: usize = 3;
2285
2286        #[test]
2287        fn test_open_to() {
2288            super::test_open_to::<V, A, B, Const>(N);
2289        }
2290        #[test]
2291        fn test_random() {
2292            super::test_random::<V, A, B, Const>(N);
2293        }
2294        #[test]
2295        fn test_random_vec_and_reconstruct() {
2296            super::test_random_vec_and_reconstruct::<V, A, B, Const>(N);
2297        }
2298        #[test]
2299        fn test_random_vec_with_global_key_and_reconstruct() {
2300            super::test_random_vec_with_global_key_and_reconstruct::<V, A, B, Const>(N);
2301        }
2302        #[test]
2303        fn test_verify_mac() {
2304            super::test_verify_mac::<V, A, B, Const>(N);
2305        }
2306        #[test]
2307        fn test_add() {
2308            super::test_add::<V, A, B, Const>(N);
2309        }
2310        #[test]
2311        fn test_add_plaintext() {
2312            super::test_add_plaintext::<V, A, B, Const>(N);
2313        }
2314        #[test]
2315        fn test_mul_constant() {
2316            super::test_mul_constant::<V, A, B, Const>(N);
2317        }
2318        #[test]
2319        fn test_sub() {
2320            super::test_sub::<V, A, B, Const>(N);
2321        }
2322        #[test]
2323        fn test_neg() {
2324            super::test_neg::<V, A, B, Const>(N);
2325        }
2326        #[test]
2327        fn test_conditional_select() {
2328            super::test_conditional_select::<V, A, B, Const>(N);
2329        }
2330        #[test]
2331        fn test_ct_eq() {
2332            super::test_ct_eq::<V, A, B, Const>(N);
2333        }
2334    }
2335
2336    mod point_share_tests {
2337        use crate::algebra::{
2338            elliptic_curve::{Curve25519Ristretto as C, Point, ScalarAsExtension, ScalarField},
2339            field::SubfieldElement,
2340        };
2341
2342        // PointShare<C> = PairwiseAuthShare<Point<C>, ScalarAsExtension<C>, Point<C>>
2343        type V = Point<C>;
2344        type A = ScalarAsExtension<C>;
2345        type B = Point<C>;
2346        type Const = SubfieldElement<ScalarField<C>>; // = Scalar<C>
2347        const N: usize = 3;
2348
2349        #[test]
2350        fn test_open_to() {
2351            super::test_open_to::<V, A, B, Const>(N);
2352        }
2353        #[test]
2354        fn test_random() {
2355            super::test_random::<V, A, B, Const>(N);
2356        }
2357        #[test]
2358        fn test_random_vec_and_reconstruct() {
2359            super::test_random_vec_and_reconstruct::<V, A, B, Const>(N);
2360        }
2361        #[test]
2362        fn test_random_vec_with_global_key_and_reconstruct() {
2363            super::test_random_vec_with_global_key_and_reconstruct::<V, A, B, Const>(N);
2364        }
2365        #[test]
2366        fn test_verify_mac() {
2367            super::test_verify_mac::<V, A, B, Const>(N);
2368        }
2369        #[test]
2370        fn test_add() {
2371            super::test_add::<V, A, B, Const>(N);
2372        }
2373        #[test]
2374        fn test_add_plaintext() {
2375            super::test_add_plaintext::<V, A, B, Const>(N);
2376        }
2377        #[test]
2378        fn test_mul_constant() {
2379            super::test_mul_constant::<V, A, B, Const>(N);
2380        }
2381        #[test]
2382        fn test_sub() {
2383            super::test_sub::<V, A, B, Const>(N);
2384        }
2385        #[test]
2386        fn test_neg() {
2387            super::test_neg::<V, A, B, Const>(N);
2388        }
2389        #[test]
2390        fn test_conditional_select() {
2391            super::test_conditional_select::<V, A, B, Const>(N);
2392        }
2393        #[test]
2394        fn test_ct_eq() {
2395            super::test_ct_eq::<V, A, B, Const>(N);
2396        }
2397    }
2398
2399    mod field_shares_tests {
2400        use std::ops::Div;
2401
2402        use typenum::{U12, U2, U3, U4};
2403
2404        use crate::{
2405            algebra::{
2406                elliptic_curve::{Curve25519Ristretto as C, ScalarAsExtension, ScalarField},
2407                field::SubfieldElement,
2408            },
2409            types::heap_array::{FieldElements, SubfieldElements},
2410        };
2411
2412        // ScalarShares<C, M> = PairwiseAuthShare<SubfieldElements<F, M>, FieldElement<F>,
2413        // FieldElements<F, M>> where F = ScalarField<C>.
2414        type F = ScalarField<C>;
2415        type M = U12;
2416        type IV = SubfieldElement<F>;
2417        type A = ScalarAsExtension<C>; // = FieldElement<F>
2418        type IB = ScalarAsExtension<C>; // = FieldElement<F>
2419        type V = SubfieldElements<F, M>;
2420        type B = FieldElements<F, M>;
2421        type Const = V; // SubfieldElements — same as V
2422        const N: usize = 3;
2423
2424        // ── General (pairwise) tests ─────────────────────────────────
2425
2426        #[test]
2427        fn test_open_to() {
2428            super::test_open_to::<V, A, B, Const>(N);
2429        }
2430        #[test]
2431        fn test_random() {
2432            super::test_random::<V, A, B, Const>(N);
2433        }
2434        #[test]
2435        fn test_random_vec_and_reconstruct() {
2436            super::test_random_vec_and_reconstruct::<V, A, B, Const>(N);
2437        }
2438        #[test]
2439        fn test_random_vec_with_global_key_and_reconstruct() {
2440            super::test_random_vec_with_global_key_and_reconstruct::<V, A, B, Const>(N);
2441        }
2442        #[test]
2443        fn test_verify_mac() {
2444            super::test_verify_mac::<V, A, B, Const>(N);
2445        }
2446        #[test]
2447        fn test_add() {
2448            super::test_add::<V, A, B, Const>(N);
2449        }
2450        #[test]
2451        fn test_add_plaintext() {
2452            super::test_add_plaintext::<V, A, B, Const>(N);
2453        }
2454        #[test]
2455        fn test_mul_constant() {
2456            super::test_mul_constant::<V, A, B, Const>(N);
2457        }
2458        #[test]
2459        fn test_sub() {
2460            super::test_sub::<V, A, B, Const>(N);
2461        }
2462        #[test]
2463        fn test_neg() {
2464            super::test_neg::<V, A, B, Const>(N);
2465        }
2466        #[test]
2467        fn test_conditional_select() {
2468            super::test_conditional_select::<V, A, B, Const>(N);
2469        }
2470        #[test]
2471        fn test_ct_eq() {
2472            super::test_ct_eq::<V, A, B, Const>(N);
2473        }
2474
2475        // ── Batched-specific tests ───────────────────────────────────
2476
2477        type MDiv2 = <U12 as Div<U2>>::Output;
2478        type MDiv3 = <U12 as Div<U3>>::Output;
2479
2480        #[test]
2481        fn test_split_halves() {
2482            super::test_split_halves::<IV, A, IB, M, MDiv2>(N);
2483        }
2484        #[test]
2485        fn test_split_thirds() {
2486            super::test_split_thirds::<IV, A, IB, M, MDiv3>(N);
2487        }
2488        #[test]
2489        fn test_into_iter() {
2490            super::test_into_iter::<IV, A, IB, M>(N);
2491        }
2492        #[test]
2493        fn test_from_iterator() {
2494            super::test_from_iterator::<IV, A, IB, M>(N);
2495        }
2496        #[test]
2497        #[should_panic]
2498        fn test_from_iterator_unequal_sizes() {
2499            super::test_from_iterator_unequal_sizes::<IV, A, IB, M>(N);
2500        }
2501        #[test]
2502        fn test_chunks() {
2503            super::test_chunks::<IV, A, IB, M, U4>(N);
2504        }
2505    }
2506
2507    mod point_shares_tests {
2508        use std::ops::Div;
2509
2510        use typenum::{U12, U2, U3, U4};
2511
2512        use crate::{
2513            algebra::elliptic_curve::{Curve25519Ristretto as C, Point, ScalarAsExtension},
2514            types::heap_array::{CurvePoints, Scalars},
2515        };
2516
2517        // PointShares<C, M> = PairwiseAuthShare<CurvePoints<C, M>, ScalarAsExtension<C>,
2518        // CurvePoints<C, M>>
2519        type M = U12;
2520        type IV = Point<C>;
2521        type A = ScalarAsExtension<C>;
2522        type IB = Point<C>;
2523        type V = CurvePoints<C, M>;
2524        type B = CurvePoints<C, M>;
2525        type Const = Scalars<C, M>;
2526        const N: usize = 3;
2527
2528        // ── General (pairwise) tests ─────────────────────────────────
2529
2530        #[test]
2531        fn test_open_to() {
2532            super::test_open_to::<V, A, B, Const>(N);
2533        }
2534        #[test]
2535        fn test_random() {
2536            super::test_random::<V, A, B, Const>(N);
2537        }
2538        #[test]
2539        fn test_random_vec_and_reconstruct() {
2540            super::test_random_vec_and_reconstruct::<V, A, B, Const>(N);
2541        }
2542        #[test]
2543        fn test_random_vec_with_global_key_and_reconstruct() {
2544            super::test_random_vec_with_global_key_and_reconstruct::<V, A, B, Const>(N);
2545        }
2546        #[test]
2547        fn test_verify_mac() {
2548            super::test_verify_mac::<V, A, B, Const>(N);
2549        }
2550        #[test]
2551        fn test_add() {
2552            super::test_add::<V, A, B, Const>(N);
2553        }
2554        #[test]
2555        fn test_add_plaintext() {
2556            super::test_add_plaintext::<V, A, B, Const>(N);
2557        }
2558        #[test]
2559        fn test_mul_constant() {
2560            super::test_mul_constant::<V, A, B, Const>(N);
2561        }
2562        #[test]
2563        fn test_sub() {
2564            super::test_sub::<V, A, B, Const>(N);
2565        }
2566        #[test]
2567        fn test_neg() {
2568            super::test_neg::<V, A, B, Const>(N);
2569        }
2570        #[test]
2571        fn test_conditional_select() {
2572            super::test_conditional_select::<V, A, B, Const>(N);
2573        }
2574        #[test]
2575        fn test_ct_eq() {
2576            super::test_ct_eq::<V, A, B, Const>(N);
2577        }
2578
2579        // ── Batched-specific tests ───────────────────────────────────
2580
2581        type MDiv2 = <U12 as Div<U2>>::Output;
2582        type MDiv3 = <U12 as Div<U3>>::Output;
2583
2584        #[test]
2585        fn test_split_halves() {
2586            super::test_split_halves::<IV, A, IB, M, MDiv2>(N);
2587        }
2588        #[test]
2589        fn test_split_thirds() {
2590            super::test_split_thirds::<IV, A, IB, M, MDiv3>(N);
2591        }
2592        #[test]
2593        fn test_into_iter() {
2594            super::test_into_iter::<IV, A, IB, M>(N);
2595        }
2596        #[test]
2597        fn test_from_iterator() {
2598            super::test_from_iterator::<IV, A, IB, M>(N);
2599        }
2600        #[test]
2601        #[should_panic]
2602        fn test_from_iterator_unequal_sizes() {
2603            super::test_from_iterator_unequal_sizes::<IV, A, IB, M>(N);
2604        }
2605        #[test]
2606        fn test_chunks() {
2607            super::test_chunks::<IV, A, IB, M, U4>(N);
2608        }
2609    }
2610}