primitives/sharing/
mod.rs

1// Secret sharing schemes.
2pub mod authenticated;
3pub mod unauthenticated;
4
5use std::borrow::Borrow;
6
7pub use authenticated::*;
8use itertools::enumerate;
9use serde::{de::DeserializeOwned, Serialize};
10
11use crate::{
12    algebra::{field::FieldExtension, ops::transpose::transpose},
13    errors::{PrimitiveError, VerificationError},
14    random::{CryptoRngCore, RandomWith},
15    types::PeerIndex,
16    utils::TakeExact,
17};
18
19// ------------------------- //
20// ---- Reconstructible ---- //
21// ------------------------- //
22/// Reconstructs a secret from a collection of openings and a local share.
23pub trait Reconstructible: Sized {
24    /// The type that is sent to / received from other peers.
25    type Opening: Serialize + Clone + DeserializeOwned + Send + Sync;
26    /// The type of the reconstructed value.
27    type Secret: Serialize + DeserializeOwned + PartialEq + Send + Sync;
28
29    /// Open the share towards another peer.
30    fn open_to(&self, peer_index: PeerIndex) -> Result<Self::Opening, PrimitiveError>;
31
32    /// Open the share towards all other peers. Returns an iterator with either one opening for
33    /// each peer or a single opening for all peers.
34    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening>;
35
36    /// Reconstruct a secret from openings coming from all other parties.
37    fn reconstruct(&self, openings: &[Self::Opening]) -> Result<Self::Secret, PrimitiveError>;
38
39    /// Reconstruct a secret from a collection of shares, by opening each share
40    /// towards all other peers, reconstructing `n` secrets from the openings and
41    /// checking that they are all equal.
42    fn reconstruct_all<T: Borrow<Self>>(shares: &[T]) -> Result<Self::Secret, PrimitiveError> {
43        let n_parties = shares.len();
44        if n_parties < 2 {
45            return Err(PrimitiveError::InvalidParameters(
46                "At least two shares are required for reconstruction.".to_string(),
47            ));
48        }
49        // Open each share to all other peers.
50        let mut all_openings = shares
51            .iter()
52            .map(|share| share.borrow().open_to_all_others())
53            .collect::<Vec<_>>();
54        // Reconstruct each secret.
55        enumerate(shares.iter())
56            .map(|(i, share)| {
57                let my_openings = enumerate(all_openings.iter_mut())
58                    .take_exact(n_parties)
59                    .filter(|(j, _)| i != *j)
60                    .map(|(_, opening)| opening.next())
61                    .collect::<Option<Vec<_>>>()
62                    .ok_or(VerificationError::MissingOpening(i))?;
63                share.borrow().reconstruct(my_openings.as_slice())
64            })
65            // Check that all reconstructed secrets are equal.
66            .reduce(|previous, current| match (previous, current) {
67                (Ok(prev), Ok(curr)) => match prev == curr {
68                    true => Ok(prev),
69                    false => Err(VerificationError::OpeningMismatch(
70                        serde_json::to_string(&prev).unwrap(),
71                        serde_json::to_string(&curr).unwrap(),
72                    )
73                    .into()),
74                },
75                (Err(e), _) | (_, Err(e)) => Err(e),
76            })
77            .unwrap() // Safe because `shares.len() >= 2`
78    }
79}
80
81impl<T: Reconstructible<Opening: Clone>> Reconstructible for Vec<T> {
82    type Opening = Vec<T::Opening>;
83    type Secret = Vec<T::Secret>;
84
85    fn open_to(&self, peer_index: PeerIndex) -> Result<Self::Opening, PrimitiveError> {
86        self.iter().map(|share| share.open_to(peer_index)).collect()
87    }
88
89    fn open_to_all_others(&self) -> impl ExactSizeIterator<Item = Self::Opening> {
90        let all_openings: Vec<Vec<_>> = self
91            .iter()
92            .map(|share| share.open_to_all_others().collect())
93            .collect();
94
95        transpose(all_openings).into_iter()
96    }
97
98    fn reconstruct(&self, openings: &[Self::Opening]) -> Result<Self::Secret, PrimitiveError> {
99        if openings.len() != self.len() {
100            return Err(PrimitiveError::InvalidParameters(
101                "Number of openings must match number of shares.".to_string(),
102            ));
103        }
104
105        // Iterate over all the i-th elements of each entry of openings
106        let mut reconstructed = Vec::with_capacity(self.len());
107        for (i, share) in self.iter().enumerate() {
108            let my_openings: Vec<_> = openings
109                .iter()
110                .map(|opening| opening.get(i).cloned())
111                .collect::<Option<Vec<_>>>()
112                .ok_or(PrimitiveError::InvalidParameters(
113                    "Opening is missing for some share.".to_string(),
114                ))?;
115            reconstructed.push(share.reconstruct(my_openings.as_slice())?);
116        }
117        Ok(reconstructed)
118    }
119}
120
121// ---------------------------- //
122// ---- Random for N Peers ---- //
123// ---------------------------- //
124pub trait RandomAuthenticatedForNPeers<F: FieldExtension>:
125    RandomWith<Vec<Vec<GlobalFieldKey<F>>>>
126{
127    fn random_for_n_peers_with_alphas<Container: FromIterator<Self>>(
128        mut rng: impl CryptoRngCore,
129        n_parties: usize,
130        all_alphas: Vec<Vec<GlobalFieldKey<F>>>,
131    ) -> Container {
132        Self::random_n_with(&mut rng, n_parties, all_alphas)
133    }
134}
135
136impl<F: FieldExtension, S: RandomWith<Vec<Vec<GlobalFieldKey<F>>>>> RandomAuthenticatedForNPeers<F>
137    for S
138{
139}
140
141pub trait RandomAuthenticatedForNPeersWith<F: FieldExtension, T: Clone>:
142    RandomWith<(T, Vec<Vec<GlobalFieldKey<F>>>)>
143{
144    fn random_authenticated_for_n_peers_with<Container: FromIterator<Self>>(
145        mut rng: impl CryptoRngCore,
146        n_parties: usize,
147        value: T,
148        all_alphas: Vec<Vec<GlobalFieldKey<F>>>,
149    ) -> Container {
150        Self::random_n_with(&mut rng, n_parties, (value, all_alphas))
151    }
152}
153
154impl<F: FieldExtension, T: Clone, S: RandomWith<(T, Vec<Vec<GlobalFieldKey<F>>>)>>
155    RandomAuthenticatedForNPeersWith<F, T> for S
156{
157}
158
159/// A trait used to add a plaintext secret to its secret-shared (reconstructible) form.
160pub trait AddPlaintext: Reconstructible {
161    /// Helper information to be used when adding a constant to the share. For example in BDOZ
162    /// shares, you need to know whether the local peer is the first peer or not.
163    type AssociatedInformation: Clone + Send + Sync;
164    /// Add a constant to the share.
165    fn add_plaintext(&self, plaintext: &Self::Secret, assoc: Self::AssociatedInformation) -> Self;
166
167    /// Add a constant to the share, consuming the share and returning a new one.
168    fn add_plaintext_owned(
169        self,
170        plaintext: &Self::Secret,
171        assoc: Self::AssociatedInformation,
172    ) -> Self {
173        self.add_plaintext(plaintext, assoc)
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180
181    #[test]
182    fn test_transpose_empty_matrix() {
183        let matrix: Vec<Vec<i32>> = vec![];
184        let result = transpose(matrix.clone());
185        assert_eq!(result, matrix);
186    }
187
188    #[test]
189    fn test_transpose_empty_rows() {
190        let matrix: Vec<Vec<i32>> = vec![];
191        let result = transpose(matrix.clone());
192        assert_eq!(result, matrix);
193    }
194
195    #[test]
196    fn test_transpose_single_element() {
197        let matrix = vec![vec![1]];
198        let result = transpose(matrix);
199        assert_eq!(result, vec![vec![1]]);
200    }
201
202    #[test]
203    fn test_transpose_single_row() {
204        let matrix = vec![vec![1, 2, 3]];
205        let result = transpose(matrix);
206        assert_eq!(result, vec![vec![1], vec![2], vec![3]]);
207    }
208
209    #[test]
210    fn test_transpose_single_column() {
211        let matrix = vec![vec![1], vec![2], vec![3]];
212        let result = transpose(matrix);
213        assert_eq!(result, vec![vec![1, 2, 3]]);
214    }
215
216    #[test]
217    fn test_transpose_square_matrix() {
218        let matrix = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
219        let result = transpose(matrix);
220        let expected = vec![vec![1, 4, 7], vec![2, 5, 8], vec![3, 6, 9]];
221        assert_eq!(result, expected);
222    }
223
224    #[test]
225    fn test_transpose_rectangular_matrix() {
226        let matrix = vec![vec![1, 2, 3, 4], vec![5, 6, 7, 8]];
227        let result = transpose(matrix);
228        let expected = vec![vec![1, 5], vec![2, 6], vec![3, 7], vec![4, 8]];
229        assert_eq!(result, expected);
230    }
231
232    #[test]
233    fn test_transpose_with_strings() {
234        let matrix = vec![vec!["a", "b"], vec!["c", "d"], vec!["e", "f"]];
235        let result = transpose(matrix);
236        let expected = vec![vec!["a", "c", "e"], vec!["b", "d", "f"]];
237        assert_eq!(result, expected);
238    }
239
240    #[test]
241    fn test_transpose_double_transpose() {
242        let matrix = vec![vec![1, 2, 3], vec![4, 5, 6]];
243        let result = transpose(transpose(matrix.clone()));
244        assert_eq!(result, matrix);
245    }
246}