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