diff --git a/Cargo.lock b/Cargo.lock index 347f378..f0f0fbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -555,7 +555,7 @@ dependencies = [ [[package]] name = "generic-ec" version = "0.0.0" -source = "git+https://github.com/dfns-labs/generic-ec?branch=d#a65125aff588964ba52736ed977d86034674e470" +source = "git+https://github.com/dfns-labs/generic-ec?branch=m#448b0789b9174ef14e9fe0f8a802cef59ba3dc4f" dependencies = [ "generic-ec-core", "generic-ec-curves", @@ -571,7 +571,7 @@ dependencies = [ [[package]] name = "generic-ec-core" version = "0.1.0" -source = "git+https://github.com/dfns-labs/generic-ec?branch=d#a65125aff588964ba52736ed977d86034674e470" +source = "git+https://github.com/dfns-labs/generic-ec?branch=m#448b0789b9174ef14e9fe0f8a802cef59ba3dc4f" dependencies = [ "generic-array", "rand_core", @@ -583,7 +583,7 @@ dependencies = [ [[package]] name = "generic-ec-curves" version = "0.1.0" -source = "git+https://github.com/dfns-labs/generic-ec?branch=d#a65125aff588964ba52736ed977d86034674e470" +source = "git+https://github.com/dfns-labs/generic-ec?branch=m#448b0789b9174ef14e9fe0f8a802cef59ba3dc4f" dependencies = [ "elliptic-curve", "generic-ec-core", @@ -598,7 +598,7 @@ dependencies = [ [[package]] name = "generic-ec-zkp" version = "0.1.0" -source = "git+https://github.com/dfns-labs/generic-ec?branch=d#a65125aff588964ba52736ed977d86034674e470" +source = "git+https://github.com/dfns-labs/generic-ec?branch=m#448b0789b9174ef14e9fe0f8a802cef59ba3dc4f" dependencies = [ "digest 0.10.6", "generic-array", @@ -851,10 +851,9 @@ dependencies = [ [[package]] name = "paillier-zk" version = "0.1.0" -source = "git+https://github.com/dfns-labs/paillier-zk?branch=m#caa26aecb57f5667c268454e6a9a3b1d1c936444" +source = "git+https://github.com/dfns-labs/paillier-zk?branch=m#a2639222e07b38657344c8327cd47ebb01be6eb7" dependencies = [ "generic-ec", - "generic-ec-core", "libpaillier", "rand_chacha", "rand_core", diff --git a/Cargo.toml b/Cargo.toml index 8ef981c..f6c4613 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,4 +3,3 @@ members = [ "cggmp21", "tests", ] - diff --git a/cggmp21/Cargo.toml b/cggmp21/Cargo.toml index 642f084..d09deb7 100644 --- a/cggmp21/Cargo.toml +++ b/cggmp21/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -generic-ec = { git = "https://github.com/dfns-labs/generic-ec", branch = "d", features = ["serde"] } -generic-ec-zkp = { git = "https://github.com/dfns-labs/generic-ec", branch = "d", features = ["serde"] } +generic-ec = { git = "https://github.com/dfns-labs/generic-ec", branch = "m", features = ["serde"] } +generic-ec-zkp = { git = "https://github.com/dfns-labs/generic-ec", branch = "m", features = ["serde"] } round-based = { git = "https://github.com/Zengo-X/round-based-protocol", branch = "round-based2", features = ["derive"] } paillier-zk = { git = "https://github.com/dfns-labs/paillier-zk", branch = "m", default-features = false, features = ["serde"] } diff --git a/cggmp21/src/key_share.rs b/cggmp21/src/key_share.rs index b465a5d..6f13d81 100644 --- a/cggmp21/src/key_share.rs +++ b/cggmp21/src/key_share.rs @@ -5,6 +5,7 @@ use std::{fmt, ops}; use generic_ec::serde::{Compact, CurveName}; use generic_ec::{Curve, NonZero, Point, Scalar, SecretScalar}; +use generic_ec_zkp::polynomial::lagrange_coefficient; use paillier_zk::libpaillier::unknown_order::BigNumber; use paillier_zk::paillier_encryption_in_range as π_enc; use serde::{Deserialize, Serialize}; @@ -12,7 +13,6 @@ use serde_with::serde_as; use thiserror::Error; use crate::security_level::SecurityLevel; -use crate::utils::lagrange_coefficient; /// Key share /// @@ -169,7 +169,8 @@ impl DirtyIncompleteKeyShare { let first_t_shares = &self.public_shares[0..usize::from(t)]; let indexes = &vss_setup.I[0..usize::from(t)]; let interpolation = |x: Scalar| { - let lagrange_coefficients = (0..t).map(|j| lagrange_coefficient(x, j, indexes)); + let lagrange_coefficients = + (0..usize::from(t)).map(|j| lagrange_coefficient(x, j, indexes)); lagrange_coefficients .zip(first_t_shares) .try_fold(Point::zero(), |acc, (lambda_j, X_j)| { @@ -450,7 +451,8 @@ pub fn reconstruct_secret_key( if let Some(VssSetup { I, .. }) = vss { let S = key_shares.iter().map(|s| s.core().i).collect::>(); let I = subset(&S, I).ok_or(ReconstructErrorReason::Subset)?; - let lagrange_coefficients = (0..t).map(|j| lagrange_coefficient(Scalar::zero(), j, &I)); + let lagrange_coefficients = + (0..usize::from(t)).map(|j| lagrange_coefficient(Scalar::zero(), j, &I)); let mut sk = lagrange_coefficients .zip(key_shares) .try_fold(Scalar::zero(), |acc, (lambda_j, key_share_j)| { diff --git a/cggmp21/src/keygen/threshold.rs b/cggmp21/src/keygen/threshold.rs index 4f140ee..de2867e 100644 --- a/cggmp21/src/keygen/threshold.rs +++ b/cggmp21/src/keygen/threshold.rs @@ -4,6 +4,7 @@ use generic_ec::hash_to_curve::{self, FromHash}; use generic_ec::{Curve, NonZero, Point, Scalar, SecretScalar}; use generic_ec_zkp::{ hash_commitment::{self, HashCommit}, + polynomial::Polynomial, schnorr_pok, }; use rand_core::{CryptoRng, RngCore}; @@ -49,7 +50,7 @@ pub struct MsgRound1 { pub struct MsgRound2Broad { #[serde(with = "hex::serde")] pub rid: L::Rid, - pub Ss: Vec>, + pub F: Polynomial>, pub sch_commit: schnorr_pok::Commit, pub decommit: hash_commitment::DecommitNonce, } @@ -115,15 +116,12 @@ where let (r, h) = schnorr_pok::prover_commits_ephemeral_secret::(rng); - let ss = utils::sample_polynomial(usize::from(t), rng); - let Ss = ss - .iter() - .map(|s| Point::generator() * s) - .collect::>(); + let f = Polynomial::>::sample(rng, usize::from(t) - 1); + let F = &f * &Point::generator(); let sigmas = (0..n) .map(|j| { let x = Scalar::from(j + 1); - utils::polynomial_value(Scalar::zero(), &x, &ss) + f.value(&x) }) .collect::>(); debug_assert_eq!(sigmas.len(), usize::from(n)); @@ -135,7 +133,7 @@ where .mix(i) .mix(t) .mix_bytes(&rid) - .mix_many(Ss.iter()) + .mix_many(F.coefs()) .mix(h.0) .commit(rng); @@ -200,7 +198,7 @@ where tracer.send_msg(); let my_decommitment = MsgRound2Broad { rid, - Ss: Ss.clone(), + F: F.clone(), sch_commit: h, decommit, }; @@ -247,7 +245,7 @@ where .mix(j) .mix(t) .mix_bytes(&decommitment.rid) - .mix_many(decommitment.Ss.iter()) + .mix_many(decommitment.F.coefs()) .mix(decommitment.sch_commit.0) .verify(&commitment.commitment, &decommitment.decommit) .is_err() @@ -261,7 +259,7 @@ where tracer.stage("Validate data size"); let blame = decommitments .iter_indexed() - .filter(|(_, _, d)| d.Ss.len() != usize::from(t)) + .filter(|(_, _, d)| d.F.degree() + 1 != usize::from(t)) .map(|t| t.0) .collect::>(); if !blame.is_empty() { @@ -273,8 +271,7 @@ where .iter_indexed() .zip(sigmas_msg.iter()) .filter(|((_, _, d), s)| { - utils::polynomial_value(Point::zero(), &Scalar::from(i + 1), &d.Ss) - != Point::generator() * s.sigma + d.F.value::<_, Point<_>>(&Scalar::from(i + 1)) != Point::generator() * s.sigma }) .map(|t| t.0 .0) .collect::>(); @@ -288,15 +285,12 @@ where .map(|d| &d.rid) .fold(L::Rid::default(), xor_array); tracer.stage("Compute Ys"); + let polynomial_sum = decommitments + .iter_including_me(&my_decommitment) + .map(|d| &d.F) + .sum::>(); let ys = (0..n) - .map(|l| { - let polynomial_sum = utils::polynomials_sum( - decommitments - .iter_including_me(&my_decommitment) - .map(|d| d.Ss.as_slice()), - ); - utils::polynomial_value(Point::zero(), &Scalar::from(l + 1), &polynomial_sum) - }) + .map(|l| polynomial_sum.value(&Scalar::from(l + 1))) .collect::>(); tracer.stage("Compute sigma"); let sigma: Scalar = sigmas_msg.iter().map(|msg| msg.sigma).sum(); @@ -370,7 +364,7 @@ where tracer.stage("Derive resulting public key and other data"); let y: Point = decommitments .iter_including_me(&my_decommitment) - .map(|d| d.Ss[0]) + .map(|d| d.F.coefs()[0]) .sum(); let key_shares_indexes = (1..=n) .map(|i| NonZero::from_scalar(Scalar::from(i))) diff --git a/cggmp21/src/signing.rs b/cggmp21/src/signing.rs index c2df8f0..72b4eff 100644 --- a/cggmp21/src/signing.rs +++ b/cggmp21/src/signing.rs @@ -3,6 +3,7 @@ use futures::SinkExt; use generic_ec::{ coords::AlwaysHasAffineX, hash_to_curve::FromHash, Curve, NonZero, Point, Scalar, SecretScalar, }; +use generic_ec_zkp::polynomial::lagrange_coefficient; use paillier_zk::libpaillier::{unknown_order::BigNumber, DecryptionKey}; use paillier_zk::{ group_element_vs_paillier_encryption_in_range as pi_log, @@ -20,7 +21,7 @@ use thiserror::Error; use crate::errors::IoError; use crate::key_share::{KeyShare, PartyAux, VssSetup}; use crate::progress::Tracer; -use crate::utils::{hash_message, iter_peers, lagrange_coefficient, subset, HashMessageError}; +use crate::utils::{hash_message, iter_peers, subset, HashMessageError}; use crate::{ key_share::InvalidKeyShare, security_level::SecurityLevel, @@ -362,10 +363,11 @@ where let I = subset(S, I).ok_or(Bug::Subset)?; let X = subset(S, &key_share.core.public_shares).ok_or(Bug::Subset)?; - let lambda_i = lagrange_coefficient(Scalar::zero(), i, &I).ok_or(Bug::LagrangeCoef)?; + let lambda_i = + lagrange_coefficient(Scalar::zero(), usize::from(i), &I).ok_or(Bug::LagrangeCoef)?; let x_i = SecretScalar::new(&mut (lambda_i * &key_share.core.x)); - let lambda = (0..t).map(|j| lagrange_coefficient(Scalar::zero(), j, &I)); + let lambda = (0..t).map(|j| lagrange_coefficient(Scalar::zero(), usize::from(j), &I)); let X = lambda .zip(&X) .map(|(lambda_j, X_j)| Some(lambda_j? * X_j)) diff --git a/cggmp21/src/trusted_dealer.rs b/cggmp21/src/trusted_dealer.rs index 29295e3..0fad455 100644 --- a/cggmp21/src/trusted_dealer.rs +++ b/cggmp21/src/trusted_dealer.rs @@ -17,20 +17,20 @@ use std::{iter, marker::PhantomData}; +use generic_ec::{Curve, NonZero, Point, Scalar, SecretScalar}; +use generic_ec_zkp::polynomial::Polynomial; use paillier_zk::libpaillier::unknown_order::BigNumber; use paillier_zk::BigNumberExt; use rand_core::{CryptoRng, RngCore}; use thiserror::Error; -use generic_ec::{Curve, NonZero, Point, Scalar, SecretScalar}; - use crate::{ key_share::{ DirtyAuxInfo, DirtyIncompleteKeyShare, DirtyKeyShare, IncompleteKeyShare, InvalidKeyShare, KeyShare, PartyAux, VssSetup, }, security_level::SecurityLevel, - utils::{polynomial_value, sample_bigint_in_mult_group}, + utils::sample_bigint_in_mult_group, }; /// Construct a trusted dealer builder @@ -110,14 +110,11 @@ impl TrustedDealerBuilder { .collect::>>() .ok_or(Reason::DeriveKeyShareIndex)?; let (shared_public_key, secret_shares) = if let Some(t) = self.t { - let polynomial_coef = iter::once(shared_secret_key) - .chain(iter::repeat_with(|| SecretScalar::::random(rng)).take((t - 1).into())) - .collect::>(); - let f = |x: &Scalar| polynomial_value(Scalar::zero(), x, &polynomial_coef); - let pk = Point::generator() * f(&Scalar::zero()); + let f = Polynomial::sample_with_const_term(rng, usize::from(t) - 1, shared_secret_key); + let pk = Point::generator() * f.value::<_, Scalar<_>>(&Scalar::zero()); let shares = key_shares_indexes .iter() - .map(|I_i| f(I_i)) + .map(|I_i| f.value(I_i)) .map(|mut x_i| SecretScalar::new(&mut x_i)) .collect::>(); (pk, shares) diff --git a/cggmp21/src/utils.rs b/cggmp21/src/utils.rs index 69827c9..aafd998 100644 --- a/cggmp21/src/utils.rs +++ b/cggmp21/src/utils.rs @@ -1,5 +1,5 @@ use digest::Digest; -use generic_ec::{Curve, NonZero, Scalar, SecretScalar}; +use generic_ec::{Curve, Scalar}; use paillier_zk::libpaillier::{unknown_order::BigNumber, EncryptionKey}; use paillier_zk::{ group_element_vs_paillier_encryption_in_range as pi_log, @@ -53,10 +53,12 @@ impl SecurityParams { l_x: L::ELL, l_y: L::ELL_PRIME, epsilon: L::EPSILON, + q: L::q(), }, pi_log: pi_log::SecurityParams { l: L::ELL, epsilon: L::EPSILON, + q: L::q(), }, pi_enc: pi_enc::SecurityParams { l: L::ELL, @@ -211,8 +213,8 @@ pub fn sqrt(x: &BigNumber) -> BigNumber { let mut low = BigNumber::one(); let mut high = x.clone(); while low < &high - 1 { - let mid = (&high + &low) / 2; - let test: BigNumber = &mid * ∣ + let mid: BigNumber = (&high + &low) / 2; + let test = &mid * ∣ match test.cmp(x) { std::cmp::Ordering::Equal => return mid, std::cmp::Ordering::Less => { @@ -242,51 +244,6 @@ where (oks, errs) } -/// Calculates lagrange coefficient $\lambda_j$ to interpolate a polynomial at point $x$ -/// -/// Lagrange coefficient can be used to turn polynomial key shares into additive -/// key shares. -/// -/// ## Inputs -/// -/// `xs` denotes the points with known values that define the polynomial. `j` is a index -/// of element in `xs` for which lagrange coefficient is calculated. `x` is a point at -/// which the polynomial is interpolated. -/// -/// `xs` usually refer to "index of a party" of MPC protocol, and shared secret is assigned -/// a coordinate `x=0`. For that reason, elements of `xs` are restricted to be non-zero to -/// avoid an implementation flaw when one of the parties can occupy `xs[j] = 0`. -/// -/// ## Example -/// E.g. we have a polynomial $f(x)$ with $deg(f) = 1$, and we have key shares -/// $(I_0, x_0 = f(I_0)), (I_1, x_1 = f(I_1)), (I_2, x_2 = f(I_2))$ (where $I_i$ -/// are distinct non-zero publicly known field elements) which all together share -/// a secret $\sk = f(0)$ in 2-out-of-3 scheme. We can take any of two key shares -/// and reconstruct a secret `sk`: -/// -/// $$\sk = f(0) = \lambda_0 \cdot x_0 + \lambda_1 \cdot x_2$$ -/// -/// where `lambda_0 = lagrange_coefficient(Scalar::zero(), 0, &[I_0, I_2])`, -/// `lambda_1 = lagrange_coefficient(Scalar::zero(), 1, &[I_0, I_2])`. -/// -/// ## Returns -/// Returns `None` if `j >= xs.len()` or if there's `m` such that `xs[j] == xs[m]` or -/// `x == xs[m]`. Note that, generally, lagrange interpolation is only defined when -/// elements in `xs` are pairwise distinct. -pub fn lagrange_coefficient( - x: Scalar, - j: u16, - xs: &[NonZero>], -) -> Option>> { - let nom = but_nth(j, xs).map(|x_m| x - x_m).product::>(); - - let x_j = xs.get(usize::from(j))?; - let denom = but_nth(j, xs).map(|x_m| x_j - x_m).product::>(); - let denom_inv = denom.invert()?; - - NonZero::from_scalar(nom * denom_inv) -} - /// Returns `[list[indexes[0]], list[indexes[1]], ..., list[indexes[n-1]]]` /// /// Result is `None` if any of `indexes[i]` is out of range of `list` @@ -297,48 +254,6 @@ pub fn subset + Copy>(indexes: &[I], list: &[T]) -> Opt .collect() } -/// `coefs` is polynomial coefficients, `coefs[i]` corresponding to `x^i` -pub fn polynomial_value(zero: C, point: &A, coefs: &[B]) -> C -where - C: for<'a> core::ops::Mul<&'a A, Output = C>, - C: for<'a> core::ops::Add<&'a B, Output = C>, -{ - coefs.iter().rev().fold(zero, |r, c| r * point + c) -} - -/// Returns sum of polynomials -/// -/// `polynomials_coefs` are polynomial coefficients, `polynomials_coefs[i][j]` corresponds to i-th -/// polyinomial coef of `x^j` -pub fn polynomials_sum<'c, C: 'c>(polynomials_coefs: impl IntoIterator) -> Vec -where - for<'r> &'r C: core::ops::Add<&'r C, Output = C>, - C: Clone, -{ - let mut polynomials = polynomials_coefs.into_iter(); - let Some(mut sum) = polynomials.next().map(|c| c.to_vec()) else { return vec![] }; - - for coefs in polynomials { - sum.iter_mut() - .zip(coefs) - .for_each(|(s, coef_i)| *s = &*s + coef_i); - if coefs.len() > sum.len() { - let sum_len = sum.len(); - sum.extend_from_slice(&coefs[sum_len..]) - } - } - - sum -} - -pub fn sample_polynomial(t: usize, rng: &mut R) -> Vec> -where - E: Curve, - R: RngCore + rand_core::CryptoRng, -{ - (0..t).map(|_| SecretScalar::random(rng)).collect() -} - #[cfg(test)] mod test { #[test] @@ -365,94 +280,8 @@ mod test { let x = BigNumber::from_rng(&modulo, &mut rng); let root = sqrt(&x); assert!(&root * &root <= x); - let root = root + 1; + let root: BigNumber = root + 1; assert!(&root * &root > x); } } } - -#[cfg(test)] -#[generic_tests::define] -mod generic_test { - use std::iter; - - use generic_ec::{Curve, NonZero, Scalar}; - use rand::Rng; - use rand_dev::DevRng; - - use super::{lagrange_coefficient, polynomial_value}; - - #[test] - fn lagrange_coefficient_reconstructs_secret() { - let mut rng = DevRng::new(); - - // Polynomial of degree 1, f(x) = coef[0] + coef[1] * x - let polynomial_coefs = [Scalar::random(&mut rng), Scalar::random(&mut rng)]; - let f = |x: &Scalar| polynomial_value(Scalar::zero(), x, &polynomial_coefs); - - // I_j represents share index of j-th party. Each party should have a - // distinct non-zero index - let I = |i: u16| NonZero::from_scalar(Scalar::::from(i + 1)).unwrap(); - let I_0 = I(0); - let I_1 = I(1); - let I_2 = I(2); - - // x_j represents a secret key share of j-th party - let x_0 = f(&I_0); - let x_1 = f(&I_1); - let x_2 = f(&I_2); - - // key shares above share a secret key `sk` in 2-out-of-3 scheme - let sk = f(&Scalar::zero()); - assert_eq!(sk, polynomial_coefs[0]); - - // We take x_0 and x_2 to reconstruct sk - let lambda_0 = lagrange_coefficient(Scalar::zero(), 0, &[I_0, I_2]).unwrap(); - let lambda_2 = lagrange_coefficient(Scalar::zero(), 1, &[I_0, I_2]).unwrap(); - - let reconstructed_sk = x_0 * lambda_0 + x_2 * lambda_2; - assert_eq!(sk, reconstructed_sk); - - // We can also reconstruct x_1 - let lambda_0 = lagrange_coefficient(I_1.into(), 0, &[I_0, I_2]).unwrap(); - let lambda_2 = lagrange_coefficient(I_1.into(), 1, &[I_0, I_2]).unwrap(); - - let reconstructed_x_1 = x_0 * lambda_0 + x_2 * lambda_2; - assert_eq!(x_1, reconstructed_x_1); - } - - #[test] - fn polynomials_sum() { - let mut rng = DevRng::new(); - - // Sample 10 polynomials of different size - let polynomials: Vec>> = iter::repeat_with(|| { - let len = rng.gen_range(5..15); - iter::repeat_with(|| Scalar::random(&mut rng)) - .take(len) - .collect() - }) - .take(10) - .collect(); - - // Calculate sum of polynomials - let polynomials_sum = - super::polynomials_sum(polynomials.iter().map(|coefs| coefs.as_slice())); - - // Sample a random point and evaluate polynomial value at this point - let point = Scalar::random(&mut rng); - - let value_actual = super::polynomial_value(Scalar::zero(), &point, &polynomials_sum); - let value_exected: Scalar = polynomials - .iter() - .map(|coefs| super::polynomial_value(Scalar::zero(), &point, coefs)) - .sum(); - - assert_eq!(value_exected, value_actual); - } - - #[instantiate_tests()] - mod secp256k1 {} - #[instantiate_tests()] - mod secp256r1 {} -} diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 197644a..211138d 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -20,7 +20,7 @@ rand_chacha = "0.3" sha2 = "0.10" round-based = { git = "https://github.com/Zengo-X/round-based-protocol", branch = "round-based2", features = ["derive", "dev"] } -generic-ec = { git = "https://github.com/dfns-labs/generic-ec", branch = "d", features = ["serde", "all-curves"] } +generic-ec = { git = "https://github.com/dfns-labs/generic-ec", branch = "m", features = ["serde", "all-curves"] } tokio = { version = "1", features = ["macros"] } futures = "0.3"