Skip to main content

primitives/sharing/
mod.rs

1// Secret sharing schemes.
2pub mod authenticated;
3pub mod unauthenticated;
4
5use std::{borrow::Borrow, sync::Arc};
6
7pub use authenticated::*;
8use itertools::{enumerate, izip};
9use serde::{de::DeserializeOwned, Serialize};
10use wincode::{SchemaRead, SchemaWrite};
11
12use crate::{
13    algebra::{field::FieldExtension, ops::transpose::transpose},
14    errors::PrimitiveError,
15    random::{CryptoRngCore, RandomWith},
16    types::PeerIndex,
17    utils::TakeExact,
18};
19
20// ------------------------- //
21// ---- Reconstructible ---- //
22// ------------------------- //
23/// Reconstructs a secret from a collection of openings and a local share.
24pub trait Reconstructible: Sized {
25    /// The type of the reconstructed value.
26    type Secret: Serialize
27        + DeserializeOwned
28        + for<'de> SchemaRead<'de, Dst = Self::Secret>
29        + SchemaWrite<Src = Self::Secret>
30        + Clone
31        + PartialEq
32        + Send
33        + Sync
34        + 'static;
35    /// The type that is sent to / received from other peers.
36    type Opening: Serialize
37        + DeserializeOwned
38        + for<'de> SchemaRead<'de, Dst = Self::Opening>
39        + SchemaWrite<Src = Self::Opening>
40        + Clone
41        + Send
42        + Sync
43        + 'static;
44
45    /// Open the share towards another peer.
46    fn open_to(&self, peer_index: PeerIndex) -> Result<Self::Opening, PrimitiveError>;
47
48    /// Open the share towards all other peers. Returns an iterator with either one opening for
49    /// each peer or a single opening for all peers.
50    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening>;
51
52    /// Reconstruct a secret from openings coming from all other parties.
53    fn reconstruct(&self, openings: &[Self::Opening]) -> Result<Self::Secret, PrimitiveError>;
54
55    /// Reconstruct a secret from a collection of shares, by opening each share
56    /// towards all other peers, reconstructing `n` secrets from the openings and
57    /// checking that they are all equal.
58    fn reconstruct_all<T: Borrow<Self>>(shares: &[T]) -> Result<Self::Secret, PrimitiveError> {
59        let n_parties = shares.len();
60        if n_parties < 2 {
61            return Err(PrimitiveError::MinimumLength(2, n_parties));
62        }
63        // Open each share to all other peers.
64        let mut all_openings = shares
65            .iter()
66            .map(|share| share.borrow().open_to_all_others())
67            .collect::<Vec<_>>();
68        // Reconstruct each secret.
69        enumerate(shares.iter())
70            .map(|(i, share)| {
71                let my_openings = enumerate(all_openings.iter_mut())
72                    .take_exact(n_parties)
73                    .filter(|(j, _)| i != *j)
74                    .map(|(_, opening)| opening.next())
75                    .collect::<Option<Vec<_>>>()
76                    .ok_or_else(|| PrimitiveError::InvalidPeerIndex(i, shares.len() - 1))?;
77                share.borrow().reconstruct(my_openings.as_slice())
78            })
79            // Check that all reconstructed secrets are equal.
80            .reduce(|previous, current| match (previous, current) {
81                (Ok(prev), Ok(curr)) => match prev == curr {
82                    true => Ok(prev),
83                    false => Err(PrimitiveError::WrongOpening(
84                        serde_json::to_string(&prev).unwrap(),
85                        serde_json::to_string(&curr).unwrap(),
86                    )),
87                },
88                (Err(e), _) | (_, Err(e)) => Err(e),
89            })
90            .unwrap() // Safe because `shares.len() >= 2`
91    }
92}
93
94impl<T: Reconstructible<Opening: Clone>> Reconstructible for Vec<T> {
95    type Opening = Vec<T::Opening>;
96    type Secret = Vec<T::Secret>;
97
98    fn open_to(&self, peer_index: PeerIndex) -> Result<Self::Opening, PrimitiveError> {
99        self.iter().map(|share| share.open_to(peer_index)).collect()
100    }
101
102    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening> {
103        let all_openings: Vec<Vec<_>> = self
104            .iter()
105            .map(|share| share.open_to_all_others().collect())
106            .collect();
107
108        transpose(all_openings).into_iter()
109    }
110
111    fn reconstruct(&self, openings: &[Self::Opening]) -> Result<Self::Secret, PrimitiveError> {
112        if openings.is_empty() {
113            return Err(PrimitiveError::MinimumLength(1, 0));
114        }
115
116        if openings[0].len() != self.len() {
117            return Err(PrimitiveError::InvalidParameters(
118                "Number of openings must match number of shares.".to_string(),
119            ));
120        }
121
122        // Iterate over all the i-th elements of each entry of openings
123        let mut reconstructed = Vec::with_capacity(self.len());
124        for (i, share) in self.iter().enumerate() {
125            let my_openings: Vec<_> = openings
126                .iter()
127                .map(|opening| opening.get(i).cloned())
128                .collect::<Option<Vec<_>>>()
129                .ok_or_else(|| {
130                    PrimitiveError::InvalidParameters(
131                        "Opening is missing for some share.".to_string(),
132                    )
133                })?;
134            reconstructed.push(share.reconstruct(my_openings.as_slice())?);
135        }
136        Ok(reconstructed)
137    }
138}
139
140impl<T: Reconstructible<Opening: Clone>> Reconstructible for Arc<[T]> {
141    type Opening = Arc<[T::Opening]>;
142    type Secret = Arc<[T::Secret]>;
143
144    fn open_to(&self, peer_index: PeerIndex) -> Result<Self::Opening, PrimitiveError> {
145        self.iter().map(|share| share.open_to(peer_index)).collect()
146    }
147
148    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening> {
149        let all_openings: Vec<Vec<_>> = self
150            .iter()
151            .map(|share| share.open_to_all_others().collect())
152            .collect();
153
154        transpose(all_openings)
155            .into_iter()
156            .map(Arc::from)
157            .collect::<Vec<_>>()
158            .into_iter()
159    }
160
161    fn reconstruct(&self, openings: &[Self::Opening]) -> Result<Self::Secret, PrimitiveError> {
162        if openings.is_empty() {
163            return Err(PrimitiveError::MinimumLength(1, 0));
164        }
165
166        if openings[0].len() != self.len() {
167            return Err(PrimitiveError::InvalidParameters(
168                "Number of openings must match number of shares.".to_string(),
169            ));
170        }
171
172        // Iterate over all the i-th elements of each entry of openings
173        let mut reconstructed = Vec::with_capacity(self.len());
174        for (i, share) in self.iter().enumerate() {
175            let my_openings: Vec<_> = openings
176                .iter()
177                .map(|opening| opening.get(i).cloned())
178                .collect::<Option<Vec<_>>>()
179                .ok_or_else(|| {
180                    PrimitiveError::InvalidParameters(
181                        "Opening is missing for some share.".to_string(),
182                    )
183                })?;
184            reconstructed.push(share.reconstruct(my_openings.as_slice())?);
185        }
186
187        Ok(reconstructed.into())
188    }
189}
190
191impl<T: Reconstructible, S: Reconstructible> Reconstructible for (T, S) {
192    type Opening = (T::Opening, S::Opening);
193    type Secret = (T::Secret, S::Secret);
194
195    fn open_to(&self, peer_index: PeerIndex) -> Result<Self::Opening, PrimitiveError> {
196        Ok((self.0.open_to(peer_index)?, self.1.open_to(peer_index)?))
197    }
198
199    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening> {
200        let all_openings_t: Vec<_> = self.0.open_to_all_others().collect();
201        let all_openings_s: Vec<_> = self.1.open_to_all_others().collect();
202        izip!(all_openings_t, all_openings_s).map(|(o1, o2)| (o1, o2))
203    }
204
205    fn reconstruct(&self, openings: &[Self::Opening]) -> Result<Self::Secret, PrimitiveError> {
206        let (openings_t, openings_s): (Vec<_>, Vec<_>) = openings.iter().cloned().unzip();
207        Ok((
208            self.0.reconstruct(&openings_t)?,
209            self.1.reconstruct(&openings_s)?,
210        ))
211    }
212}
213
214// ---------------------------- //
215// ---- Random for N Peers ---- //
216// ---------------------------- //
217pub trait RandomAuthenticatedForNPeers<F: FieldExtension>:
218    RandomWith<Vec<Vec<GlobalFieldKey<F>>>>
219{
220    fn random_for_n_peers_with_alphas<Container: FromIterator<Self>>(
221        mut rng: impl CryptoRngCore,
222        n_parties: usize,
223        all_alphas: Vec<Vec<GlobalFieldKey<F>>>,
224    ) -> Container {
225        Self::random_n_with(&mut rng, n_parties, all_alphas)
226    }
227}
228
229impl<F: FieldExtension, S: RandomWith<Vec<Vec<GlobalFieldKey<F>>>>> RandomAuthenticatedForNPeers<F>
230    for S
231{
232}
233
234pub trait RandomAuthenticatedForNPeersWith<F: FieldExtension, T: Clone>:
235    RandomWith<(T, Vec<Vec<GlobalFieldKey<F>>>)>
236{
237    fn random_authenticated_for_n_peers_with<Container: FromIterator<Self>>(
238        mut rng: impl CryptoRngCore,
239        n_parties: usize,
240        value: T,
241        all_alphas: Vec<Vec<GlobalFieldKey<F>>>,
242    ) -> Container {
243        Self::random_n_with(&mut rng, n_parties, (value, all_alphas))
244    }
245}
246
247impl<F: FieldExtension, T: Clone, S: RandomWith<(T, Vec<Vec<GlobalFieldKey<F>>>)>>
248    RandomAuthenticatedForNPeersWith<F, T> for S
249{
250}
251
252/// A trait used to add a plaintext to a certain type. Commonly used to correct
253/// shares after a masked opening, or to add a public constant
254pub trait AddPlaintext: Reconstructible {
255    type AssociatedInformation: Clone + Send + Sync;
256
257    /// Add a plaintext to the share.
258    fn add_plaintext(&self, ptx: &Self::Secret, assoc: Self::AssociatedInformation) -> Self;
259
260    /// Add a plaintext to the share, consuming the share and returning a new one.
261    fn add_plaintext_owned(self, ptx: &Self::Secret, assoc: Self::AssociatedInformation) -> Self {
262        self.add_plaintext(ptx, assoc)
263    }
264}
265
266#[cfg(test)]
267mod tests {
268    use super::*;
269    use crate::{
270        algebra::elliptic_curve::Curve25519Ristretto,
271        random::Random,
272        sharing::ScalarShares,
273    };
274
275    #[test]
276    fn test_transpose_empty_matrix() {
277        let matrix: Vec<Vec<i32>> = vec![];
278        let result = transpose(matrix.clone());
279        assert_eq!(result, matrix);
280    }
281
282    #[test]
283    fn test_transpose_empty_rows() {
284        let matrix: Vec<Vec<i32>> = vec![];
285        let result = transpose(matrix.clone());
286        assert_eq!(result, matrix);
287    }
288
289    #[test]
290    fn test_transpose_single_element() {
291        let matrix = vec![vec![1]];
292        let result = transpose(matrix);
293        assert_eq!(result, vec![vec![1]]);
294    }
295
296    #[test]
297    fn test_transpose_single_row() {
298        let matrix = vec![vec![1, 2, 3]];
299        let result = transpose(matrix);
300        assert_eq!(result, vec![vec![1], vec![2], vec![3]]);
301    }
302
303    #[test]
304    fn test_transpose_single_column() {
305        let matrix = vec![vec![1], vec![2], vec![3]];
306        let result = transpose(matrix);
307        assert_eq!(result, vec![vec![1, 2, 3]]);
308    }
309
310    #[test]
311    fn test_transpose_square_matrix() {
312        let matrix = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
313        let result = transpose(matrix);
314        let expected = vec![vec![1, 4, 7], vec![2, 5, 8], vec![3, 6, 9]];
315        assert_eq!(result, expected);
316    }
317
318    #[test]
319    fn test_transpose_rectangular_matrix() {
320        let matrix = vec![vec![1, 2, 3, 4], vec![5, 6, 7, 8]];
321        let result = transpose(matrix);
322        let expected = vec![vec![1, 5], vec![2, 6], vec![3, 7], vec![4, 8]];
323        assert_eq!(result, expected);
324    }
325
326    #[test]
327    fn test_transpose_with_strings() {
328        let matrix = vec![vec!["a", "b"], vec!["c", "d"], vec!["e", "f"]];
329        let result = transpose(matrix);
330        let expected = vec![vec!["a", "c", "e"], vec!["b", "d", "f"]];
331        assert_eq!(result, expected);
332    }
333
334    #[test]
335    fn test_transpose_double_transpose() {
336        let matrix = vec![vec![1, 2, 3], vec![4, 5, 6]];
337        let result = transpose(transpose(matrix.clone()));
338        assert_eq!(result, matrix);
339    }
340
341    #[test]
342    fn test_reconstruct_vec() {
343        let n_parties = 3;
344        let mut rng = crate::random::test_rng();
345
346        let scalar_shares: Vec<_> =
347            ScalarShares::<Curve25519Ristretto, typenum::U5>::random_n(&mut rng, n_parties);
348        let scalar_shares = scalar_shares
349            .into_iter()
350            .map(|s| s.into_iter().collect::<Vec<_>>())
351            .collect::<Vec<_>>();
352
353        let reconstructed =
354            Vec::<ScalarShare<Curve25519Ristretto>>::reconstruct_all(&scalar_shares).unwrap();
355        let expected = (0..5)
356            .map(|i| {
357                ScalarShare::<Curve25519Ristretto>::reconstruct_all(
358                    &scalar_shares.iter().map(|v| &v[i]).collect::<Vec<_>>(),
359                )
360                .unwrap()
361            })
362            .collect::<Vec<_>>();
363        assert_eq!(reconstructed, expected);
364    }
365
366    #[test]
367    fn test_reconstruct_tuple() {
368        let n_parties = 3;
369        let mut rng = crate::random::test_rng();
370
371        let scalar_shares: Vec<_> =
372            ScalarShare::<Curve25519Ristretto>::random_n(&mut rng, n_parties);
373        let base_field_shares: Vec<_> =
374            BaseFieldShare::<Curve25519Ristretto>::random_n(&mut rng, n_parties);
375
376        let shares: Vec<(
377            ScalarShare<Curve25519Ristretto>,
378            BaseFieldShare<Curve25519Ristretto>,
379        )> = izip!(&scalar_shares, &base_field_shares)
380            .map(|(s, b)| (s.clone(), b.clone()))
381            .collect();
382
383        let reconstructed = <(
384            ScalarShare<Curve25519Ristretto>,
385            BaseFieldShare<Curve25519Ristretto>,
386        )>::reconstruct_all(&shares)
387        .unwrap();
388
389        assert_eq!(
390            reconstructed.0,
391            ScalarShare::<Curve25519Ristretto>::reconstruct_all(&scalar_shares).unwrap()
392        );
393        assert_eq!(
394            reconstructed.1,
395            BaseFieldShare::<Curve25519Ristretto>::reconstruct_all(&base_field_shares).unwrap()
396        );
397    }
398}