Skip to main content

primitives/sharing/authenticated/pairwise/
share.rs

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