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}