Skip to content

Commit

Permalink
Feldman Verifiable Secret Sharing (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
supragya authored Apr 9, 2024
1 parent a9c9566 commit ae604e1
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 108 deletions.
6 changes: 3 additions & 3 deletions [Fel87]feldman-verifiable-secret-sharing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ name = "feldman-verifiable-secret-sharing"
version = "0.1.0"

[dependencies]
shamir-secret-sharing = { path = "../[Sha97]shamir-secret-sharing" }
num-traits = { workspace = true }
simba = { workspace = true }
polynomial = { path = "../polynomial" }
rand = { workspace = true }
rand_chacha = { workspace = true }
ark-bls12-381 = { workspace = true }
ark-ec = { workspace = true }
ark-ff = { workspace = true }
ark-std = { workspace = true }
232 changes: 139 additions & 93 deletions [Fel87]feldman-verifiable-secret-sharing/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,106 +1,152 @@
use ark_bls12_381::G1Projective;
use ark_ec::Group;
use std::{
fmt::Debug,
ops::{Mul, Sub},
};

use ark_ff::fields::{Field, Fp64, MontBackend, MontConfig};
use num_traits::{One, Zero};
use polynomial::Polynomial;
use rand::{RngCore, SeedableRng};
use shamir_secret_sharing::SecretShare;
use simba::scalar::ComplexField;

/// Feldman's Verifiable secret sharing is very close to
/// Shamir's secret sharing: an ideal and perfect and ideal
/// (k, n)-threshold scheme based on polynomial interpolation,
/// with each secret share being verifiable.
/// Here, `n` is the number of secret parts generated by the
/// scheme, knowledge of any `k` of which can reconstruct original
/// secret
pub struct FeldmanVSS<T, const K: usize, const N: usize> {
/// The underlying secret value to be broken up
/// into secret "parts"
secret: T,
}

#[allow(dead_code)]
fn generate_two_powers(point: G1Projective, n: usize) -> Vec<G1Projective> {
if n < 2 {
panic!();
}
let mut powers: Vec<G1Projective> = Vec::with_capacity(n);
powers.push(G1Projective::default());
powers.push(point);
for idx in 2..n {
powers.push(powers[idx - 1].double());
pub trait Roundable {
fn round_to_nearest_integer(&self) -> u64;
}

impl Roundable for f64 {
fn round_to_nearest_integer(&self) -> u64 {
self.round() as u64
}
powers
}

#[allow(dead_code)]
fn point_exponentiation(exponent: u32, powers_of_2: &Vec<G1Projective>) -> G1Projective {
// Initialize the result to the identity element (zero point) of the group
let mut result = G1Projective::default();
#[derive(MontConfig)]
#[modulus = "99679"]
#[generator = "13"]
pub struct FqConfig;
pub type Fq = Fp64<MontBackend<FqConfig, 1>>;

impl<T, const K: usize, const N: usize> FeldmanVSS<T, K, N>
where
T: From<u32>
+ Debug
+ Clone
+ Zero
+ One
+ Mul<Output = T>
+ Sub<Output = T>
+ ComplexField
+ Roundable,
{
pub fn new_from_secret(secret: T) -> Self {
Self { secret }
}

// Iterate through each bit of the exponent
for i in 0..32 {
if (exponent >> i) & 1 == 1 {
// If the i-th bit is set in the exponent, add the corresponding power_of_2 to the result
result += &powers_of_2[i as usize];
pub fn new_from_verified_shares(
shares: [SecretShare<T>; K],
verification_points: [Fq; K],
) -> Self {
let evaluations: Vec<(T, T)> = shares.iter().map(|s| s.into_tuple()).collect();
let reconstructed_poly = Polynomial::new_from_evals(&evaluations);

shares.iter().for_each(|share| {
let mut validating_point = Fq::one();
for (exponent, point) in verification_points.iter().enumerate() {
validating_point = validating_point
* point.pow(&[share
.evaluation_point
.clone()
.powi(exponent as i32)
.round_to_nearest_integer()])
}
assert_eq!(
Fq::from(13).pow(&[share.opening.round_to_nearest_integer()]),
validating_point
);
});

Self {
secret: reconstructed_poly.eval(T::zero()),
}
}

result
/// Get the underlying secret value
pub fn get_secret(&self) -> T {
self.secret.clone()
}

/// Generate `n` secret shares alongwith their polynomial
/// coefficient as exponents over a finite cyclic group generator,
/// called verification points.
pub fn generate_secret_shares(&self) -> ([SecretShare<T>; N], [Fq; K]) {
// first, generate a polynomial of form `f(x) = s + a*x + b*x^2 + ...`
// where `s` is the encoded secret value, `a`, `b`... are random
// coefficients and degree of `f(x)` is `K-1`.
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0);

let coefficients: Vec<T> = vec![self.secret.clone()]
.into_iter()
.chain((1..K).map(|_| T::from(rng.next_u32() % 100)))
.collect();

let verification_points: Vec<Fq> = coefficients
.iter()
.map(|x| Fq::from(13).pow(&[x.round_to_nearest_integer()]))
.collect();

let polynomial = Polynomial::new_from_coeffs(&coefficients);

let secret_shares: Vec<SecretShare<T>> = (0..N)
.map(|_| {
let evaluation_point = T::from(rng.next_u32() % 80);
let opening = polynomial.eval(evaluation_point.clone());
SecretShare {
evaluation_point,
opening,
}
})
.collect();

(
secret_shares
.try_into()
.expect("slice with incorrect length"),
verification_points
.try_into()
.expect("slice with incorrect length"),
)
}
}

#[cfg(test)]
mod tests {
// use ark_ec::Group;
// use polynomial::Polynomial;
// // use rand::prelude::*;
// use super::*;

// #[test]
// fn feldman_verifiable_secret_sharing_low_f64() {
// // Let's say we want to create a verifyable secret scheme
// // for hiding S = 5
// let secret = 5;

// // Assume that we want to break secret into 4 "parts",
// // of which any 2 should be able to reconstruct the
// // original secret
// let parts = 4;

// let coeffs: Vec<u32> = vec![secret as u32, 4];

// let generator_two_powers =
// generate_two_powers(ark_bls12_381::G1Projective::generator(), 32);

// let public_g_raised_coeffs: Vec<G1Projective> = coeffs
// .iter()
// .map(|x| point_exponentiation(*x, &generator_two_powers))
// .collect();

// let coeffs: Vec<f64> = coeffs.into_iter().map(|x| x as f64).collect();

// let polynomial = Polynomial::new_from_coeffs(&coeffs);

// // Ensure that we can get the secret back given we evaluate
// // at 0
// assert_eq!(polynomial.eval(0.0) as f64, secret as f64);

// // Now evaulate at a few random points to make the secret
// let secret_parts: Vec<(f64, f64)> = (1..(parts + 1))
// .map(|x| {
// let eval = polynomial.eval((x * 10) as f64);
// ((x * 10) as f64, eval)
// })
// .collect();

// // Ensure reconstruction is possible
// let secrets_revealed = [secret_parts[0], secret_parts[3]];
// let reconstructed_poly = Polynomial::new_from_evals(&secrets_revealed);
// // Now that we have reconstructed the polynomial from secret parts,
// // we need to ensure that those "secrets" are verifyably correct.
// // This is where VSS kicks in. For this, we use `public_g_raised_coeffs`
// let reconstructed_coeffs: Vec<u32> = reconstructed_poly
// .get_raw_coefficients()
// .into_iter()
// .map(|x| x as u32)
// .collect();

// let a_powers: Vec<Vec<G1Projective>> = public_g_raised_coeffs
// .into_iter()
// .map(|x| generate_two_powers(x, 32))
// .collect();

// for (x, eval) in secrets_revealed {
// let mut running_x_pow = 1.0;
// let mut running_g_multiplier = G1Projective::default();
// for (idx, coeff) in reconstructed_coeffs.iter().enumerate() {
// running_g_multiplier =
// running_g_multiplier * point_exponentiation(*coeff, &a_powers[idx])
// }
// }

// // assert!((reconstructed_poly.eval(0.0) - (secret as f64)).abs() < 0.0000001);
// }
use crate::FeldmanVSS;

#[test]
fn feldman_verifyable_secret_sharing_f64() {
let secret_value = 2;

let (shares, verification_points) =
FeldmanVSS::<f64, 2, 4>::new_from_secret(secret_value.into()).generate_secret_shares();

let given_shares = [shares[0].clone(), shares[2].clone()];

let reconstructed_from_shares =
FeldmanVSS::<f64, 2, 4>::new_from_verified_shares(given_shares, verification_points);

assert_eq!(reconstructed_from_shares.get_secret() as i32, secret_value)
}
}
29 changes: 17 additions & 12 deletions [Sha97]shamir-secret-sharing/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use std::fmt::Debug;
use std::ops::Mul;
use std::ops::Sub;

use num_traits::One;
use num_traits::Zero;
use polynomial::Polynomial;
use rand::RngCore;
use rand::SeedableRng;
use simba::scalar::ComplexField;
use std::fmt::Debug;
use std::ops::Mul;
use std::ops::Sub;

/// Shamir's secret sharing is an ideal and perfect and ideal
/// Shamir's secret sharing is an ideal and perfect
/// (k, n)-threshold scheme based on polynomial interpolation.
/// Here, `n` is the number of secret parts generated by the
/// scheme, knowledge of any `k` of which can reconstruct original
Expand All @@ -24,8 +23,17 @@ pub struct ShamirSecret<T, const K: usize, const N: usize> {
/// point" and `f(x)` is "opening"
#[derive(Debug, Clone)]
pub struct SecretShare<T> {
evaluation_point: T,
opening: T,
pub evaluation_point: T,
pub opening: T,
}

impl<T> SecretShare<T>
where
T: Clone,
{
pub fn into_tuple(&self) -> (T, T) {
(self.evaluation_point.clone(), self.opening.clone())
}
}

impl<T, const K: usize, const N: usize> ShamirSecret<T, K, N>
Expand All @@ -42,10 +50,7 @@ where

/// Generate a Shamir's secret from `K` shares
pub fn new_from_shares(shares: [SecretShare<T>; K]) -> Self {
let evaluations: Vec<(T, T)> = shares
.into_iter()
.map(|s| (s.evaluation_point, s.opening))
.collect();
let evaluations: Vec<(T, T)> = shares.into_iter().map(|s| s.into_tuple()).collect();
let reconstructed_poly = Polynomial::new_from_evals(&evaluations);

Self {
Expand Down Expand Up @@ -94,7 +99,7 @@ mod tests {
use crate::ShamirSecret;

#[test]
fn shamir_secret_sharing_basic() {
fn shamir_secret_sharing_f64() {
let secret_value = 2;

let shares = ShamirSecret::<f64, 2, 4>::new_from_secret(secret_value.into())
Expand Down

0 comments on commit ae604e1

Please sign in to comment.