diff --git a/src/common.rs b/src/common.rs index 0b6834a3..8320b85c 100644 --- a/src/common.rs +++ b/src/common.rs @@ -22,6 +22,28 @@ use crate::{ /// A merkle root is a 256 bit hash pub type MerkleRoot = [u8; 32]; +/// A Polynomial where the parameters are not necessarily the same type as the args +pub struct Polynomial { + /// parameters for the polynomial + pub data: Vec, + _x: std::marker::PhantomData, +} + +impl< + Param: Clone + Zero + Add + std::ops::AddAssign<>::Output>, + Arg: Clone + std::ops::Mul, + > Polynomial +{ + /// evaluate the polynomial with the passed arg + pub fn eval(&self, x: Arg) -> Param { + let mut ret = Param::zero(); + for i in 0..self.data.len() { + ret += x.clone() * self.data[i].clone(); + } + ret + } +} + #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] /// A commitment to a polynonial, with a Schnorr proof of ownership bound to the ID pub struct PolyCommitment { diff --git a/src/compute.rs b/src/compute.rs index de4595a2..388b8b4d 100644 --- a/src/compute.rs +++ b/src/compute.rs @@ -130,6 +130,16 @@ pub fn poly(x: &Scalar, f: &Vec) -> Result { Point::multimult(s, f.clone()) } +/// Evaluate the public polynomial `f` at scalar `x` using multi-exponentiation +#[allow(clippy::ptr_arg)] +pub fn private_poly(x: Scalar, f: &Vec) -> Scalar { + let mut sum = Scalar::zero(); + for i in 0..f.len() { + sum += x * f[i]; + } + sum +} + /// Create a BIP340 compliant tagged hash by double hashing the tag pub fn tagged_hash(tag: &str) -> Sha256 { let mut hasher = Sha256::new(); diff --git a/src/net.rs b/src/net.rs index 794c3f7b..e8287e3a 100644 --- a/src/net.rs +++ b/src/net.rs @@ -125,6 +125,8 @@ impl Signable for Message { pub struct DkgBegin { /// DKG round ID pub dkg_id: u64, + /// Keep the constant factor so DKG will produce the same key + pub keep_constant: bool, } impl Signable for DkgBegin { diff --git a/src/state_machine/coordinator/fire.rs b/src/state_machine/coordinator/fire.rs index 93e04a69..17f4a063 100644 --- a/src/state_machine/coordinator/fire.rs +++ b/src/state_machine/coordinator/fire.rs @@ -363,6 +363,7 @@ impl Coordinator { ); let dkg_begin = DkgBegin { dkg_id: self.current_dkg_id, + keep_constant: false, }; let dkg_begin_packet = Packet { sig: dkg_begin diff --git a/src/state_machine/coordinator/frost.rs b/src/state_machine/coordinator/frost.rs index 9409388d..cc5e7ae1 100644 --- a/src/state_machine/coordinator/frost.rs +++ b/src/state_machine/coordinator/frost.rs @@ -194,6 +194,7 @@ impl Coordinator { ); let dkg_begin = DkgBegin { dkg_id: self.current_dkg_id, + keep_constant: false, }; let dkg_begin_packet = Packet { diff --git a/src/state_machine/signer/mod.rs b/src/state_machine/signer/mod.rs index 2bd74f45..f68b18d4 100644 --- a/src/state_machine/signer/mod.rs +++ b/src/state_machine/signer/mod.rs @@ -260,14 +260,14 @@ impl Signer { } /// Reset internal state - pub fn reset(&mut self, dkg_id: u64, rng: &mut T) { + pub fn reset(&mut self, dkg_id: u64, keep_constant: bool, rng: &mut T) { self.dkg_id = dkg_id; self.commitments.clear(); self.decrypted_shares.clear(); self.decryption_keys.clear(); self.invalid_private_shares.clear(); self.public_nonces.clear(); - self.signer.reset_polys(rng); + self.signer.reset_polys(keep_constant, rng); self.dkg_public_shares.clear(); self.dkg_private_shares.clear(); self.dkg_private_begin_msg = None; @@ -594,7 +594,7 @@ impl Signer { fn dkg_begin(&mut self, dkg_begin: &DkgBegin) -> Result, Error> { let mut rng = OsRng; - self.reset(dkg_begin.dkg_id, &mut rng); + self.reset(dkg_begin.dkg_id, dkg_begin.keep_constant, &mut rng); self.move_to(State::DkgPublicDistribute)?; //let _party_state = self.signer.save(); diff --git a/src/traits.rs b/src/traits.rs index 08a32361..bfbc9612 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,6 +1,5 @@ use core::{cmp::PartialEq, fmt::Debug}; use hashbrown::HashMap; -use polynomial::Polynomial; use rand_core::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; @@ -15,7 +14,7 @@ use crate::{ /// The saved state required to reconstruct a party pub struct PartyState { /// The party's private polynomial - pub polynomial: Option>, + pub polynomial: Option>, /// The key IDS and associate private keys for this party pub private_keys: Vec<(u32, Scalar)>, /// The nonce being used by this party @@ -72,7 +71,7 @@ pub trait Signer: Clone + Debug + PartialEq { fn get_poly_commitments(&self, rng: &mut RNG) -> Vec; /// Reset all polynomials for this signer - fn reset_polys(&mut self, rng: &mut RNG); + fn reset_polys(&mut self, keep_constant: bool, rng: &mut RNG); /// Clear all polynomials for this signer fn clear_polys(&mut self); diff --git a/src/v1.rs b/src/v1.rs index 65f5def9..cf974959 100644 --- a/src/v1.rs +++ b/src/v1.rs @@ -1,6 +1,5 @@ use hashbrown::HashMap; use num_traits::{One, Zero}; -use polynomial::Polynomial; use rand_core::{CryptoRng, RngCore}; use tracing::warn; @@ -26,7 +25,7 @@ pub struct Party { /// The public key pub public_key: Point, /// The polynomial used for Lagrange interpolation - pub f: Option>, + pub f: Option>, num_keys: u32, threshold: u32, private_key: Scalar, @@ -92,10 +91,8 @@ impl Party { ) -> Option { if let Some(poly) = &self.f { Some(PolyCommitment { - id: ID::new(&self.id(), &poly.data()[0], rng), - poly: (0..poly.data().len()) - .map(|i| &poly.data()[i] * G) - .collect(), + id: ID::new(&self.id(), &poly[0], rng), + poly: (0..poly.len()).map(|i| &poly[i] * G).collect(), }) } else { warn!("get_poly_commitment called with no polynomial"); @@ -104,8 +101,22 @@ impl Party { } /// Make a new polynomial - pub fn reset_poly(&mut self, rng: &mut RNG) { - self.f = Some(VSS::random_poly(self.threshold - 1, rng)); + pub fn reset_poly(&mut self, keep_constant: bool, rng: &mut RNG) { + let constant = if let Some(poly) = &self.f { + if keep_constant { + Some(poly[0].clone()) + } else { + None + } + } else { + None + }; + + if let Some(c) = constant { + self.f = Some(VSS::random_poly_with_constant(self.threshold - 1, c, rng)); + } else { + self.f = Some(VSS::random_poly(self.threshold - 1, rng)); + } } /// Clear the polynomial @@ -118,7 +129,7 @@ impl Party { if let Some(poly) = &self.f { let mut shares = HashMap::new(); for i in 1..self.num_keys + 1 { - shares.insert(i, poly.eval(compute::id(i))); + shares.insert(i, compute::private_poly(compute::id(i), poly)); } shares } else { @@ -546,9 +557,9 @@ impl traits::Signer for Signer { polys } - fn reset_polys(&mut self, rng: &mut RNG) { + fn reset_polys(&mut self, keep_constant: bool, rng: &mut RNG) { for party in self.parties.iter_mut() { - party.reset_poly(rng); + party.reset_poly(keep_constant, rng); } } diff --git a/src/v2.rs b/src/v2.rs index f738a8c2..cc14df9d 100644 --- a/src/v2.rs +++ b/src/v2.rs @@ -1,6 +1,5 @@ use hashbrown::HashMap; use num_traits::{One, Zero}; -use polynomial::Polynomial; use rand_core::{CryptoRng, RngCore}; use tracing::warn; @@ -29,7 +28,7 @@ pub struct Party { num_keys: u32, num_parties: u32, threshold: u32, - f: Option>, + f: Option>, private_keys: HashMap, group_key: Point, nonce: Nonce, @@ -72,10 +71,8 @@ impl Party { ) -> Option { if let Some(poly) = &self.f { Some(PolyCommitment { - id: ID::new(&self.id(), &poly.data()[0], rng), - poly: (0..poly.data().len()) - .map(|i| &poly.data()[i] * G) - .collect(), + id: ID::new(&self.id(), &poly[0], rng), + poly: (0..poly.len()).map(|i| &poly[i] * G).collect(), }) } else { warn!("get_poly_commitment called with no polynomial"); @@ -88,7 +85,7 @@ impl Party { let mut shares = HashMap::new(); if let Some(poly) = &self.f { for i in 1..self.num_keys + 1 { - shares.insert(i, poly.eval(compute::id(i))); + shares.insert(i, compute::private_poly(compute::id(i), poly)); } } else { warn!("get_poly_commitment called with no polynomial"); @@ -478,8 +475,22 @@ impl traits::Signer for Party { } } - fn reset_polys(&mut self, rng: &mut RNG) { - self.f = Some(VSS::random_poly(self.threshold - 1, rng)); + fn reset_polys(&mut self, keep_constant: bool, rng: &mut RNG) { + let constant = if let Some(poly) = &self.f { + if keep_constant { + Some(poly[0].clone()) + } else { + None + } + } else { + None + }; + + if let Some(c) = constant { + self.f = Some(VSS::random_poly_with_constant(self.threshold - 1, c, rng)); + } else { + self.f = Some(VSS::random_poly(self.threshold - 1, rng)); + } } fn clear_polys(&mut self) { diff --git a/src/vss.rs b/src/vss.rs index 4ffbcb45..2bd7dd26 100644 --- a/src/vss.rs +++ b/src/vss.rs @@ -1,4 +1,3 @@ -use polynomial::Polynomial; use rand_core::{CryptoRng, RngCore}; use crate::curve::scalar::Scalar; @@ -8,8 +7,19 @@ pub struct VSS {} impl VSS { /// Construct a random polynomial of the passed degree `n` - pub fn random_poly(n: u32, rng: &mut RNG) -> Polynomial { - let params: Vec = (0..n + 1).map(|_| Scalar::random(rng)).collect(); - Polynomial::new(params) + pub fn random_poly(n: u32, rng: &mut RNG) -> Vec { + (0..n + 1).map(|_| Scalar::random(rng)).collect() + } + + /// Construct a random polynomial of the passed degree `n` using the passed constant term + pub fn random_poly_with_constant( + n: u32, + constant: Scalar, + rng: &mut RNG, + ) -> Vec { + let mut params: Vec = (0..n + 1).map(|_| Scalar::random(rng)).collect(); + params[0] = constant; + + params } }