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