// A module which contains necessary algorithms to compute Shamir's shares and recover secrets use alloc::vec::Vec; use rand::distributions::{Distribution, Uniform}; use super::field::GF256; use super::share::Share; // Finds the [root of the Lagrange polynomial](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing#Computationally_efficient_approach). // The expected `shares` argument format is the same as the output by the `get_evaluatorĀ“ function. // Where each (key, value) pair corresponds to one share, where the key is the `x` and the value is a vector of `y`, // where each element corresponds to one of the secret's byte chunks. pub fn interpolate(shares: &[Share]) -> Vec { (0..shares[0].y.len()) .map(|s| { shares .iter() .map(|s_i| { shares .iter() .filter(|s_j| s_j.x != s_i.x) .map(|s_j| s_j.x.clone() / (s_j.x.clone() - s_i.x.clone())) .product::() * s_i.y[s].clone() }) .sum::() .0 }) .collect() } // Generates `k` polynomial coefficients, being the last one `s` and the others randomly generated between `[0, 255]`. // Coefficient degrees go from higher to lower in the returned vector order. pub fn random_polynomial(s: GF256, k: u8, rng: &mut R) -> Vec { let k = k as usize; let mut poly = Vec::with_capacity(k); let between = Uniform::new_inclusive(0, 255); for _ in 1..k { poly.push(GF256(between.sample(rng))); } poly.push(s); poly } // Returns an iterator over the points of the `polys` polynomials passed as argument. // Each item of the iterator is a tuple `(x, [f_1(x), f_2(x)..])` where eaxh `f_i` is the result for the ith polynomial. // Each polynomial corresponds to one byte chunk of the original secret. // The iterator will start at `x = 1` and end at `x = 255`. pub fn get_evaluator(polys: Vec>) -> impl Iterator { (1..=u8::MAX).map(GF256).map(move |x| Share { x: x.clone(), y: polys .iter() .map(|p| { p.iter() .fold(GF256(0), |acc, c| acc * x.clone() + c.clone()) }) .collect(), }) } #[cfg(test)] mod tests { use super::{get_evaluator, interpolate, random_polynomial, Share, GF256}; use alloc::{vec, vec::Vec}; use rand_chacha::rand_core::SeedableRng; #[test] fn random_polynomial_works() { let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); let poly = random_polynomial(GF256(1), 3, &mut rng); assert_eq!(poly.len(), 3); assert_eq!(poly[2], GF256(1)); } #[test] fn evaluator_works() { let iter = get_evaluator(vec![vec![GF256(3), GF256(2), GF256(5)]]); let values: Vec<_> = iter.take(2).map(|s| (s.x.clone(), s.y.clone())).collect(); assert_eq!( values, vec![(GF256(1), vec![GF256(4)]), (GF256(2), vec![GF256(13)])] ); } #[test] fn interpolate_works() { let mut rng = rand_chacha::ChaCha8Rng::from_seed([0x90; 32]); let poly = random_polynomial(GF256(185), 10, &mut rng); let iter = get_evaluator(vec![poly]); let shares: Vec = iter.take(10).collect(); let root = interpolate(&shares); assert_eq!(root, vec![185]); } }