From 9e8e134ef7c02b7a9914feddbd4048721a2c62a6 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Mon, 5 Aug 2024 06:32:37 -0400 Subject: [PATCH] Replace Interpolation::None with Interpolation::Constant Allows the MuSig DKG to keep the secret share as the original private key, enabling deriving FROST nonces consistently regardless of the MuSig context. --- crypto/dkg/src/lib.rs | 63 ++++++++++++++++++------------------- crypto/dkg/src/musig.rs | 24 +++++++------- crypto/dkg/src/promote.rs | 2 +- crypto/dkg/src/tests/mod.rs | 3 +- 4 files changed, 45 insertions(+), 47 deletions(-) diff --git a/crypto/dkg/src/lib.rs b/crypto/dkg/src/lib.rs index 32c73963..48037bcd 100644 --- a/crypto/dkg/src/lib.rs +++ b/crypto/dkg/src/lib.rs @@ -209,21 +209,16 @@ mod lib { } } - #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] - #[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize))] - pub(crate) enum Interpolation { - None, + #[derive(Clone, PartialEq, Eq, Debug, Zeroize)] + pub(crate) enum Interpolation { + Constant(Vec), Lagrange, } - impl Interpolation { - pub(crate) fn interpolation_factor( - self, - i: Participant, - included: &[Participant], - ) -> F { + impl Interpolation { + pub(crate) fn interpolation_factor(&self, i: Participant, included: &[Participant]) -> F { match self { - Interpolation::None => F::ONE, + Interpolation::Constant(c) => c[usize::from(u16::from(i) - 1)], Interpolation::Lagrange => { let i_f = F::from(u64::from(u16::from(i))); @@ -254,7 +249,7 @@ mod lib { /// Threshold Parameters. pub(crate) params: ThresholdParams, /// The interpolation method used. - pub(crate) interpolation: Interpolation, + pub(crate) interpolation: Interpolation, /// Secret share key. pub(crate) secret_share: Zeroizing, @@ -291,21 +286,14 @@ mod lib { impl ThresholdCore { pub(crate) fn new( params: ThresholdParams, - interpolation: Interpolation, + interpolation: Interpolation, secret_share: Zeroizing, verification_shares: HashMap, ) -> ThresholdCore { let t = (1 ..= params.t()).map(Participant).collect::>(); - ThresholdCore { - params, - interpolation, - secret_share, - group_key: t - .iter() - .map(|i| verification_shares[i] * interpolation.interpolation_factor::(*i, &t)) - .sum(), - verification_shares, - } + let group_key = + t.iter().map(|i| verification_shares[i] * interpolation.interpolation_factor(*i, &t)).sum(); + ThresholdCore { params, interpolation, secret_share, group_key, verification_shares } } /// Parameters for these keys. @@ -334,10 +322,15 @@ mod lib { writer.write_all(&self.params.t.to_le_bytes())?; writer.write_all(&self.params.n.to_le_bytes())?; writer.write_all(&self.params.i.to_bytes())?; - writer.write_all(match self.interpolation { - Interpolation::None => &[0], - Interpolation::Lagrange => &[1], - })?; + match &self.interpolation { + Interpolation::Constant(c) => { + writer.write_all(&[0])?; + for c in c { + writer.write_all(c.to_repr().as_ref())?; + } + } + Interpolation::Lagrange => writer.write_all(&[1])?, + }; let mut share_bytes = self.secret_share.to_repr(); writer.write_all(share_bytes.as_ref())?; share_bytes.as_mut().zeroize(); @@ -389,7 +382,13 @@ mod lib { let mut interpolation = [0]; reader.read_exact(&mut interpolation)?; let interpolation = match interpolation[0] { - 0 => Interpolation::None, + 0 => Interpolation::Constant({ + let mut res = Vec::with_capacity(usize::from(n)); + for _ in 0 .. n { + res.push(C::read_F(reader)?); + } + res + }), 1 => Interpolation::Lagrange, _ => Err(io::Error::other("invalid interpolation method"))?, }; @@ -426,7 +425,7 @@ mod lib { /// View of keys, interpolated and offset for usage. #[derive(Clone)] pub struct ThresholdView { - interpolation: Interpolation, + interpolation: Interpolation, offset: C::F, group_key: C::G, included: Vec, @@ -525,13 +524,13 @@ mod lib { included.sort(); let mut secret_share = Zeroizing::new( - self.core.interpolation.interpolation_factor::(self.params().i(), &included) * + self.core.interpolation.interpolation_factor(self.params().i(), &included) * self.secret_share().deref(), ); let mut verification_shares = self.verification_shares(); for (i, share) in &mut verification_shares { - *share *= self.core.interpolation.interpolation_factor::(*i, &included); + *share *= self.core.interpolation.interpolation_factor(*i, &included); } // The offset is included by adding it to the participant with the lowest ID @@ -542,7 +541,7 @@ mod lib { *verification_shares.get_mut(&included[0]).unwrap() += C::generator() * offset; Ok(ThresholdView { - interpolation: self.core.interpolation, + interpolation: self.core.interpolation.clone(), offset, group_key: self.group_key(), secret_share, diff --git a/crypto/dkg/src/musig.rs b/crypto/dkg/src/musig.rs index 0998e537..c0cdb852 100644 --- a/crypto/dkg/src/musig.rs +++ b/crypto/dkg/src/musig.rs @@ -102,26 +102,26 @@ pub fn musig( binding.push(binding_factor::(transcript.clone(), i)); } - // Multiply our private key by our binding factor - let mut secret_share = private_key.clone(); - *secret_share *= binding[pos]; + // Our secret share is our private key + let secret_share = private_key.clone(); // Calculate verification shares let mut verification_shares = HashMap::new(); let mut group_key = C::G::identity(); - for (l, (key, binding)) in keys.iter().zip(binding).enumerate() { - let bound = *key * binding; - group_key += bound; + for l in 1 ..= keys_len { + let key = keys[usize::from(l) - 1]; + group_key += key * binding[usize::from(l - 1)]; // These errors also shouldn't be possible, for the same reasons as documented above - verification_shares.insert( - Participant::new(1 + u16::try_from(l).map_err(|_| DkgError::InvalidSigningSet)?) - .ok_or(DkgError::InvalidSigningSet)?, - bound, - ); + verification_shares.insert(Participant::new(l).ok_or(DkgError::InvalidSigningSet)?, key); } debug_assert_eq!(C::generator() * secret_share.deref(), verification_shares[¶ms.i()]); debug_assert_eq!(musig_key::(context, keys).unwrap(), group_key); - Ok(ThresholdCore::new(params, Interpolation::None, secret_share, verification_shares)) + Ok(ThresholdCore::new( + params, + Interpolation::Constant(binding), + secret_share, + verification_shares, + )) } diff --git a/crypto/dkg/src/promote.rs b/crypto/dkg/src/promote.rs index d7a98b59..c8dcaed0 100644 --- a/crypto/dkg/src/promote.rs +++ b/crypto/dkg/src/promote.rs @@ -113,7 +113,7 @@ impl> GeneratorPromotion< Ok(ThresholdKeys { core: Arc::new(ThresholdCore::new( params, - self.base.core.interpolation, + self.base.core.interpolation.clone(), self.base.secret_share().clone(), verification_shares, )), diff --git a/crypto/dkg/src/tests/mod.rs b/crypto/dkg/src/tests/mod.rs index 2a2d25b3..4399d72a 100644 --- a/crypto/dkg/src/tests/mod.rs +++ b/crypto/dkg/src/tests/mod.rs @@ -47,8 +47,7 @@ pub fn recover_key(keys: &HashMap> let group_private = keys.iter().fold(C::F::ZERO, |accum, (i, keys)| { accum + - (first.core.interpolation.interpolation_factor::(*i, &included) * - keys.secret_share().deref()) + (first.core.interpolation.interpolation_factor(*i, &included) * keys.secret_share().deref()) }); assert_eq!(C::generator() * group_private, first.group_key(), "failed to recover keys"); group_private