blahaj/
lib.rs

1//! Fast, small and secure [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) library crate
2//!
3//! Usage example (std):
4//! ```
5//! use blahaj::{ Sharks, Share };
6//!
7//! // Set a minimum threshold of 10 shares
8//! let sharks = Sharks(10);
9//! // Obtain an iterator over the shares for secret [1, 2, 3, 4]
10//! # #[cfg(feature = "std")]
11//! # {
12//! let dealer = sharks.dealer(&[1, 2, 3, 4]);
13//! // Get 10 shares
14//! let shares: Vec<Share> = dealer.take(10).collect();
15//! // Recover the original secret!
16//! let secret = sharks.recover(shares.as_slice()).unwrap();
17//! assert_eq!(secret, vec![1, 2, 3, 4]);
18//! # }
19//! ```
20//!
21//! Usage example (no std):
22//! ```
23//! use blahaj::{ Sharks, Share };
24//! use rand_chacha::rand_core::SeedableRng;
25//!
26//! // Set a minimum threshold of 10 shares
27//! let sharks = Sharks(10);
28//! // Obtain an iterator over the shares for secret [1, 2, 3, 4]
29//! let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]);
30//! let dealer = sharks.dealer_rng(&[1, 2, 3, 4], &mut rng);
31//! // Get 10 shares
32//! let shares: Vec<Share> = dealer.take(10).collect();
33//! // Recover the original secret!
34//! let secret = sharks.recover(shares.as_slice()).unwrap();
35//! assert_eq!(secret, vec![1, 2, 3, 4]);
36//! ```
37#![cfg_attr(not(feature = "std"), no_std)]
38
39mod field;
40mod math;
41mod share;
42
43extern crate alloc;
44
45use alloc::vec::Vec;
46use hashbrown::HashSet;
47
48use field::GF256;
49pub use share::Share;
50
51/// Tuple struct which implements methods to generate shares and recover secrets over a 256 bits Galois Field.
52/// Its only parameter is the minimum shares threshold.
53///
54/// Usage example:
55/// ```
56/// # use blahaj::{ Sharks, Share };
57/// // Set a minimum threshold of 10 shares
58/// let sharks = Sharks(10);
59/// // Obtain an iterator over the shares for secret [1, 2, 3, 4]
60/// # #[cfg(feature = "std")]
61/// # {
62/// let dealer = sharks.dealer(&[1, 2, 3, 4]);
63/// // Get 10 shares
64/// let shares: Vec<Share> = dealer.take(10).collect();
65/// // Recover the original secret!
66/// let secret = sharks.recover(shares.as_slice()).unwrap();
67/// assert_eq!(secret, vec![1, 2, 3, 4]);
68/// # }
69/// ```
70pub struct Sharks(pub u8);
71
72impl Sharks {
73    /// This method is useful when `std` is not available. For typical usage
74    /// see the `dealer` method.
75    ///
76    /// Given a `secret` byte slice, returns an `Iterator` along new shares.
77    /// The maximum number of shares that can be generated is 256.
78    /// A random number generator has to be provided.
79    ///
80    /// Example:
81    /// ```
82    /// # use blahaj::{ Sharks, Share };
83    /// # use rand_chacha::rand_core::SeedableRng;
84    /// # let sharks = Sharks(3);
85    /// // Obtain an iterator over the shares for secret [1, 2]
86    /// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]);
87    /// let dealer = sharks.dealer_rng(&[1, 2], &mut rng);
88    /// // Get 3 shares
89    /// let shares: Vec<Share> = dealer.take(3).collect();
90    pub fn dealer_rng<R: rand::Rng>(
91        &self,
92        secret: &[u8],
93        rng: &mut R,
94    ) -> impl Iterator<Item = Share> {
95        let mut polys = Vec::with_capacity(secret.len());
96
97        for chunk in secret {
98            polys.push(math::random_polynomial(GF256(*chunk), self.0, rng))
99        }
100
101        math::get_evaluator(polys)
102    }
103
104    /// Given a `secret` byte slice, returns an `Iterator` along new shares.
105    /// The maximum number of shares that can be generated is 256.
106    ///
107    /// Example:
108    /// ```
109    /// # use blahaj::{ Sharks, Share };
110    /// # let sharks = Sharks(3);
111    /// // Obtain an iterator over the shares for secret [1, 2]
112    /// let dealer = sharks.dealer(&[1, 2]);
113    /// // Get 3 shares
114    /// let shares: Vec<Share> = dealer.take(3).collect();
115    #[cfg(feature = "std")]
116    pub fn dealer(&self, secret: &[u8]) -> impl Iterator<Item = Share> {
117        let mut rng = rand::thread_rng();
118        self.dealer_rng(secret, &mut rng)
119    }
120
121    /// Given an iterable collection of shares, recovers the original secret.
122    /// If the number of distinct shares is less than the minimum threshold an `Err` is returned,
123    /// otherwise an `Ok` containing the secret.
124    ///
125    /// Example:
126    /// ```
127    /// # use blahaj::{ Sharks, Share };
128    /// # use rand_chacha::rand_core::SeedableRng;
129    /// # let sharks = Sharks(3);
130    /// # let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]);
131    /// # let mut shares: Vec<Share> = sharks.dealer_rng(&[1], &mut rng).take(3).collect();
132    /// // Recover original secret from shares
133    /// let mut secret = sharks.recover(&shares);
134    /// // Secret correctly recovered
135    /// assert!(secret.is_ok());
136    /// // Remove shares for demonstration purposes
137    /// shares.clear();
138    /// secret = sharks.recover(&shares);
139    /// // Not enough shares to recover secret
140    /// assert!(secret.is_err());
141    pub fn recover<'a, T>(&self, shares: T) -> Result<Vec<u8>, &str>
142    where
143        T: IntoIterator<Item = &'a Share>,
144        T::IntoIter: Iterator<Item = &'a Share>,
145    {
146        let mut share_length: Option<usize> = None;
147        let mut keys: HashSet<u8> = HashSet::new();
148        let mut values: Vec<Share> = Vec::new();
149
150        for share in shares.into_iter() {
151            if share_length.is_none() {
152                share_length = Some(share.y.len());
153            }
154
155            if Some(share.y.len()) != share_length {
156                return Err("All shares must have the same length");
157            } else {
158                keys.insert(share.x.0);
159                values.push(share.clone());
160            }
161        }
162
163        if keys.is_empty() || (keys.len() < self.0 as usize) {
164            Err("Not enough shares to recover original secret")
165        } else {
166            Ok(math::interpolate(values.as_slice()))
167        }
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::{Share, Sharks};
174    use alloc::{vec, vec::Vec};
175
176    impl Sharks {
177        #[cfg(not(feature = "std"))]
178        fn make_shares(&self, secret: &[u8]) -> impl Iterator<Item = Share> {
179            use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
180
181            let mut rng = ChaCha8Rng::from_seed([0x90; 32]);
182            self.dealer_rng(secret, &mut rng)
183        }
184
185        #[cfg(feature = "std")]
186        fn make_shares(&self, secret: &[u8]) -> impl Iterator<Item = Share> {
187            self.dealer(secret)
188        }
189    }
190
191    #[test]
192    fn test_insufficient_shares_err() {
193        let sharks = Sharks(255);
194        let shares: Vec<Share> = sharks.make_shares(&[1]).take(254).collect();
195        let secret = sharks.recover(&shares);
196        assert!(secret.is_err());
197    }
198
199    #[test]
200    fn test_duplicate_shares_err() {
201        let sharks = Sharks(255);
202        let mut shares: Vec<Share> = sharks.make_shares(&[1]).take(255).collect();
203        shares[1] = Share {
204            x: shares[0].x.clone(),
205            y: shares[0].y.clone(),
206        };
207        let secret = sharks.recover(&shares);
208        assert!(secret.is_err());
209    }
210
211    #[test]
212    fn test_integration_works() {
213        let sharks = Sharks(255);
214        let shares: Vec<Share> = sharks.make_shares(&[1, 2, 3, 4]).take(255).collect();
215        let secret = sharks.recover(&shares).unwrap();
216        assert_eq!(secret, vec![1, 2, 3, 4]);
217    }
218}