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::ops::transpose::transpose,
14    errors::PrimitiveError,
15    types::PeerIndex,
16    utils::TakeExact,
17};
18
19// ------------------------- //
20// ---- Reconstructible ---- //
21// ------------------------- //
22/// Reconstructs a secret-shared value from a collection of openings and a local share.
23pub trait Reconstructible: Sized {
24    /// The type of the reconstructed value.
25    type Value: Serialize
26        + DeserializeOwned
27        + for<'de> SchemaRead<'de, Dst = Self::Value>
28        + SchemaWrite<Src = Self::Value>
29        + Clone
30        + PartialEq
31        + Send
32        + Sync
33        + 'static;
34    /// The type that is sent to / received from other peers.
35    type Opening: Serialize
36        + DeserializeOwned
37        + for<'de> SchemaRead<'de, Dst = Self::Opening>
38        + SchemaWrite<Src = Self::Opening>
39        + Clone
40        + Send
41        + Sync
42        + 'static;
43
44    /// Open the share towards another peer.
45    fn open_to(&self, peer_index: PeerIndex) -> Result<Self::Opening, PrimitiveError>;
46
47    /// Open the share towards all other peers. Returns an iterator with either one opening for
48    /// each peer or a single opening for all peers.
49    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening>;
50
51    /// Reconstruct a secret from openings coming from all other parties.
52    fn reconstruct(&self, openings: Vec<Self::Opening>) -> Result<Self::Value, PrimitiveError>;
53
54    /// Reconstruct a secret from a collection of shares, by opening each share
55    /// towards all other peers, reconstructing `n` secrets from the openings and
56    /// checking that they are all equal.
57    fn reconstruct_all<T: Borrow<Self>>(shares: Vec<T>) -> Result<Self::Value, PrimitiveError> {
58        let n_parties = shares.len();
59        if n_parties < 2 {
60            return Err(PrimitiveError::MinimumLength(2, n_parties));
61        }
62        // Open each share to all other peers.
63        let mut all_openings = shares
64            .iter()
65            .map(|share| share.borrow().open_to_all_others())
66            .collect::<Vec<_>>();
67        // Reconstruct each secret.
68        enumerate(shares.iter())
69            .map(|(i, share)| {
70                let my_openings = enumerate(all_openings.iter_mut())
71                    .take_exact(n_parties)
72                    .filter(|(j, _)| i != *j)
73                    .map(|(_, opening)| opening.next())
74                    .collect::<Option<Vec<_>>>()
75                    .ok_or_else(|| PrimitiveError::InvalidPeerIndex(i, shares.len() - 1))?;
76                share.borrow().reconstruct(my_openings)
77            })
78            // Check that all reconstructed secrets are equal.
79            .reduce(|previous, current| match (previous, current) {
80                (Ok(prev), Ok(curr)) => match prev == curr {
81                    true => Ok(prev),
82                    false => Err(PrimitiveError::WrongOpening(
83                        serde_json::to_string(&prev).unwrap(),
84                        serde_json::to_string(&curr).unwrap(),
85                    )),
86                },
87                (Err(e), _) | (_, Err(e)) => Err(e),
88            })
89            .unwrap() // Safe because `shares.len() >= 2`
90    }
91}
92
93impl<T: Reconstructible<Opening: Clone>> Reconstructible for Vec<T> {
94    type Opening = Vec<T::Opening>;
95    type Value = Vec<T::Value>;
96
97    fn open_to(&self, peer_index: PeerIndex) -> Result<Self::Opening, PrimitiveError> {
98        self.iter().map(|share| share.open_to(peer_index)).collect()
99    }
100
101    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening> {
102        let all_openings: Vec<Vec<_>> = self
103            .iter()
104            .map(|share| share.open_to_all_others().collect())
105            .collect();
106
107        transpose(all_openings).into_iter()
108    }
109
110    fn reconstruct(&self, openings: Vec<Self::Opening>) -> Result<Self::Value, PrimitiveError> {
111        if openings.is_empty() {
112            return Err(PrimitiveError::MinimumLength(1, 0));
113        }
114
115        if openings[0].len() != self.len() {
116            return Err(PrimitiveError::InvalidParameters(
117                "Number of openings must match number of shares.".to_string(),
118            ));
119        }
120
121        // Iterate over all the i-th elements of each entry of openings
122        let mut reconstructed = Vec::with_capacity(self.len());
123        for (i, share) in self.iter().enumerate() {
124            let my_openings: Vec<_> = openings
125                .iter()
126                .map(|opening| opening.get(i).cloned())
127                .collect::<Option<Vec<_>>>()
128                .ok_or_else(|| {
129                    PrimitiveError::InvalidParameters(
130                        "Opening is missing for some share.".to_string(),
131                    )
132                })?;
133            reconstructed.push(share.reconstruct(my_openings)?);
134        }
135        Ok(reconstructed)
136    }
137}
138
139impl<T: Reconstructible<Opening: Clone>> Reconstructible for Arc<[T]> {
140    type Opening = Arc<[T::Opening]>;
141    type Value = Arc<[T::Value]>;
142
143    fn open_to(&self, peer_index: PeerIndex) -> Result<Self::Opening, PrimitiveError> {
144        self.iter().map(|share| share.open_to(peer_index)).collect()
145    }
146
147    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening> {
148        let all_openings: Vec<Vec<_>> = self
149            .iter()
150            .map(|share| share.open_to_all_others().collect())
151            .collect();
152
153        transpose(all_openings)
154            .into_iter()
155            .map(Arc::from)
156            .collect::<Vec<_>>()
157            .into_iter()
158    }
159
160    fn reconstruct(&self, openings: Vec<Self::Opening>) -> Result<Self::Value, PrimitiveError> {
161        if openings.is_empty() {
162            return Err(PrimitiveError::MinimumLength(1, 0));
163        }
164
165        if openings[0].len() != self.len() {
166            return Err(PrimitiveError::InvalidParameters(
167                "Number of openings must match number of shares.".to_string(),
168            ));
169        }
170
171        // Iterate over all the i-th elements of each entry of openings
172        let mut reconstructed = Vec::with_capacity(self.len());
173        for (i, share) in self.iter().enumerate() {
174            let my_openings: Vec<_> = openings
175                .iter()
176                .map(|opening| opening.get(i).cloned())
177                .collect::<Option<Vec<_>>>()
178                .ok_or_else(|| {
179                    PrimitiveError::InvalidParameters(
180                        "Opening is missing for some share.".to_string(),
181                    )
182                })?;
183            reconstructed.push(share.reconstruct(my_openings)?);
184        }
185
186        Ok(reconstructed.into())
187    }
188}
189
190impl<T: Reconstructible, S: Reconstructible> Reconstructible for (T, S) {
191    type Opening = (T::Opening, S::Opening);
192    type Value = (T::Value, S::Value);
193
194    fn open_to(&self, peer_index: PeerIndex) -> Result<Self::Opening, PrimitiveError> {
195        Ok((self.0.open_to(peer_index)?, self.1.open_to(peer_index)?))
196    }
197
198    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening> {
199        let all_openings_t: Vec<_> = self.0.open_to_all_others().collect();
200        let all_openings_s: Vec<_> = self.1.open_to_all_others().collect();
201        izip!(all_openings_t, all_openings_s).map(|(o1, o2)| (o1, o2))
202    }
203
204    fn reconstruct(&self, openings: Vec<Self::Opening>) -> Result<Self::Value, PrimitiveError> {
205        let (openings_t, openings_s): (Vec<_>, Vec<_>) = openings.into_iter().unzip();
206        Ok((
207            self.0.reconstruct(openings_t)?,
208            self.1.reconstruct(openings_s)?,
209        ))
210    }
211}
212
213/// A trait used to add a plaintext to a certain type. Commonly used to correct
214/// shares after a masked opening, or to add a public constant
215pub trait PlaintextOps: Reconstructible + Clone {
216    /// Add a plaintext to the share, consuming the share and returning a new one.
217    fn add_plaintext(self, ptx: &Self::Value, is_first_peer: bool) -> Self;
218
219    /// Subtract a plaintext from the share, consuming the share and returning a new one.
220    fn sub_plaintext(self, ptx: &Self::Value, is_first_peer: bool) -> Self;
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226    use crate::{
227        algebra::elliptic_curve::Curve25519Ristretto,
228        random::Random,
229        sharing::ScalarShares,
230    };
231
232    #[test]
233    fn test_reconstruct_vec() {
234        let n_parties = 3;
235        let mut rng = crate::random::test_rng();
236
237        let scalar_shares: Vec<_> =
238            ScalarShares::<Curve25519Ristretto, typenum::U5>::random_n(&mut rng, n_parties);
239        let scalar_shares = scalar_shares
240            .into_iter()
241            .map(|s| s.into_iter().collect::<Vec<_>>())
242            .collect::<Vec<_>>();
243
244        let reconstructed =
245            Vec::<ScalarShare<Curve25519Ristretto>>::reconstruct_all(scalar_shares.clone())
246                .unwrap();
247        let expected = (0..5)
248            .map(|i| {
249                ScalarShare::<Curve25519Ristretto>::reconstruct_all(
250                    scalar_shares.iter().map(|v| v[i].clone()).collect(),
251                )
252                .unwrap()
253            })
254            .collect::<Vec<_>>();
255        assert_eq!(reconstructed, expected);
256    }
257
258    #[test]
259    fn test_reconstruct_tuple() {
260        let n_parties = 3;
261        let mut rng = crate::random::test_rng();
262
263        let scalar_shares: Vec<_> =
264            ScalarShare::<Curve25519Ristretto>::random_n(&mut rng, n_parties);
265        let base_field_shares: Vec<_> =
266            BaseFieldShare::<Curve25519Ristretto>::random_n(&mut rng, n_parties);
267
268        let shares: Vec<(
269            ScalarShare<Curve25519Ristretto>,
270            BaseFieldShare<Curve25519Ristretto>,
271        )> = izip!(&scalar_shares, &base_field_shares)
272            .map(|(s, b)| (s.clone(), b.clone()))
273            .collect();
274
275        let reconstructed = <(
276            ScalarShare<Curve25519Ristretto>,
277            BaseFieldShare<Curve25519Ristretto>,
278        )>::reconstruct_all(shares)
279        .unwrap();
280
281        assert_eq!(
282            reconstructed.0,
283            ScalarShare::<Curve25519Ristretto>::reconstruct_all(scalar_shares).unwrap()
284        );
285        assert_eq!(
286            reconstructed.1,
287            BaseFieldShare::<Curve25519Ristretto>::reconstruct_all(base_field_shares).unwrap()
288        );
289    }
290}