-
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.
* workspace based deps * Taplo FMT * found bug: shamir secret sharing * working: low f64 SSS algo and FSSS * fix: ssv * fix: commented Fel87 * fix: CI
- Loading branch information
Showing
11 changed files
with
272 additions
and
70 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,13 +1,28 @@ | ||
[workspace] | ||
members = [ | ||
"polynomial", | ||
"univariate-polynomial-iop-zerotest", | ||
"halo2-trials", | ||
"[Sha97]shamir-secret-sharing" | ||
"polynomial", | ||
"univariate-polynomial-iop-zerotest", | ||
"halo2-trials", | ||
"[Sha97]shamir-secret-sharing", | ||
"[Fel87]feldman-verifiable-secret-sharing", | ||
] | ||
resolver = "2" | ||
|
||
[profile.release] | ||
codegen-units = 1 | ||
lto = "fat" | ||
opt-level = "z" | ||
|
||
[workspace.dependencies] | ||
ark-bls12-381 = "0.4.0" | ||
ark-ec = "0.4.2" | ||
ark-ff = "0.4.2" | ||
ark-std = "0.4.0" | ||
num-traits = "0.2.18" | ||
nalgebra = "0.32.5" | ||
halo2 = "0.0.0" | ||
halo2_proofs = "0.3.0" | ||
rand_core = "0.6.4" | ||
rand = "0.8.5" | ||
rand_chacha = "0.3.1" | ||
simba = "0.8.1" |
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 |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
edition = "2021" | ||
name = "feldman-verifiable-secret-sharing" | ||
version = "0.1.0" | ||
|
||
[dependencies] | ||
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 } |
Empty file.
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 |
---|---|---|
@@ -0,0 +1,106 @@ | ||
use ark_bls12_381::G1Projective; | ||
use ark_ec::Group; | ||
|
||
#[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()); | ||
} | ||
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(); | ||
|
||
// 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]; | ||
} | ||
} | ||
|
||
result | ||
} | ||
|
||
#[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); | ||
// } | ||
} |
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,53 +1,108 @@ | ||
#[cfg(test)] | ||
mod tests { | ||
use polynomial::Polynomial; | ||
use rand::prelude::*; | ||
use std::fmt::Debug; | ||
use std::ops::Mul; | ||
use std::ops::Sub; | ||
|
||
#[test] | ||
fn shamir_secret_sharing() { | ||
// Let's say we want to create a shamir secret scheme | ||
// for hiding S = 119 | ||
let secret = 119; | ||
|
||
// 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 threshold = 2; | ||
|
||
// Hence, coefficients will be of form `secret + r1*x + r2*x^2...` | ||
// where `r` is random values from `(0, threshold)` | ||
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0); | ||
let coeffs: Vec<f64> = [secret as f64] | ||
use num_traits::One; | ||
use num_traits::Zero; | ||
use polynomial::Polynomial; | ||
use rand::RngCore; | ||
use rand::SeedableRng; | ||
use simba::scalar::ComplexField; | ||
|
||
/// Shamir's secret sharing is an ideal and perfect and ideal | ||
/// (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 | ||
/// secret | ||
pub struct ShamirSecret<T, const K: usize, const N: usize> { | ||
/// The underlying secret value to be broken up | ||
/// into secret "parts" | ||
secret: T, | ||
} | ||
|
||
/// A share of secret of form `(x, f(x))` where `x` is "evalutation | ||
/// point" and `f(x)` is "opening" | ||
#[derive(Debug, Clone)] | ||
pub struct SecretShare<T> { | ||
evaluation_point: T, | ||
opening: T, | ||
} | ||
|
||
impl<T, const K: usize, const N: usize> ShamirSecret<T, K, N> | ||
where | ||
T: From<u32> + Debug + Clone + Zero + One + Mul<Output = T> + Sub<Output = T> + ComplexField, | ||
{ | ||
/// Generates a Shamir's secret generator for a given | ||
/// secret value `secret` such that any `k` of `n` shares | ||
/// generated can reconstruct the `secret` back | ||
pub fn new_from_secret(secret: T) -> Self { | ||
assert!(K < N); | ||
Self { secret } | ||
} | ||
|
||
/// 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() | ||
.chain((0..threshold - 1).map(|_| rng.next_u32() as f64)) | ||
.map(|s| (s.evaluation_point, s.opening)) | ||
.collect(); | ||
let reconstructed_poly = Polynomial::new_from_evals(&evaluations); | ||
|
||
let polynomial = Polynomial::new_from_coeffs(&coeffs); | ||
Self { | ||
secret: reconstructed_poly.eval(T::zero()), | ||
} | ||
} | ||
|
||
// Ensure that we can get the secret back given we evaluate | ||
// at 0 | ||
assert_eq!(polynomial.eval(0.0) as i64, secret); | ||
/// Get the underlying secret value | ||
pub fn get_secret(&self) -> T { | ||
self.secret.clone() | ||
} | ||
|
||
// Now evaulate at a few random points to make the secret | ||
let secret_parts: Vec<(f64, f64)> = (0..parts) | ||
/// Generate `n` secret shares | ||
pub fn generate_secret_shares(&self) -> [SecretShare<T>; N] { | ||
// 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 polynomial = Polynomial::new_from_coeffs(&coefficients); | ||
|
||
let secret_shares: Vec<SecretShare<T>> = (0..N) | ||
.map(|_| { | ||
let point_of_eval = rng.next_u32() as f64; | ||
let eval = polynomial.eval(point_of_eval); // + 1.0 required as we do not want to generate at 0 | ||
(point_of_eval, eval) | ||
let evaluation_point = T::from(rng.next_u32() % 80); | ||
let opening = polynomial.eval(evaluation_point.clone()); | ||
SecretShare { | ||
evaluation_point, | ||
opening, | ||
} | ||
}) | ||
.collect(); | ||
|
||
// Ensure reconstruction is possible | ||
let reconstructed_poly = Polynomial::new_from_evals(&[secret_parts[0], secret_parts[3]]); | ||
assert!(reconstructed_poly.eval(0.0) - (secret as f64) < 0.0000001); // due to f64 inaccuracy | ||
secret_shares | ||
.try_into() | ||
.expect("slice with incorrect length") | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::ShamirSecret; | ||
|
||
#[test] | ||
fn shamir_secret_sharing_basic() { | ||
let secret_value = 2; | ||
|
||
let shares = ShamirSecret::<f64, 2, 4>::new_from_secret(secret_value.into()) | ||
.generate_secret_shares(); | ||
|
||
// Ensure reconstruction is possible | ||
let reconstructed_poly = Polynomial::new_from_evals(&[secret_parts[1], secret_parts[3]]); | ||
assert!(reconstructed_poly.eval(0.0) - (secret as f64) < 0.0000001); // due to f64 inaccuracy | ||
let reconstructed_from_shares = | ||
ShamirSecret::<f64, 2, 4>::new_from_shares([shares[0].clone(), shares[2].clone()]); | ||
|
||
// Ensure reconstruction is possible | ||
let reconstructed_poly = Polynomial::new_from_evals(&[secret_parts[1], secret_parts[2]]); | ||
assert!(reconstructed_poly.eval(0.0) - (secret as f64) < 0.0000001); // due to f64 inaccuracy | ||
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
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
Oops, something went wrong.