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.
This commit is contained in:
Luke Parker
2024-08-05 06:32:37 -04:00
parent f08faeadff
commit 9e8e134ef7
4 changed files with 45 additions and 47 deletions

View File

@@ -209,21 +209,16 @@ mod lib {
} }
} }
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] #[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize))] pub(crate) enum Interpolation<F: Zeroize + PrimeField> {
pub(crate) enum Interpolation { Constant(Vec<F>),
None,
Lagrange, Lagrange,
} }
impl Interpolation { impl<F: Zeroize + PrimeField> Interpolation<F> {
pub(crate) fn interpolation_factor<F: PrimeField>( pub(crate) fn interpolation_factor(&self, i: Participant, included: &[Participant]) -> F {
self,
i: Participant,
included: &[Participant],
) -> F {
match self { match self {
Interpolation::None => F::ONE, Interpolation::Constant(c) => c[usize::from(u16::from(i) - 1)],
Interpolation::Lagrange => { Interpolation::Lagrange => {
let i_f = F::from(u64::from(u16::from(i))); let i_f = F::from(u64::from(u16::from(i)));
@@ -254,7 +249,7 @@ mod lib {
/// Threshold Parameters. /// Threshold Parameters.
pub(crate) params: ThresholdParams, pub(crate) params: ThresholdParams,
/// The interpolation method used. /// The interpolation method used.
pub(crate) interpolation: Interpolation, pub(crate) interpolation: Interpolation<C::F>,
/// Secret share key. /// Secret share key.
pub(crate) secret_share: Zeroizing<C::F>, pub(crate) secret_share: Zeroizing<C::F>,
@@ -291,21 +286,14 @@ mod lib {
impl<C: Ciphersuite> ThresholdCore<C> { impl<C: Ciphersuite> ThresholdCore<C> {
pub(crate) fn new( pub(crate) fn new(
params: ThresholdParams, params: ThresholdParams,
interpolation: Interpolation, interpolation: Interpolation<C::F>,
secret_share: Zeroizing<C::F>, secret_share: Zeroizing<C::F>,
verification_shares: HashMap<Participant, C::G>, verification_shares: HashMap<Participant, C::G>,
) -> ThresholdCore<C> { ) -> ThresholdCore<C> {
let t = (1 ..= params.t()).map(Participant).collect::<Vec<_>>(); let t = (1 ..= params.t()).map(Participant).collect::<Vec<_>>();
ThresholdCore { let group_key =
params, t.iter().map(|i| verification_shares[i] * interpolation.interpolation_factor(*i, &t)).sum();
interpolation, ThresholdCore { params, interpolation, secret_share, group_key, verification_shares }
secret_share,
group_key: t
.iter()
.map(|i| verification_shares[i] * interpolation.interpolation_factor::<C::F>(*i, &t))
.sum(),
verification_shares,
}
} }
/// Parameters for these keys. /// Parameters for these keys.
@@ -334,10 +322,15 @@ mod lib {
writer.write_all(&self.params.t.to_le_bytes())?; writer.write_all(&self.params.t.to_le_bytes())?;
writer.write_all(&self.params.n.to_le_bytes())?; writer.write_all(&self.params.n.to_le_bytes())?;
writer.write_all(&self.params.i.to_bytes())?; writer.write_all(&self.params.i.to_bytes())?;
writer.write_all(match self.interpolation { match &self.interpolation {
Interpolation::None => &[0], Interpolation::Constant(c) => {
Interpolation::Lagrange => &[1], 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(); let mut share_bytes = self.secret_share.to_repr();
writer.write_all(share_bytes.as_ref())?; writer.write_all(share_bytes.as_ref())?;
share_bytes.as_mut().zeroize(); share_bytes.as_mut().zeroize();
@@ -389,7 +382,13 @@ mod lib {
let mut interpolation = [0]; let mut interpolation = [0];
reader.read_exact(&mut interpolation)?; reader.read_exact(&mut interpolation)?;
let interpolation = match interpolation[0] { 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, 1 => Interpolation::Lagrange,
_ => Err(io::Error::other("invalid interpolation method"))?, _ => Err(io::Error::other("invalid interpolation method"))?,
}; };
@@ -426,7 +425,7 @@ mod lib {
/// View of keys, interpolated and offset for usage. /// View of keys, interpolated and offset for usage.
#[derive(Clone)] #[derive(Clone)]
pub struct ThresholdView<C: Ciphersuite> { pub struct ThresholdView<C: Ciphersuite> {
interpolation: Interpolation, interpolation: Interpolation<C::F>,
offset: C::F, offset: C::F,
group_key: C::G, group_key: C::G,
included: Vec<Participant>, included: Vec<Participant>,
@@ -525,13 +524,13 @@ mod lib {
included.sort(); included.sort();
let mut secret_share = Zeroizing::new( let mut secret_share = Zeroizing::new(
self.core.interpolation.interpolation_factor::<C::F>(self.params().i(), &included) * self.core.interpolation.interpolation_factor(self.params().i(), &included) *
self.secret_share().deref(), self.secret_share().deref(),
); );
let mut verification_shares = self.verification_shares(); let mut verification_shares = self.verification_shares();
for (i, share) in &mut verification_shares { for (i, share) in &mut verification_shares {
*share *= self.core.interpolation.interpolation_factor::<C::F>(*i, &included); *share *= self.core.interpolation.interpolation_factor(*i, &included);
} }
// The offset is included by adding it to the participant with the lowest ID // 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; *verification_shares.get_mut(&included[0]).unwrap() += C::generator() * offset;
Ok(ThresholdView { Ok(ThresholdView {
interpolation: self.core.interpolation, interpolation: self.core.interpolation.clone(),
offset, offset,
group_key: self.group_key(), group_key: self.group_key(),
secret_share, secret_share,

View File

@@ -102,26 +102,26 @@ pub fn musig<C: Ciphersuite>(
binding.push(binding_factor::<C>(transcript.clone(), i)); binding.push(binding_factor::<C>(transcript.clone(), i));
} }
// Multiply our private key by our binding factor // Our secret share is our private key
let mut secret_share = private_key.clone(); let secret_share = private_key.clone();
*secret_share *= binding[pos];
// Calculate verification shares // Calculate verification shares
let mut verification_shares = HashMap::new(); let mut verification_shares = HashMap::new();
let mut group_key = C::G::identity(); let mut group_key = C::G::identity();
for (l, (key, binding)) in keys.iter().zip(binding).enumerate() { for l in 1 ..= keys_len {
let bound = *key * binding; let key = keys[usize::from(l) - 1];
group_key += bound; group_key += key * binding[usize::from(l - 1)];
// These errors also shouldn't be possible, for the same reasons as documented above // These errors also shouldn't be possible, for the same reasons as documented above
verification_shares.insert( verification_shares.insert(Participant::new(l).ok_or(DkgError::InvalidSigningSet)?, key);
Participant::new(1 + u16::try_from(l).map_err(|_| DkgError::InvalidSigningSet)?)
.ok_or(DkgError::InvalidSigningSet)?,
bound,
);
} }
debug_assert_eq!(C::generator() * secret_share.deref(), verification_shares[&params.i()]); debug_assert_eq!(C::generator() * secret_share.deref(), verification_shares[&params.i()]);
debug_assert_eq!(musig_key::<C>(context, keys).unwrap(), group_key); debug_assert_eq!(musig_key::<C>(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,
))
} }

View File

@@ -113,7 +113,7 @@ impl<C1: Ciphersuite, C2: Ciphersuite<F = C1::F, G = C1::G>> GeneratorPromotion<
Ok(ThresholdKeys { Ok(ThresholdKeys {
core: Arc::new(ThresholdCore::new( core: Arc::new(ThresholdCore::new(
params, params,
self.base.core.interpolation, self.base.core.interpolation.clone(),
self.base.secret_share().clone(), self.base.secret_share().clone(),
verification_shares, verification_shares,
)), )),

View File

@@ -47,8 +47,7 @@ pub fn recover_key<C: Ciphersuite>(keys: &HashMap<Participant, ThresholdKeys<C>>
let group_private = keys.iter().fold(C::F::ZERO, |accum, (i, keys)| { let group_private = keys.iter().fold(C::F::ZERO, |accum, (i, keys)| {
accum + accum +
(first.core.interpolation.interpolation_factor::<C::F>(*i, &included) * (first.core.interpolation.interpolation_factor(*i, &included) * keys.secret_share().deref())
keys.secret_share().deref())
}); });
assert_eq!(C::generator() * group_private, first.group_key(), "failed to recover keys"); assert_eq!(C::generator() * group_private, first.group_key(), "failed to recover keys");
group_private group_private