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