Skip to main content

primitives/sharing/authenticated/batched/
curve_shares.rs

1use std::{
2    fmt::Debug,
3    ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
4};
5
6use itertools::enumerate;
7use serde::{Deserialize, Serialize};
8use subtle::{Choice, ConstantTimeEq};
9use wincode::{SchemaRead, SchemaWrite};
10
11use super::CurveKeys;
12use crate::{
13    algebra::elliptic_curve::{Curve, Scalar, ScalarAsExtension},
14    errors::PrimitiveError,
15    izip_eq,
16    random::{CryptoRngCore, Random, RandomWith},
17    sharing::{
18        authenticated::NParties,
19        unauthenticated::AdditiveShares,
20        GlobalCurveKey,
21        Reconstructible,
22        VerifiableWith,
23    },
24    types::{
25        heap_array::curve_arrays::{CurvePoints, Scalars, ScalarsAsExtension},
26        CollectAll,
27        ConditionallySelectable,
28        PeerIndex,
29        Positive,
30    },
31    utils::IntoExactSizeIterator,
32};
33#[derive(Clone, Default, PartialEq, Eq, Serialize, Deserialize, SchemaRead, SchemaWrite)]
34#[serde(bound = "C: Curve, M: Positive")]
35#[repr(C)]
36pub struct OpenPointShares<C: Curve, M: Positive> {
37    pub(crate) value: CurvePoints<C, M>,
38    pub(crate) mac: CurvePoints<C, M>,
39}
40
41impl<C: Curve, M: Positive> OpenPointShares<C, M> {
42    pub fn new(value: CurvePoints<C, M>, mac: CurvePoints<C, M>) -> Self {
43        Self { value, mac }
44    }
45
46    pub fn get_value(&self) -> &CurvePoints<C, M> {
47        &self.value
48    }
49
50    pub fn get_mac(&self) -> &CurvePoints<C, M> {
51        &self.mac
52    }
53}
54
55impl<C: Curve, M: Positive> Debug for OpenPointShares<C, M> {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        f.debug_struct(format!("OpenPointShares<{}>", M::USIZE).as_str())
58            .field("value", &self.value)
59            .field("mac", &self.mac)
60            .finish()
61    }
62}
63
64/// A share of a point on a curve, with its associated MACs and the keys for all
65/// other parties' shares.
66#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(bound = "C: Curve")]
68pub struct PointShares<C: Curve, M: Positive> {
69    pub(crate) value: CurvePoints<C, M>,       // X_i
70    pub(crate) macs: Box<[CurvePoints<C, M>]>, // {MAC(x_i)_j} ∀j∈[1..n]∖{i}
71    pub(crate) keys: Box<[CurveKeys<C, M>]>,   // {(α_ij, β_ij)} ∀j∈[1..n]∖{i}
72}
73
74impl<C: Curve, M: Positive> PointShares<C, M> {
75    pub fn try_new(
76        value: CurvePoints<C, M>,
77        macs: Box<[CurvePoints<C, M>]>,
78        keys: Box<[CurveKeys<C, M>]>,
79    ) -> Result<Self, PrimitiveError> {
80        if macs.is_empty() {
81            return Err(PrimitiveError::MinimumLength(2, 0));
82        }
83        if macs.len() != keys.len() {
84            return Err(PrimitiveError::InvalidSize(keys.len(), macs.len()));
85        }
86        /* NOTE: We assume length equality for macs & keys from this point on */
87        Ok(Self { value, macs, keys })
88    }
89
90    fn new(
91        value: CurvePoints<C, M>,
92        macs: Box<[CurvePoints<C, M>]>,
93        keys: Box<[CurveKeys<C, M>]>,
94    ) -> Self {
95        Self { value, macs, keys }
96    }
97
98    pub fn get_value(&self) -> &CurvePoints<C, M> {
99        &self.value
100    }
101
102    pub fn get_keys(&self) -> &[CurveKeys<C, M>] {
103        &self.keys
104    }
105
106    pub fn get_key(&self, index: PeerIndex) -> Option<&CurveKeys<C, M>> {
107        self.keys.get(index)
108    }
109
110    pub fn get_macs(&self) -> &[CurvePoints<C, M>] {
111        &self.macs
112    }
113
114    pub fn get_mac(&self, index: PeerIndex) -> Option<&CurvePoints<C, M>> {
115        self.macs.get(index)
116    }
117
118    #[inline]
119    pub fn get_alphas(&self) -> impl ExactSizeIterator<Item = GlobalCurveKey<C>> + '_ {
120        self.keys.iter().map(|key| key.get_alpha())
121    }
122}
123
124// --------------------
125// |   Verification   |
126// --------------------
127impl<C: Curve, M: Positive> VerifiableWith for PointShares<C, M> {
128    type VerificationData = ();
129    /// Check the MACs of the share received from another peer.
130    #[inline]
131    fn verify_from_peer_with(
132        &self,
133        open_share: &OpenPointShares<C, M>,
134        peer: PeerIndex,
135        _verification_data: (),
136    ) -> Result<(), PrimitiveError> {
137        self.get_key(peer)
138            .ok_or(PrimitiveError::InvalidPeerIndex(peer, self.keys.len()))?
139            .verify_mac(open_share)
140            .map_err(|e| e.blame(peer))
141    }
142
143    /// Check the MACs of each share received from all other peers.
144    #[inline]
145    fn verify_with(
146        &self,
147        open_shares: &[OpenPointShares<C, M>],
148        _verification_data: (),
149    ) -> Result<(), PrimitiveError> {
150        enumerate(izip_eq!(open_shares, &self.keys))
151            .map(|(from_peer, (open_share, key))| {
152                key.verify_mac(open_share).map_err(|e| e.blame(from_peer))
153            })
154            .collect_errors()?;
155        Ok(())
156    }
157}
158
159// --------------------------------
160// |   Opening & Reconstruction   |
161// --------------------------------
162
163impl<C: Curve, M: Positive> Reconstructible for PointShares<C, M> {
164    type Opening = OpenPointShares<C, M>;
165    type Secret = CurvePoints<C, M>;
166
167    /// Open the share towards another peer.
168    fn open_to(&self, peer: PeerIndex) -> Result<OpenPointShares<C, M>, PrimitiveError> {
169        let mac = self
170            .get_mac(peer)
171            .ok_or(PrimitiveError::InvalidPeerIndex(peer, self.macs.len()))?
172            .to_owned();
173        Ok(OpenPointShares::new(self.get_value().to_owned(), mac))
174    }
175
176    /// Open the share towards all other peers.
177    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = OpenPointShares<C, M>> {
178        self.get_macs()
179            .iter()
180            .map(|mac| OpenPointShares::new(self.get_value().to_owned(), mac.to_owned()))
181    }
182
183    /// Reconstruct a secret from openings coming from all other parties.
184    fn reconstruct(
185        &self,
186        openings: &[OpenPointShares<C, M>],
187    ) -> Result<Self::Secret, PrimitiveError> {
188        if openings.len() != self.get_keys().len() {
189            return Err(PrimitiveError::InvalidSize(
190                self.get_keys().len(),
191                openings.len(),
192            ));
193        }
194        self.verify_with(openings, ())?;
195        Ok(openings
196            .iter()
197            .fold(self.get_value().to_owned(), |acc, open_share| {
198                acc + open_share.get_value()
199            }))
200    }
201}
202
203// -------------------------
204// |   Random Generation   |
205// -------------------------
206
207fn compute_macs<C: Curve, M: Positive>(
208    all_unauth_shares: &[CurvePoints<C, M>],
209    all_keys: &[Box<[CurveKeys<C, M>]>],
210) -> Vec<Box<[CurvePoints<C, M>]>> {
211    let mut all_key_iters = all_keys.iter().map(|k| k.iter()).collect::<Vec<_>>();
212    enumerate(all_unauth_shares.iter())
213        .map(|(i, my_unauth_share)| {
214            enumerate(all_key_iters.iter_mut())
215                .filter(|(j, _)| *j != i)
216                .map(|(_, keys_iter)| keys_iter.next().unwrap().compute_mac(my_unauth_share))
217                .collect()
218        })
219        .collect()
220}
221
222impl<C: Curve, M: Positive> Random for PointShares<C, M> {
223    fn random(_rng: impl CryptoRngCore) -> Self {
224        unimplemented!(
225            "Type {} does not support `random` since it needs to know `n_parties`. Use `random_with(..., n_parties)` instead.",
226            std::any::type_name::<Self>()
227        )
228    }
229
230    /// Generate one random field share per peer, with consistent MACs and keys across all peers.
231    fn random_n<Container: FromIterator<Self>>(
232        mut rng: impl CryptoRngCore,
233        n_parties: NParties,
234    ) -> Container {
235        let all_unauth_shares: Vec<_> = CurvePoints::random_n(&mut rng, n_parties);
236        let all_keys = (0..n_parties)
237            .map(|_| CurveKeys::<C, M>::random_n(&mut rng, n_parties - 1))
238            .collect::<Vec<_>>();
239        let all_macs = compute_macs(&all_unauth_shares, &all_keys);
240        izip_eq!(all_unauth_shares, all_macs, all_keys)
241            .map(|(value, macs, keys)| PointShares::new(value, macs, keys))
242            .collect()
243    }
244}
245
246impl<C: Curve, M: Positive> RandomWith<NParties> for PointShares<C, M> {
247    /// Generate a random field share, with Macs and keys for all the other parties.
248    fn random_with(mut rng: impl CryptoRngCore, n_parties: NParties) -> Self {
249        PointShares::new(
250            CurvePoints::<C, M>::random(&mut rng),
251            CurvePoints::<C, M>::random_n(&mut rng, n_parties - 1),
252            CurveKeys::<C, M>::random_n(&mut rng, n_parties - 1),
253        )
254    }
255}
256
257impl<C: Curve, M: Positive> RandomWith<(NParties, CurvePoints<C, M>)> for PointShares<C, M> {
258    /// Generate a random field share with a specific secret share as value and random macs & keys.
259    fn random_with(
260        mut rng: impl CryptoRngCore,
261        n_parties_value: (NParties, CurvePoints<C, M>),
262    ) -> Self {
263        let (n_parties, value) = n_parties_value;
264        PointShares::new(
265            value,
266            CurvePoints::<C, M>::random_n(&mut rng, n_parties - 1),
267            CurveKeys::<C, M>::random_n(&mut rng, n_parties - 1),
268        )
269    }
270}
271
272impl<C: Curve, M: Positive> RandomWith<CurvePoints<C, M>> for PointShares<C, M> {
273    fn random_with(_rng: impl CryptoRngCore, _data: CurvePoints<C, M>) -> Self {
274        unimplemented!(
275            "Type {} does not support `random_with` since it needs to know `n_parties`. Use `random_n_with` instead.",
276            std::any::type_name::<Self>()
277        )
278    }
279
280    /// Secret share a value among n parties, generating an authenticated share for each
281    /// peer with consistent MACs and keys across all peers.
282    fn random_n_with<Container: FromIterator<Self>>(
283        mut rng: impl CryptoRngCore,
284        n_parties: usize,
285        value: CurvePoints<C, M>,
286    ) -> Container {
287        let all_unauth_shares = value.to_additive_shares(n_parties, &mut rng);
288        let all_keys = (0..n_parties)
289            .map(|_| CurveKeys::<C, M>::random_n(&mut rng, n_parties - 1))
290            .collect::<Vec<_>>();
291        let all_macs = compute_macs(&all_unauth_shares, &all_keys);
292        izip_eq!(all_unauth_shares, all_macs, all_keys)
293            .map(|(value, macs, keys)| PointShares::new(value, macs, keys))
294            .collect()
295    }
296
297    /// Generate a random field share with a specific secret share as value,
298    /// with consistent random MACs and keys across all peers.
299    fn random_n_with_each<Container: FromIterator<Self>>(
300        mut rng: impl CryptoRngCore,
301        n_parties_and_value: impl IntoExactSizeIterator<Item = CurvePoints<C, M>>,
302    ) -> Container {
303        let all_unauth_shares = n_parties_and_value.into_iter().collect::<Vec<_>>();
304        let n_parties = all_unauth_shares.len();
305        let all_keys = (0..n_parties)
306            .map(|_| CurveKeys::<C, M>::random_n(&mut rng, n_parties - 1))
307            .collect::<Vec<_>>();
308        let all_macs = compute_macs(&all_unauth_shares, &all_keys);
309        izip_eq!(all_unauth_shares, all_macs, all_keys)
310            .map(|(value, macs, keys)| PointShares::new(value, macs, keys))
311            .collect()
312    }
313}
314
315impl<C: Curve, M: Positive> RandomWith<Vec<GlobalCurveKey<C>>> for PointShares<C, M> {
316    /// Generate a random field share from its global keys (alphas).
317    fn random_with(mut rng: impl CryptoRngCore, alphas: Vec<GlobalCurveKey<C>>) -> Self {
318        let n_other_parties = alphas.len();
319        PointShares::new(
320            CurvePoints::random(&mut rng),
321            CurvePoints::<C, M>::random_n(&mut rng, n_other_parties),
322            CurveKeys::<C, M>::random_n_with_each(&mut rng, alphas),
323        )
324    }
325
326    /// Generate one random field share per peer from their global keys (alphas).
327    fn random_n_with_each<Container: FromIterator<Self>>(
328        mut rng: impl CryptoRngCore,
329        all_alphas: impl IntoExactSizeIterator<Item = Vec<GlobalCurveKey<C>>>,
330    ) -> Container {
331        let all_alphas = all_alphas.into_iter();
332        let all_unauth_shares: Vec<_> = CurvePoints::random_n(&mut rng, all_alphas.len());
333        let all_keys = all_alphas
334            .into_iter()
335            .map(|my_alphas| CurveKeys::<C, M>::random_n_with_each(&mut rng, my_alphas))
336            .collect::<Vec<_>>();
337        let all_macs = compute_macs(&all_unauth_shares, &all_keys);
338        izip_eq!(all_unauth_shares, all_macs, all_keys)
339            .map(|(value, macs, keys)| PointShares::new(value, macs, keys))
340            .collect()
341    }
342}
343
344impl<C: Curve, M: Positive> RandomWith<(CurvePoints<C, M>, Vec<GlobalCurveKey<C>>)>
345    for PointShares<C, M>
346{
347    /// Generate a random field share from its global keys (alphas) and its share
348    fn random_with(
349        mut rng: impl CryptoRngCore,
350        value_alphas: (CurvePoints<C, M>, Vec<GlobalCurveKey<C>>),
351    ) -> Self {
352        let (value, alphas) = value_alphas;
353        let n_other_parties = alphas.len();
354        PointShares::new(
355            value,
356            CurvePoints::<C, M>::random_n(&mut rng, n_other_parties),
357            CurveKeys::<C, M>::random_n_with_each(&mut rng, alphas),
358        )
359    }
360
361    /// Generate one random field share per peer from their global keys (alphas).
362    fn random_n_with_each<Container: FromIterator<Self>>(
363        mut rng: impl CryptoRngCore,
364        unauth_shares_and_alphas: impl IntoIterator<Item = (CurvePoints<C, M>, Vec<GlobalCurveKey<C>>)>,
365    ) -> Container {
366        let (all_unauth_shares, all_keys): (Vec<_>, Vec<_>) = unauth_shares_and_alphas
367            .into_iter()
368            .map(|(value, my_alphas)| {
369                (
370                    value,
371                    CurveKeys::<C, M>::random_n_with_each(&mut rng, my_alphas),
372                )
373            })
374            .unzip();
375        let all_macs = compute_macs(&all_unauth_shares, &all_keys);
376        izip_eq!(all_unauth_shares, all_macs, all_keys)
377            .map(|(value, macs, keys)| PointShares::new(value, macs, keys))
378            .collect()
379    }
380}
381
382impl<C: Curve, M: Positive> RandomWith<(CurvePoints<C, M>, Vec<Vec<GlobalCurveKey<C>>>)>
383    for PointShares<C, M>
384{
385    fn random_with(
386        _rng: impl CryptoRngCore,
387        _data: (CurvePoints<C, M>, Vec<Vec<GlobalCurveKey<C>>>),
388    ) -> Self {
389        unimplemented!(
390            "Cannot discern what alpha/global key to use for this peer. Use `random_n_with` instead."
391        );
392    }
393
394    /// Generate a random field share per peer from a value to secret share and global keys
395    /// (alphas).
396    fn random_n_with<Container: FromIterator<Self>>(
397        mut rng: impl CryptoRngCore,
398        n_parties: usize,
399        secret_value_and_alphas: (CurvePoints<C, M>, Vec<Vec<GlobalCurveKey<C>>>),
400    ) -> Container {
401        let (secret_value, all_alphas) = secret_value_and_alphas;
402        assert_eq!(all_alphas.len(), n_parties);
403        let all_unauth_shares = secret_value.to_additive_shares(all_alphas.len(), &mut rng);
404        let all_keys = all_alphas
405            .into_iter()
406            .map(|my_alphas| CurveKeys::<C, M>::random_n_with_each(&mut rng, my_alphas))
407            .collect::<Vec<_>>();
408        let all_macs = compute_macs(&all_unauth_shares, &all_keys);
409        izip_eq!(all_unauth_shares, all_macs, all_keys)
410            .map(|(value, macs, keys)| PointShares::new(value, macs, keys))
411            .collect()
412    }
413}
414
415// ------------------------------------
416// | Curve Arithmetic Implementations |
417// ------------------------------------
418
419// === Addition === //
420
421#[macros::op_variants(owned, borrowed, flipped_commutative)]
422impl<'a, C: Curve, M: Positive> Add<&'a PointShares<C, M>> for PointShares<C, M> {
423    type Output = PointShares<C, M>;
424
425    #[inline]
426    fn add(mut self, other: &'a PointShares<C, M>) -> Self::Output {
427        self.value += &other.value;
428        izip_eq!(&mut self.macs, &other.macs).for_each(|(a, b)| *a += b);
429        izip_eq!(&mut self.keys, &other.keys).for_each(|(a, b)| *a += b);
430        self
431    }
432}
433
434#[macros::op_variants(owned)]
435impl<'a, C: Curve, M: Positive> AddAssign<&'a PointShares<C, M>> for PointShares<C, M> {
436    #[inline]
437    fn add_assign(&mut self, other: &'a PointShares<C, M>) {
438        self.value += &other.value;
439        izip_eq!(&mut self.macs, &other.macs).for_each(|(a, b)| *a += b);
440        izip_eq!(&mut self.keys, &other.keys).for_each(|(a, b)| *a += b);
441    }
442}
443
444// === Subtraction === //
445
446#[macros::op_variants(owned, borrowed, flipped)]
447impl<'a, C: Curve, M: Positive> Sub<&'a PointShares<C, M>> for PointShares<C, M> {
448    type Output = PointShares<C, M>;
449
450    #[inline]
451    fn sub(mut self, other: &'a PointShares<C, M>) -> Self::Output {
452        self.value -= &other.value;
453        izip_eq!(&mut self.macs, &other.macs).for_each(|(a, b)| *a -= b);
454        izip_eq!(&mut self.keys, &other.keys).for_each(|(a, b)| *a -= b);
455        self
456    }
457}
458
459#[macros::op_variants(owned)]
460impl<'a, C: Curve, M: Positive> SubAssign<&'a PointShares<C, M>> for PointShares<C, M> {
461    #[inline]
462    fn sub_assign(&mut self, other: &'a PointShares<C, M>) {
463        self.value -= &other.value;
464        izip_eq!(&mut self.macs, &other.macs).for_each(|(a, b)| *a -= b);
465        izip_eq!(&mut self.keys, &other.keys).for_each(|(a, b)| *a -= b);
466    }
467}
468
469// === Broadcast multiplication === //
470
471#[macros::op_variants(owned, borrowed)]
472impl<'a, C: Curve, M: Positive> Mul<&'a ScalarAsExtension<C>> for PointShares<C, M> {
473    type Output = PointShares<C, M>;
474
475    #[inline]
476    fn mul(mut self, other: &'a ScalarAsExtension<C>) -> Self::Output {
477        self.value *= other;
478        izip_eq!(&mut self.keys).for_each(|key| *key *= other);
479        izip_eq!(&mut self.macs).for_each(|mac| *mac *= other);
480        self
481    }
482}
483
484#[macros::op_variants(owned, borrowed)]
485impl<'a, C: Curve, M: Positive> Mul<&'a Scalar<C>> for PointShares<C, M> {
486    type Output = PointShares<C, M>;
487
488    #[inline]
489    fn mul(mut self, other: &'a Scalar<C>) -> Self::Output {
490        self.value *= other;
491        izip_eq!(&mut self.keys).for_each(|key| *key *= other);
492        izip_eq!(&mut self.macs).for_each(|mac| *mac *= other);
493        self
494    }
495}
496
497#[macros::op_variants(owned, borrowed)]
498impl<'a, C: Curve, M: Positive> Mul<&'a ScalarsAsExtension<C, M>> for PointShares<C, M> {
499    type Output = PointShares<C, M>;
500
501    #[inline]
502    fn mul(mut self, other: &'a ScalarsAsExtension<C, M>) -> Self::Output {
503        self.value *= other;
504        izip_eq!(&mut self.keys).for_each(|key| *key *= other);
505        izip_eq!(&mut self.macs).for_each(|mac| *mac *= other);
506        self
507    }
508}
509
510#[macros::op_variants(owned, borrowed)]
511impl<'a, C: Curve, M: Positive> Mul<&'a Scalars<C, M>> for PointShares<C, M> {
512    type Output = PointShares<C, M>;
513
514    #[inline]
515    fn mul(mut self, other: &'a Scalars<C, M>) -> Self::Output {
516        self.value *= other;
517        izip_eq!(&mut self.keys).for_each(|key| *key *= other);
518        izip_eq!(&mut self.macs).for_each(|mac| *mac *= other);
519        self
520    }
521}
522
523// === MulAssign === //
524
525#[macros::op_variants(owned)]
526impl<'a, C: Curve, M: Positive> MulAssign<&'a ScalarAsExtension<C>> for PointShares<C, M> {
527    #[inline]
528    fn mul_assign(&mut self, other: &'a ScalarAsExtension<C>) {
529        self.value *= other;
530        izip_eq!(&mut self.keys).for_each(|key| *key *= other);
531        izip_eq!(&mut self.macs).for_each(|mac| *mac *= other);
532    }
533}
534
535#[macros::op_variants(owned)]
536impl<'a, C: Curve, M: Positive> MulAssign<&'a Scalar<C>> for PointShares<C, M> {
537    #[inline]
538    fn mul_assign(&mut self, other: &'a Scalar<C>) {
539        self.value *= other;
540        izip_eq!(&mut self.keys).for_each(|key| *key *= other);
541        izip_eq!(&mut self.macs).for_each(|mac| *mac *= other);
542    }
543}
544
545#[macros::op_variants(owned)]
546impl<'a, C: Curve, M: Positive> MulAssign<&'a ScalarsAsExtension<C, M>> for PointShares<C, M> {
547    #[inline]
548    fn mul_assign(&mut self, other: &'a ScalarsAsExtension<C, M>) {
549        self.value *= other;
550        izip_eq!(&mut self.keys).for_each(|key| *key *= other);
551        izip_eq!(&mut self.macs).for_each(|mac| *mac *= other);
552    }
553}
554
555#[macros::op_variants(owned)]
556impl<'a, C: Curve, M: Positive> MulAssign<&'a Scalars<C, M>> for PointShares<C, M> {
557    #[inline]
558    fn mul_assign(&mut self, other: &'a Scalars<C, M>) {
559        self.value *= other;
560        izip_eq!(&mut self.keys).for_each(|key| *key *= other);
561        izip_eq!(&mut self.macs).for_each(|mac| *mac *= other);
562    }
563}
564
565// === Negation === //
566
567#[macros::op_variants(borrowed)]
568impl<C: Curve, M: Positive> Neg for PointShares<C, M> {
569    type Output = PointShares<C, M>;
570
571    #[inline]
572    fn neg(self) -> Self::Output {
573        PointShares {
574            value: -self.value,
575            keys: izip_eq!(self.keys).map(|key| -key).collect(),
576            macs: izip_eq!(self.macs).map(|mac| -mac).collect(),
577        }
578    }
579}
580
581// === Constant addition / subtraction === //
582
583impl<C: Curve, M: Positive> PointShares<C, M> {
584    /// Adds a public constant to the secret shared value, updating the keys accordingly.
585    /// A designated peer (P_0) will modify its value, while the other peers will update their keys.
586    #[inline]
587    pub fn add_secret_owned(mut self, constant: &CurvePoints<C, M>, is_peer_zero: bool) -> Self {
588        if is_peer_zero {
589            self.value += constant;
590        } else {
591            let key0 = self.keys.get_mut(0).expect("Missing key 0");
592            key0.betas -= constant * *key0.alpha;
593        }
594        self
595    }
596
597    /// Adds a public constant to the secret shared value, updating the keys accordingly.
598    /// A designated peer (P_0) will modify its value, while the other peers will update their keys.
599    #[inline]
600    pub fn add_secret(&self, constant: &CurvePoints<C, M>, is_peer_zero: bool) -> Self {
601        let result = self.clone();
602        result.add_secret_owned(constant, is_peer_zero)
603    }
604}
605
606// === Constant time equality / selection === //
607
608impl<C: Curve, M: Positive> ConstantTimeEq for PointShares<C, M> {
609    #[inline]
610    fn ct_eq(&self, other: &Self) -> Choice {
611        self.value.ct_eq(&other.value) & self.keys.ct_eq(&other.keys) & self.macs.ct_eq(&other.macs)
612    }
613}
614
615impl<C: Curve, M: Positive> ConditionallySelectable for PointShares<C, M> {
616    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
617        PointShares {
618            value: CurvePoints::conditional_select(&a.value, &b.value, choice),
619            macs: izip_eq!(&a.macs, &b.macs)
620                .map(|(a, b)| CurvePoints::conditional_select(a, b, choice))
621                .collect(),
622            keys: izip_eq!(&a.keys, &b.keys)
623                .map(|(a, b)| CurveKeys::conditional_select(a, b, choice))
624                .collect(),
625        }
626    }
627}
628
629#[cfg(test)]
630mod tests {
631
632    use itertools::enumerate;
633    use typenum::U12;
634
635    use super::*;
636    use crate::{
637        algebra::elliptic_curve::Curve25519Ristretto as C,
638        random,
639        sharing::Verifiable,
640        types::heap_array::curve_arrays::Scalars,
641    };
642
643    // TODO: Generalize once PairwiseAuthenticatedShare trait is implemented.
644
645    //////////////////////////////////////////////////
646    // CHANGE ONLY THESE TO TEST ANOTHER SHARE TYPE //
647    pub type M = U12;
648    pub type Value = CurvePoints<C, M>;
649    pub type Constant = Scalars<C, M>;
650    pub type Share = PointShares<C, M>;
651    pub type GlobalKey = GlobalCurveKey<C>;
652    //////////////////////////////////////////////////
653
654    pub const N_PARTIES: usize = 3;
655
656    #[test]
657    fn test_open_to() {
658        let mut rng = random::test_rng();
659        let local_share = Share::random_with(&mut rng, N_PARTIES);
660
661        for i in 0..N_PARTIES - 1 {
662            let open_share = local_share.open_to(i).unwrap();
663            assert_eq!(open_share.get_value(), &local_share.value);
664            assert_eq!(open_share.get_mac(), &local_share.macs[i]);
665        }
666    }
667
668    #[test]
669    fn test_random() {
670        let mut rng = random::test_rng();
671
672        // Generate a completely random share
673        let share = Share::random_with(&mut rng, N_PARTIES);
674        assert_eq!(share.get_macs().len(), N_PARTIES - 1);
675        assert_eq!(share.get_keys().len(), N_PARTIES - 1);
676
677        // Generate a share with a specific value
678        let value = &Value::random(&mut rng);
679        let share_with_value = Share::random_with(&mut rng, (N_PARTIES, value.to_owned()));
680        assert_eq!(share_with_value.get_value(), value);
681        assert_eq!(share_with_value.get_macs().len(), N_PARTIES - 1);
682        assert_eq!(share_with_value.get_keys().len(), N_PARTIES - 1);
683    }
684
685    #[test]
686    fn test_random_vec_and_reconstruct() {
687        let mut rng = random::test_rng();
688
689        // Generate a vector of random shares, one per party
690        let shares: Vec<_> = Share::random_n(&mut rng, N_PARTIES);
691        assert_eq!(shares.len(), N_PARTIES);
692        for share in &shares {
693            assert_eq!(share.get_macs().len(), N_PARTIES - 1);
694            assert_eq!(share.get_keys().len(), N_PARTIES - 1);
695        }
696        let unauthenticated_shares = shares
697            .iter()
698            .map(|s| s.get_value().to_owned())
699            .collect::<Vec<_>>();
700        let expected = Value::from_additive_shares(&unauthenticated_shares);
701        let reconstructed = Share::reconstruct_all(&shares).unwrap();
702        assert_eq!(reconstructed, expected);
703
704        // Secret share a value among n parties
705        let value = &Value::random(&mut rng);
706        let shares: Vec<_> = Share::random_n_with(&mut rng, N_PARTIES, value.to_owned());
707        assert_eq!(shares.len(), N_PARTIES);
708        for share in &shares {
709            assert_eq!(share.get_macs().len(), N_PARTIES - 1);
710            assert_eq!(share.get_keys().len(), N_PARTIES - 1);
711        }
712        let reconstructed = Share::reconstruct_all(&shares).unwrap();
713        assert_eq!(reconstructed, value.to_owned());
714    }
715
716    #[test]
717    fn test_random_vec_with_global_key_and_reconstruct() {
718        let mut rng = random::test_rng();
719
720        // Generate n authenticated shares from n global keys (alphas)
721        let alphas = Vec::<Vec<GlobalKey>>::random_with(&mut rng, N_PARTIES);
722        let shares_from_alphas: Vec<_> = Share::random_n_with_each(&mut rng, alphas.clone());
723        assert_eq!(shares_from_alphas.len(), N_PARTIES);
724        for (share_a, my_alphas) in izip_eq!(&shares_from_alphas, alphas) {
725            assert_eq!(share_a.get_macs().len(), N_PARTIES - 1);
726            assert_eq!(share_a.get_keys().len(), N_PARTIES - 1);
727            assert_eq!(share_a.get_alphas().collect::<Vec<_>>(), my_alphas);
728        }
729        let _ = Share::reconstruct_all(&shares_from_alphas).unwrap(); // Verify all MACs
730
731        // Generate n authenticated shares from a value and n global keys (alphas)
732        let value = &Value::random(&mut rng);
733        let alphas = Vec::<Vec<GlobalKey>>::random_with(&mut rng, N_PARTIES);
734        let shares_from_value_and_alphas: Vec<_> =
735            Share::random_n_with(&mut rng, N_PARTIES, (value.to_owned(), alphas.clone()));
736        assert_eq!(shares_from_value_and_alphas.len(), N_PARTIES);
737        for (share_a, my_alphas) in izip_eq!(&shares_from_value_and_alphas, alphas) {
738            assert_eq!(share_a.get_macs().len(), N_PARTIES - 1);
739            assert_eq!(share_a.get_keys().len(), N_PARTIES - 1);
740            assert_eq!(share_a.get_alphas().collect::<Vec<_>>(), my_alphas);
741        }
742        let reconstructed = Share::reconstruct_all(&shares_from_value_and_alphas).unwrap();
743        assert_eq!(&reconstructed, value);
744
745        // Generate n authenticated shares from n unauthenticated shares and n global keys (alphas)
746        let value = Value::random(&mut rng);
747        let unauth_shares = value.to_additive_shares(N_PARTIES, &mut rng);
748        let alphas = Vec::<Vec<GlobalKey>>::random_with(&mut rng, N_PARTIES);
749        let shares_from_unauth_and_alphas: Vec<_> =
750            Share::random_n_with_each(&mut rng, izip_eq!(unauth_shares.clone(), alphas.clone()));
751        assert_eq!(shares_from_unauth_and_alphas.len(), N_PARTIES);
752        for (share_a, my_alphas) in izip_eq!(&shares_from_unauth_and_alphas, alphas) {
753            assert_eq!(share_a.get_macs().len(), N_PARTIES - 1);
754            assert_eq!(share_a.get_keys().len(), N_PARTIES - 1);
755            assert_eq!(share_a.get_alphas().collect::<Vec<_>>(), my_alphas);
756        }
757        let reconstructed = Share::reconstruct_all(&shares_from_unauth_and_alphas).unwrap();
758        assert_eq!(reconstructed, value);
759    }
760
761    #[test]
762    fn test_verify_mac() {
763        let mut rng = random::test_rng();
764
765        let shares: Vec<_> = Share::random_n(&mut rng, N_PARTIES);
766
767        // Verify each open separately
768        enumerate(shares.iter()).for_each(|(i, s_i)| {
769            enumerate(shares.iter())
770                .filter(|(j, _)| i != *j)
771                .for_each(|(j, s_j)| {
772                    let open_share = s_j.open_to(i - (i > j) as usize).unwrap();
773                    s_i.verify_from(&open_share, j - (j > i) as usize).unwrap();
774                });
775        });
776
777        // Verify all openings at once
778        Share::verify_all(&shares).unwrap();
779    }
780
781    #[test]
782    fn test_add() {
783        let mut rng = random::test_rng();
784
785        let alphas = Vec::<Vec<GlobalKey>>::random_with(&mut rng, N_PARTIES);
786        let a = &Value::random(&mut rng);
787        let b = &Value::random(&mut rng);
788
789        let shares_a: Vec<_> =
790            Share::random_n_with(&mut rng, N_PARTIES, (a.to_owned(), alphas.clone()));
791        let shares_b: Vec<_> = Share::random_n_with(&mut rng, N_PARTIES, (b.to_owned(), alphas));
792
793        // &a + &b
794        let shares_a_ref_plus_b_ref = izip_eq!(&shares_a, &shares_b)
795            .map(|(share_a, share_b)| share_a + share_b)
796            .collect::<Vec<_>>();
797        let reconstructed = Share::reconstruct_all(&shares_a_ref_plus_b_ref).unwrap();
798        assert_eq!(reconstructed, a + b);
799
800        // &a + b
801        let shares_a_ref_plus_b = izip_eq!(&shares_a, shares_b.clone())
802            .map(|(share_a, share_b)| share_a + share_b)
803            .collect::<Vec<_>>();
804        let reconstructed = Share::reconstruct_all(&shares_a_ref_plus_b).unwrap();
805        assert_eq!(reconstructed, a + b);
806
807        // a + &b
808        let shares_a_plus_b_ref = izip_eq!(shares_a.clone(), &shares_b)
809            .map(|(share_a, share_b)| share_a + share_b)
810            .collect::<Vec<_>>();
811        let reconstructed = Share::reconstruct_all(&shares_a_plus_b_ref).unwrap();
812        assert_eq!(reconstructed, a + b);
813
814        // a + b
815        let shares_a_plus_b = izip_eq!(shares_a.clone(), shares_b.clone())
816            .map(|(share_a, share_b)| share_a + share_b)
817            .collect::<Vec<_>>();
818        let reconstructed = Share::reconstruct_all(&shares_a_plus_b).unwrap();
819        assert_eq!(reconstructed, a + b);
820
821        // a += &b
822        let mut shares_a_add_assign_b_ref = shares_a.clone();
823        izip_eq!(&mut shares_a_add_assign_b_ref, &shares_b)
824            .for_each(|(share_a, share_b)| *share_a += share_b);
825        let reconstructed = Share::reconstruct_all(&shares_a_add_assign_b_ref).unwrap();
826        assert_eq!(reconstructed, a + b);
827
828        // a += b
829        let mut shares_a_add_assign_b = shares_a.clone();
830        izip_eq!(&mut shares_a_add_assign_b, shares_b)
831            .for_each(|(share_a, share_b)| *share_a += share_b);
832        let reconstructed = Share::reconstruct_all(&shares_a_add_assign_b).unwrap();
833        assert_eq!(reconstructed, a + b);
834    }
835
836    #[test]
837    fn test_add_secret() {
838        let mut rng = random::test_rng();
839
840        let a = &Value::random(&mut rng);
841        let k = &Value::random(&mut rng);
842
843        let shares_a: Vec<_> = Share::random_n_with(&mut rng, N_PARTIES, a.to_owned());
844
845        // &a + k
846        let shares_a_plus_k_ref = enumerate(shares_a.iter())
847            .map(|(i, share_a)| share_a.add_secret(k, i == 0))
848            .collect::<Vec<_>>();
849        let reconstructed = Share::reconstruct_all(&shares_a_plus_k_ref).unwrap();
850        assert_eq!(reconstructed, a + k);
851
852        // a + k
853        let shares_a_plus_k = enumerate(shares_a)
854            .map(|(i, share_a)| share_a.add_secret_owned(k, i == 0))
855            .collect::<Vec<_>>();
856        let reconstructed = Share::reconstruct_all(&shares_a_plus_k).unwrap();
857        assert_eq!(reconstructed, a + k);
858    }
859
860    #[test]
861    fn test_mul_constant() {
862        let mut rng = random::test_rng();
863
864        let a = &Value::random(&mut rng);
865        let k = &Constant::random(&mut rng);
866
867        let shares_a: Vec<_> = Share::random_n_with(&mut rng, N_PARTIES, a.to_owned());
868
869        // a * k
870        let shares_a_times_k = izip_eq!(shares_a.clone())
871            .map(|share_a| share_a * k)
872            .collect::<Vec<_>>();
873        let reconstructed = Share::reconstruct_all(&shares_a_times_k).unwrap();
874        assert_eq!(reconstructed, a * k);
875
876        // a *= k
877        let mut shares_a_times_k_assign = shares_a.clone();
878        izip_eq!(&mut shares_a_times_k_assign).for_each(|share_a| *share_a *= k);
879        let reconstructed = Share::reconstruct_all(&shares_a_times_k_assign).unwrap();
880        assert_eq!(reconstructed, a * k);
881    }
882
883    #[test]
884    fn test_sub() {
885        let mut rng = random::test_rng();
886        let alphas = Vec::<Vec<GlobalKey>>::random_with(&mut rng, N_PARTIES);
887
888        let a = &Value::random(&mut rng);
889        let b = &Value::random(&mut rng);
890
891        let shares_a: Vec<_> =
892            Share::random_n_with(&mut rng, N_PARTIES, (a.to_owned(), alphas.clone()));
893        let shares_b: Vec<_> = Share::random_n_with(&mut rng, N_PARTIES, (b.to_owned(), alphas));
894
895        // &a - &b
896        let shares_a_ref_minus_b_ref = izip_eq!(&shares_a, &shares_b)
897            .map(|(share_a, share_b)| share_a - share_b)
898            .collect::<Vec<_>>();
899
900        let reconstructed = Share::reconstruct_all(&shares_a_ref_minus_b_ref).unwrap();
901        assert_eq!(reconstructed, a - b);
902
903        // &a - b
904        let shares_a_ref_minus_b = izip_eq!(&shares_a, shares_b.clone())
905            .map(|(share_a, share_b)| share_a - share_b)
906            .collect::<Vec<_>>();
907        let reconstructed = Share::reconstruct_all(&shares_a_ref_minus_b).unwrap();
908        assert_eq!(reconstructed, a - b);
909
910        // a - &b
911        let shares_a_minus_b_ref = izip_eq!(shares_a.clone(), &shares_b)
912            .map(|(share_a, share_b)| share_a - share_b)
913            .collect::<Vec<_>>();
914        let reconstructed = Share::reconstruct_all(&shares_a_minus_b_ref).unwrap();
915        assert_eq!(reconstructed, a - b);
916
917        // a - b
918        let shares_a_minus_b = izip_eq!(shares_a.clone(), shares_b.clone())
919            .map(|(share_a, share_b)| share_a - share_b)
920            .collect::<Vec<_>>();
921        let reconstructed = Share::reconstruct_all(&shares_a_minus_b).unwrap();
922        assert_eq!(reconstructed, a - b);
923
924        // a -= &b
925        let mut shares_a_sub_assign_b_ref = shares_a.clone();
926        izip_eq!(&mut shares_a_sub_assign_b_ref, &shares_b)
927            .for_each(|(share_a, share_b)| *share_a -= share_b);
928        let reconstructed = Share::reconstruct_all(&shares_a_sub_assign_b_ref).unwrap();
929        assert_eq!(reconstructed, a - b);
930
931        // a -= b
932        let mut shares_a_sub_assign_b = shares_a.clone();
933        izip_eq!(&mut shares_a_sub_assign_b, shares_b)
934            .for_each(|(share_a, share_b)| *share_a -= share_b);
935        let reconstructed = Share::reconstruct_all(&shares_a_sub_assign_b).unwrap();
936        assert_eq!(reconstructed, a - b);
937    }
938
939    #[test]
940    fn test_neg() {
941        let mut rng = random::test_rng();
942
943        let a = Value::random(&mut rng);
944
945        let shares_a: Vec<_> = Share::random_n_with(&mut rng, N_PARTIES, a.to_owned());
946
947        // - &a
948        let shares_a_neg_ref = shares_a.iter().map(|share_a| -share_a).collect::<Vec<_>>();
949        let reconstructed = Share::reconstruct_all(&shares_a_neg_ref).unwrap();
950        assert_eq!(reconstructed, -a.to_owned());
951
952        // - a
953        let shares_a_neg = shares_a
954            .into_iter()
955            .map(|share_a| -share_a)
956            .collect::<Vec<_>>();
957        let reconstructed = Share::reconstruct_all(&shares_a_neg).unwrap();
958        assert_eq!(reconstructed, -a.to_owned());
959    }
960
961    #[test]
962    fn test_conditional_select() {
963        let mut rng = random::test_rng();
964
965        let shares_a = Share::random_with(&mut rng, N_PARTIES);
966        let shares_b = Share::random_with(&mut rng, N_PARTIES);
967
968        // Select shares_a
969        let choice = Choice::from(0u8);
970        let selected = Share::conditional_select(&shares_a, &shares_b, choice);
971        assert_eq!(selected, shares_a);
972
973        // Select shares_b
974        let choice = Choice::from(1u8);
975        let selected = Share::conditional_select(&shares_a, &shares_b, choice);
976        assert_eq!(selected, shares_b);
977    }
978
979    #[test]
980    fn test_ct_eq() {
981        let mut rng = random::test_rng();
982
983        let shares_a = Share::random_with(&mut rng, N_PARTIES);
984        let shares_b = Share::random_with(&mut rng, N_PARTIES);
985
986        // Check equality
987        assert!(Into::<bool>::into(shares_a.ct_eq(&shares_a.clone())));
988        assert!(Into::<bool>::into(shares_b.ct_eq(&shares_b.clone())));
989        assert!(!Into::<bool>::into(shares_a.ct_eq(&shares_b)));
990        assert!(!Into::<bool>::into(shares_b.ct_eq(&shares_a)));
991    }
992}