use crate::geometry::*;
use rand::rngs::StdRng;
use rand::{Rng, RngCore, SeedableRng};
pub fn from_secret(
secret: u8,
shares_required: u8,
shares_to_create: u8,
mut rand: Option<&mut dyn RngCore>,
) -> Result<Vec<(u8, u8)>, Error> {
if shares_required > shares_to_create {
return Err(Error::UnreconstructableSecret(
shares_to_create,
shares_required,
));
}
if shares_to_create < 2 {
return Err(Error::InvalidNumberOfShares(shares_to_create));
}
let mut shares: Vec<(u8, u8)> = Vec::with_capacity(shares_to_create as usize);
let mut share_poly = GaloisPolynomial::new();
let mut rng: Box<dyn RngCore> = match rand {
Some(rng) => Box::new(rng),
None => Box::new(StdRng::from_entropy()),
};
share_poly.set_coeff(Coeff(secret), 0);
for i in 1..shares_required {
let curr_co = rng.gen_range(2, 255);
share_poly.set_coeff(Coeff(curr_co), i as usize);
}
for i in 1..=shares_to_create {
let curr_x = i as u8;
let curr_y = share_poly.get_y_value(curr_x);
shares.push((curr_x, curr_y));
}
Ok(shares)
}
pub fn reconstruct_secret(shares: Vec<(u8, u8)>) -> u8 {
GaloisPolynomial::get_y_intercept_from_points(shares.as_slice())
}
pub fn from_secrets(
secret: &[u8],
shares_required: u8,
shares_to_create: u8,
rand: Option<&mut dyn RngCore>,
) -> Result<Vec<Vec<(u8, u8)>>, Error> {
if secret.is_empty() {
return Err(Error::EmptySecretArray);
}
let mut from_entropy: Box<dyn RngCore>;
let mut rand = match rand {
Some(rng) => rng,
None => {
from_entropy = Box::new(StdRng::from_entropy());
&mut from_entropy
}
};
let mut list_of_share_lists: Vec<Vec<(u8, u8)>> = Vec::with_capacity(secret.len());
for s in secret {
match from_secret(*s, shares_required, shares_to_create, Some(&mut rand)) {
Ok(shares) => {
list_of_share_lists.push(shares);
}
Err(e) => {
return Err(e);
}
}
}
let list_of_share_lists = transpose_vec_matrix(list_of_share_lists).unwrap();
Ok(list_of_share_lists)
}
pub fn reconstruct_secrets(share_lists: Vec<Vec<(u8, u8)>>) -> Result<Vec<u8>, Error> {
let mut secrets: Vec<u8> = Vec::with_capacity(share_lists[0].len());
let share_lists = transpose_vec_matrix(share_lists)?;
for point_list in share_lists {
secrets.push(reconstruct_secret(point_list));
}
Ok(secrets)
}
pub fn from_secrets_no_points(
secret: &[u8],
shares_required: u8,
shares_to_create: u8,
rand: Option<&mut dyn RngCore>,
) -> Result<Vec<Vec<u8>>, Error> {
Ok(
from_secrets(secret, shares_required, shares_to_create, rand)?
.into_iter()
.map(reduce_share)
.map(|(x, ys)| {
let mut new_share = Vec::with_capacity(ys.len() + 1);
new_share.push(x);
new_share.extend_from_slice(ys.as_slice());
new_share
})
.collect(),
)
}
pub fn reconstruct_secrets_no_points(share_lists: Vec<Vec<u8>>) -> Result<Vec<u8>, Error> {
reconstruct_secrets(share_lists.into_iter().map(expand_share).collect())
}
pub fn reduce_share(share: Vec<(u8, u8)>) -> (u8, Vec<u8>) {
(share[0].0, share.into_iter().map(|(_, y)| y).collect())
}
pub fn expand_share(share: Vec<u8>) -> Vec<(u8, u8)> {
let x_value = share[0];
share[1..].iter().map(|y| (x_value, *y)).collect()
}
#[allow(clippy::needless_range_loop)]
pub fn transpose_vec_matrix<T: Clone>(matrix: Vec<Vec<T>>) -> Result<Vec<Vec<T>>, Error> {
for i in 1..matrix.len() {
if matrix[i - 1].len() != matrix[i].len() {
return Err(Error::InvalidMatrix {
index_of_invalid_length_row: i,
});
}
}
let col_len = matrix.len();
let row_len = matrix[0].len();
let mut transpose: Vec<Vec<T>> = Vec::with_capacity(col_len);
for _ in 0..matrix[0].len() {
transpose.push(Vec::with_capacity(row_len));
}
for i in 0..matrix.len() {
for j in 0..matrix[i].len() {
transpose[j].push(matrix[i][j].clone());
}
}
Ok(transpose)
}
#[derive(Debug)]
pub enum Error {
NotEnoughShares { given: u8, required: u8 },
InvalidMatrix { index_of_invalid_length_row: usize },
InvalidNumberOfShares(u8),
UnreconstructableSecret(u8, u8),
EmptySecretArray,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::NotEnoughShares { given, required } => write!(
f,
"Not enough shares to recreate secret: Given: {}; Required: {}",
given, required
),
Error::InvalidMatrix {
index_of_invalid_length_row,
} => write!(
f,
"Row {} is not the same length as previous rows",
index_of_invalid_length_row
),
Error::EmptySecretArray => write!(f, "Secret array should not be empty"),
Error::InvalidNumberOfShares(num) => {
write!(f, "Need to generate at least 2 shares. Requested: {}", num)
}
Error::UnreconstructableSecret(to_create, required) => write!(
f,
"Can't create less shares than required to reconstruct. Create: {}, Req: {}",
to_create, required
),
}
}
}
impl std::error::Error for Error {}
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::StdRng;
use rand::Rng;
use rand::SeedableRng;
#[test]
fn many_test() {
let num_iters = 10;
let mut rand = StdRng::seed_from_u64(123u64);
for _ in 0..num_iters {
let secret: u8 = rand.gen_range(1, 256) as u8;
let shares_required: u8 = rand.gen_range(2, 10);
let shares_to_create: u8 = shares_required + rand.gen_range(0, 6);
basic_single_value(secret, shares_to_create, shares_required);
}
}
fn basic_single_value(secret: u8, shares_to_create: u8, shares_required: u8) {
let shares = from_secret(secret, shares_required, shares_to_create, None).unwrap();
let secret_decrypted = reconstruct_secret(shares);
assert_eq!(secret, secret_decrypted);
}
#[test]
fn transpose() {
let matrix = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
let matrix1 = vec![vec![1, 4, 7], vec![2, 5, 8], vec![3, 6, 9]];
let matrix2 = vec![vec![1, 2, 3, 4], vec![5, 6, 7, 8]];
let matrix3 = vec![vec![1, 5], vec![2, 6], vec![3, 7], vec![4, 8]];
assert_eq!(transpose_vec_matrix(matrix).unwrap(), matrix1);
assert_eq!(transpose_vec_matrix(matrix2).unwrap(), matrix3);
}
#[cfg(feature = "benchmark_tests")]
#[test]
fn large_data_and_benchmark() {
use std::time::Instant;
let secret = "According to all known laws of aviation,
there is no way a bee should be able to fly.
Its wings are too small to get its fat little body off the ground.
The bee, of course, flies anyway
because bees don't care what humans think is impossible.";
let shares_required = 5;
let shares_to_create = 5;
let now = Instant::now();
let share_lists =
from_secrets(secret.as_bytes(), shares_required, shares_to_create).unwrap();
let recon_secret_vec = reconstruct_secrets(share_lists).unwrap();
let recon_secret = String::from_utf8(recon_secret_vec).unwrap();
let time_elap = now.elapsed().as_millis();
println!("Time elapsed: {} milliseconds", time_elap);
assert_eq!(secret, &recon_secret[..])
}
#[test]
fn no_points() {
let secret = vec![10, 20, 30, 40, 50];
let n = 3;
let shares = from_secrets_no_points(&secret, n, n, None).unwrap();
let recon = reconstruct_secrets_no_points(shares).unwrap();
assert_eq!(secret, recon);
}
}