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