-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feldman Verifiable Secret Sharing (#9)
- Loading branch information
Showing
3 changed files
with
159 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters