From c6284b85a4c3ccb91e336f30cda9b11d0b6bea9f Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Wed, 1 Mar 2023 01:06:13 -0500 Subject: [PATCH] 3.6.8 Simplify offset splitting This wasn't done prior to be 'leaderless', as now the participant with the lowest ID has an extra step, yet this is still trivial. There's also notable performance benefits to not taking the previous dividing approach, which performed an exp. --- crypto/dkg/src/lib.rs | 36 ++++++++++++++++++++---------------- crypto/frost/src/sign.rs | 2 +- docs/cryptography/FROST.md | 6 ++---- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/crypto/dkg/src/lib.rs b/crypto/dkg/src/lib.rs index e513665d..ce0ce400 100644 --- a/crypto/dkg/src/lib.rs +++ b/crypto/dkg/src/lib.rs @@ -401,31 +401,35 @@ impl ThresholdKeys { self.core.serialize() } - pub fn view(&self, included: &[Participant]) -> Result, DkgError<()>> { + pub fn view(&self, mut included: Vec) -> Result, DkgError<()>> { if (included.len() < self.params().t.into()) || (usize::from(self.params().n) < included.len()) { Err(DkgError::InvalidSigningSet)?; } + included.sort(); - let offset_share = self.offset.unwrap_or_else(C::F::zero) * - C::F::from(included.len().try_into().unwrap()).invert().unwrap(); - let offset_verification_share = C::generator() * offset_share; + let mut secret_share = + Zeroizing::new(lagrange::(self.params().i, &included) * self.secret_share().deref()); + + let mut verification_shares = self.verification_shares(); + for (i, share) in verification_shares.iter_mut() { + *share *= lagrange::(*i, &included); + } + + // The offset is included by adding it to the participant with the lowest ID + let offset = self.offset.unwrap_or_else(C::F::zero); + if included[0] == self.params().i() { + *secret_share += offset; + } + *verification_shares.get_mut(&included[0]).unwrap() += C::generator() * offset; Ok(ThresholdView { - offset: self.offset.unwrap_or_else(C::F::zero), + offset, group_key: self.group_key(), - secret_share: Zeroizing::new( - (lagrange::(self.params().i, included) * self.secret_share().deref()) + offset_share, - ), + secret_share, original_verification_shares: self.verification_shares(), - verification_shares: self - .verification_shares() - .iter() - .map(|(l, share)| { - (*l, (*share * lagrange::(*l, included)) + offset_verification_share) - }) - .collect(), - included: included.to_vec(), + verification_shares, + included, }) } } diff --git a/crypto/frost/src/sign.rs b/crypto/frost/src/sign.rs index b869e610..9634b161 100644 --- a/crypto/frost/src/sign.rs +++ b/crypto/frost/src/sign.rs @@ -318,7 +318,7 @@ impl> SignMachine for AlgorithmSignMachi } } - let view = self.params.keys.view(&included).unwrap(); + let view = self.params.keys.view(included.clone()).unwrap(); validate_map(&preprocesses, &included, multisig_params.i())?; { diff --git a/docs/cryptography/FROST.md b/docs/cryptography/FROST.md index 9f6b3378..464052e2 100644 --- a/docs/cryptography/FROST.md +++ b/docs/cryptography/FROST.md @@ -39,10 +39,8 @@ elements instead of `2n`. Finally, to support additive offset signing schemes (accounts, stealth addresses, randomization), it's possible to specify a scalar offset for keys. The public key signed for is also offset by this value. During the signing -process, the offset is explicitly transcripted. Then, the offset is divided by -`p`, the amount of participating signers, and each signer adds it to their -post-interpolation key share. This maintains a leaderless protocol while still -being correct. +process, the offset is explicitly transcripted. Then, the offset is added to the +participant with the lowest ID. # Caching