star_sharks/
lib.rs

1//! Fast, small and secure [Shamir's Secret
2//! Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing)
3//! library crate for large finite fields
4#![cfg_attr(not(feature = "std"), no_std)]
5
6extern crate alloc;
7
8// implement operations using a larger finite field
9extern crate ff;
10mod share_ff;
11
12use alloc::collections::BTreeSet;
13use alloc::vec::Vec;
14use core::convert::TryInto;
15
16use crate::ff::PrimeField;
17pub use share_ff::Evaluator;
18pub use share_ff::Share;
19pub use share_ff::{get_evaluator, interpolate, random_polynomial};
20pub use share_ff::{Fp, FpRepr, FIELD_ELEMENT_LEN};
21
22/// Tuple struct which implements methods to generate shares
23/// and recover secrets over a finite field.
24/// Its only parameter is the minimum shares threshold.
25pub struct Sharks(pub u32);
26
27impl Sharks {
28  /// This method is useful when `std` is not available. For typical usage
29  /// see the `dealer` method.
30  ///
31  /// Given a `secret` byte slice, returns an `Iterator` along new shares.
32  /// A random number generator has to be provided.
33  ///
34  /// Example:
35  /// ```
36  /// # use star_sharks::{ Sharks, Share };
37  /// # use rand_chacha::rand_core::SeedableRng;
38  /// # let sharks = Sharks(3);
39  /// // Obtain an iterator over the shares for secret [1, 2]
40  /// let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]);
41  /// let dealer = sharks.dealer_rng(&[1, 2], &mut rng).unwrap();
42  /// // Get 3 shares
43  /// let shares: Vec<Share> = dealer.take(3).collect();
44  pub fn dealer_rng<R: rand::Rng>(
45    &self,
46    secret: &[u8],
47    rng: &mut R,
48  ) -> Result<Evaluator, &str> {
49    let mut polys = Vec::with_capacity(secret.len());
50
51    let secret_fp_len = secret.len() / FIELD_ELEMENT_LEN;
52    for i in 0..secret_fp_len {
53      let element = Fp::from_repr(FpRepr(
54        secret[i * FIELD_ELEMENT_LEN..(i + 1) * FIELD_ELEMENT_LEN]
55          .try_into()
56          .expect("bad chunk"),
57      ));
58      if element.is_none().into() {
59        return Err("Failed to create field element from secret");
60      }
61      let element = element.unwrap();
62      polys.push(random_polynomial(element, self.0, rng));
63    }
64
65    Ok(get_evaluator(polys))
66  }
67
68  /// Given a `secret` byte slice, returns an `Iterator` along new shares.
69  ///
70  /// Example:
71  /// ```
72  /// # use star_sharks::{ Sharks, Share };
73  /// # let sharks = Sharks(3);
74  /// // Obtain an iterator over the shares for secret [1, 2]
75  /// let dealer = sharks.dealer(&[1, 2]).unwrap();
76  /// // Get 3 shares
77  /// let shares: Vec<Share> = dealer.take(3).collect();
78  #[cfg(feature = "std")]
79  pub fn dealer(&self, secret: &[u8]) -> Result<Evaluator, &str> {
80    let mut rng = rand::thread_rng();
81    self.dealer_rng(secret, &mut rng)
82  }
83
84  /// Given an iterable collection of shares, recovers the original secret.
85  /// If the number of distinct shares is less than the minimum threshold an `Err` is returned,
86  /// otherwise an `Ok` containing the secret.
87  ///
88  /// Example:
89  /// ```
90  /// # use star_sharks::{ Sharks, Share };
91  /// # use rand_chacha::rand_core::SeedableRng;
92  /// # let sharks = Sharks(3);
93  /// # let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]);
94  /// # let dealer = sharks.dealer_rng(&[1], &mut rng).unwrap();
95  /// # let mut shares: Vec<Share> = dealer.take(3).collect();
96  /// // Recover original secret from shares
97  /// let secret = sharks.recover(&shares);
98  /// // Secret correctly recovered
99  /// assert!(secret.is_ok());
100  /// // Remove shares for demonstration purposes
101  /// shares.clear();
102  /// let secret = sharks.recover(&shares);
103  /// // Not enough shares to recover secret
104  /// assert!(secret.is_err());
105  pub fn recover<'a, T>(&self, shares: T) -> Result<Vec<u8>, &str>
106  where
107    T: IntoIterator<Item = &'a Share>,
108    T::IntoIter: Iterator<Item = &'a Share>,
109  {
110    let mut share_length: Option<usize> = None;
111    let mut keys: BTreeSet<Vec<u8>> = BTreeSet::new();
112    let mut values: Vec<Share> = Vec::new();
113
114    for share in shares.into_iter() {
115      if share_length.is_none() {
116        share_length = Some(share.y.len());
117      }
118
119      if Some(share.y.len()) != share_length {
120        return Err("All shares must have the same length");
121      } else if keys.insert(share.x.to_repr().as_ref().to_vec()) {
122        values.push(share.clone());
123      }
124    }
125
126    if keys.is_empty() || (keys.len() < self.0 as usize) {
127      Err("Not enough shares to recover original secret")
128    } else {
129      // We only need the threshold number of shares to recover
130      interpolate(&values[0..self.0 as usize])
131    }
132  }
133}
134
135#[cfg(test)]
136mod tests {
137  use super::{Fp, Share, Sharks};
138  use crate::ff::{Field, PrimeField};
139  use crate::FIELD_ELEMENT_LEN;
140  use alloc::{vec, vec::Vec};
141  use core::convert::TryFrom;
142
143  impl Sharks {
144    #[cfg(not(feature = "std"))]
145    fn make_shares(
146      &self,
147      secret: &[u8],
148    ) -> Result<impl Iterator<Item = Share>, &str> {
149      use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
150      // CAUTION: Fixed seed for no-std testing. Don't copy this code!
151      let mut rng = ChaCha8Rng::from_seed([0x90; 32]);
152      self.dealer_rng(secret, &mut rng)
153    }
154
155    #[cfg(feature = "std")]
156    fn make_shares(
157      &self,
158      secret: &[u8],
159    ) -> Result<impl Iterator<Item = Share>, &str> {
160      self.dealer(secret)
161    }
162  }
163
164  fn fp_one() -> Fp {
165    Fp::ONE
166  }
167
168  fn fp_two() -> Fp {
169    fp_one().double()
170  }
171
172  fn fp_one_repr() -> Vec<u8> {
173    fp_one().to_repr().as_ref().to_vec()
174  }
175
176  fn fp_two_repr() -> Vec<u8> {
177    (fp_one().double()).to_repr().as_ref().to_vec()
178  }
179
180  fn fp_three_repr() -> Vec<u8> {
181    (fp_two() + fp_one()).to_repr().as_ref().to_vec()
182  }
183
184  fn fp_four_repr() -> Vec<u8> {
185    (fp_two() + fp_two()).to_repr().as_ref().to_vec()
186  }
187
188  #[test]
189  fn insufficient_shares() {
190    let sharks = Sharks(500);
191    let shares: Vec<Share> = sharks
192      .make_shares(&fp_one_repr())
193      .unwrap()
194      .take(499)
195      .collect();
196    let secret = sharks.recover(&shares);
197    assert!(secret.is_err());
198  }
199
200  #[test]
201  fn duplicate_shares() {
202    let sharks = Sharks(500);
203    let mut shares: Vec<Share> = sharks
204      .make_shares(&fp_one_repr())
205      .unwrap()
206      .take(500)
207      .collect();
208    shares[1] = Share {
209      x: shares[0].x,
210      y: shares[0].y.clone(),
211    };
212    let secret = sharks.recover(&shares);
213    assert!(secret.is_err());
214  }
215
216  #[test]
217  fn integration() {
218    let sharks = Sharks(500);
219    let mut input = Vec::new();
220    input.extend(fp_one_repr());
221    input.extend(fp_two_repr());
222    input.extend(fp_three_repr());
223    input.extend(fp_four_repr());
224    let shares: Vec<Share> =
225      sharks.make_shares(&input).unwrap().take(500).collect();
226    let secret = sharks.recover(&shares).unwrap();
227    assert_eq!(secret, test_bytes());
228  }
229
230  #[test]
231  #[cfg(feature = "std")]
232  fn integration_random() {
233    let sharks = Sharks(40);
234    let mut rng = rand::thread_rng();
235    let mut input = Vec::new();
236    input.extend(fp_one_repr());
237    input.extend(fp_two_repr());
238    input.extend(fp_three_repr());
239    input.extend(fp_four_repr());
240    let evaluator = sharks.dealer(&input).unwrap();
241    let shares: Vec<Share> =
242      core::iter::repeat_with(|| evaluator.gen(&mut rng))
243        .take(55)
244        .collect();
245    let secret = sharks.recover(&shares).unwrap();
246    assert_eq!(secret, test_bytes());
247  }
248
249  fn test_bytes() -> Vec<u8> {
250    let suffix = vec![0u8; FIELD_ELEMENT_LEN - 1];
251    let mut bytes = vec![1u8; 1];
252    bytes.extend(suffix.clone()); // x coord
253    bytes.extend(vec![2u8; 1]);
254    bytes.extend(suffix.clone()); // y coord #1
255    bytes.extend(vec![3u8; 1]);
256    bytes.extend(suffix.clone()); // y coord #2
257    bytes.extend(vec![4u8; 1]);
258    bytes.extend(suffix); // y coord #3
259    bytes
260  }
261
262  #[test]
263  fn zero_threshold() {
264    let sharks = Sharks(0);
265    let testcase = Share::try_from(test_bytes().as_slice()).unwrap();
266    let secret = sharks.recover(&vec![testcase]);
267    assert!(secret.is_err());
268  }
269
270  #[test]
271  #[cfg(feature = "std")]
272  fn dealer_short_secret() {
273    let sharks = Sharks(2);
274
275    // one less byte than needed
276    let secret = [0u8; FIELD_ELEMENT_LEN - 1];
277    let _dealer = sharks.dealer(&secret);
278
279    // one more for a short second element
280    let secret = [1u8; FIELD_ELEMENT_LEN + 1];
281    let _dealer = sharks.dealer(&secret);
282  }
283}