use crate::math::FiniteFieldElement;
use num_bigint::BigUint;
use std::fmt;
pub const MODULUS_ARRAY_128: [u32; 4] = [u32::MAX - 158, u32::MAX, u32::MAX, u32::MAX];
pub const MODULUS_ARRAY_160: [u32; 5] = [u32::MAX - 46, u32::MAX, u32::MAX, u32::MAX, u32::MAX];
pub const MODULUS_ARRAY_192: [u32; 6] = [
u32::MAX - 236,
u32::MAX,
u32::MAX,
u32::MAX,
u32::MAX,
u32::MAX,
];
pub const MODULUS_ARRAY_224: [u32; 7] = [
u32::MAX - 62,
u32::MAX,
u32::MAX,
u32::MAX,
u32::MAX,
u32::MAX,
u32::MAX,
];
pub const MODULUS_ARRAY_256: [u32; 8] = [
u32::MAX - 188,
u32::MAX,
u32::MAX,
u32::MAX,
u32::MAX,
u32::MAX,
u32::MAX,
u32::MAX,
];
pub(crate) fn get_modulus_for_bits(num_bits: usize) -> Option<BigUint> {
match num_bits {
128 => Some(BigUint::from_slice(&MODULUS_ARRAY_128)),
160 => Some(BigUint::from_slice(&MODULUS_ARRAY_160)),
192 => Some(BigUint::from_slice(&MODULUS_ARRAY_192)),
224 => Some(BigUint::from_slice(&MODULUS_ARRAY_224)),
256 => Some(BigUint::from_slice(&MODULUS_ARRAY_256)),
_ => None,
}
}
pub(crate) fn get_modulus_for_words(num_words: usize) -> Option<BigUint> {
match num_words {
12 => Some(BigUint::from_slice(&MODULUS_ARRAY_128)),
15 => Some(BigUint::from_slice(&MODULUS_ARRAY_160)),
18 => Some(BigUint::from_slice(&MODULUS_ARRAY_192)),
21 => Some(BigUint::from_slice(&MODULUS_ARRAY_224)),
24 => Some(BigUint::from_slice(&MODULUS_ARRAY_256)),
_ => None,
}
}
pub(crate) struct SecretPolynomial {
coefficients: Vec<FiniteFieldElement>,
}
pub(crate) struct SecretShare {
pub index: u32,
pub element: FiniteFieldElement,
}
impl SecretShare {
pub fn new(element: &FiniteFieldElement, index: u32) -> Self {
SecretShare {
index,
element: element.clone(),
}
}
}
impl Clone for SecretShare {
fn clone(&self) -> SecretShare {
SecretShare {
index: self.index,
element: self.element.clone(),
}
}
}
impl fmt::Display for SecretShare {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "[{}, {}]", self.index, self.element.value)
}
}
impl SecretPolynomial {
pub(crate) fn new(secret: &FiniteFieldElement, num_bits: usize, degree: usize) -> Option<Self> {
match get_modulus_for_bits(num_bits) {
Some(modulus) => {
let mut coefficients = vec![secret.clone()];
for _in in 1..=degree {
coefficients.push(FiniteFieldElement::new_random(num_bits, &modulus));
}
Some(SecretPolynomial { coefficients })
}
None => None,
}
}
fn evaluate(&self, value: u32) -> FiniteFieldElement {
let degree = self.coefficients.len() - 1;
let mut result = self.coefficients[degree].clone();
let finite_field_value = FiniteFieldElement::new_integer(value, &result.modulus);
for index in (0..degree).rev() {
result = (result * finite_field_value.clone()) + self.coefficients[index].clone();
}
result
}
pub(crate) fn get_secret_shares(&self, number: u32) -> Vec<SecretShare> {
let mut secret_shares = vec![];
for index in 1..=number {
secret_shares.push(SecretShare {
index,
element: self.evaluate(index),
});
}
secret_shares
}
}
pub(crate) fn reconstruct_secret(secret_shares: &[SecretShare]) -> FiniteFieldElement {
let modulus = &secret_shares[0].element.modulus;
let indices: Vec<u32> = secret_shares.iter().map(|share| share.index).collect();
let mut secret = FiniteFieldElement::new_integer(0, modulus);
for secret_share in secret_shares {
let term = secret_share.element.clone();
let mut multiply_term = FiniteFieldElement::new_integer(1, modulus);
let mut divide_term = FiniteFieldElement::new_integer(1, modulus);
let other_indices: Vec<u32> = indices
.iter()
.copied()
.filter(|index| *index != secret_share.index)
.collect();
for index in other_indices {
let index_element = FiniteFieldElement::new_integer(index, modulus);
let secret_share_index_element =
FiniteFieldElement::new_integer(secret_share.index, modulus);
multiply_term = multiply_term * index_element.clone();
divide_term = divide_term * (index_element - secret_share_index_element.clone());
}
secret = secret + (term * multiply_term / divide_term);
}
secret
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{seq::SliceRandom, Rng};
const NUM_TEST_RUNS: usize = 10;
#[test]
fn test_polynomial_evaluation() {
let modulus = get_modulus_for_bits(128).unwrap();
let mut rng = rand::thread_rng();
for _test in 0..NUM_TEST_RUNS {
let secret = FiniteFieldElement::new_random(128, &modulus);
let degree = rng.gen_range(2..20);
let polynomial = SecretPolynomial::new(&secret, 128, degree).unwrap();
assert_eq!(polynomial.evaluate(0), secret);
let mut coefficient_sum: FiniteFieldElement =
FiniteFieldElement::new_integer(0, &modulus);
for coefficient in &polynomial.coefficients {
coefficient_sum = coefficient_sum + coefficient.clone();
}
assert_eq!(polynomial.evaluate(1), coefficient_sum);
}
}
#[test]
fn test_working_secret_reconstruction() {
let mut rng = rand::thread_rng();
for _test in 0..NUM_TEST_RUNS {
let secret = FiniteFieldElement::new_random(256, &get_modulus_for_bits(256).unwrap());
let degree = rng.gen_range(2..20);
let polynomial = SecretPolynomial::new(&secret, 256, degree).unwrap();
let shares = polynomial.get_secret_shares((degree * 2) as u32);
let random_shares: Vec<SecretShare> = shares
.choose_multiple(&mut rng, degree + 1)
.cloned()
.collect();
let reconstructed_secret = reconstruct_secret(&random_shares);
assert_eq!(secret, reconstructed_secret);
}
}
#[test]
fn test_failing_secret_reconstruction() {
let modulus = &get_modulus_for_bits(256).unwrap();
let mut rng = rand::thread_rng();
for _test in 0..NUM_TEST_RUNS {
let secret = FiniteFieldElement::new_random(256, modulus);
let degree = rng.gen_range(2..20);
let polynomial = SecretPolynomial::new(&secret, 256, degree).unwrap();
let shares = polynomial.get_secret_shares((degree * 2) as u32);
let num_secret_shares = rng.gen_range(1..degree + 1);
let random_shares: Vec<SecretShare> = shares
.choose_multiple(&mut rng, num_secret_shares)
.cloned()
.collect();
let reconstructed_secret = reconstruct_secret(&random_shares);
assert_ne!(secret, reconstructed_secret);
}
}
}