Skip to main content

primitives/sharing/authenticated/pairwise/
share.rs

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