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