git checkout -f next ./crypto

Proceeds to remove the eVRF DKG after, only keeping what's relevant to this
branch alone.
This commit is contained in:
Luke Parker
2025-08-15 17:33:22 -04:00
parent 078d6e51e5
commit 15a9cbef40
30 changed files with 318 additions and 213 deletions

View File

@@ -4,7 +4,6 @@
use core::fmt::{self, Debug};
#[cfg(feature = "std")]
use thiserror::Error;
use zeroize::Zeroize;
@@ -63,8 +62,7 @@ impl fmt::Display for Participant {
}
/// Various errors possible during key generation.
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "std", derive(Error))]
#[derive(Clone, PartialEq, Eq, Debug, Error)]
pub enum DkgError<B: Clone + PartialEq + Eq + Debug> {
/// A parameter was zero.
#[cfg_attr(feature = "std", error("a parameter was 0 (threshold {0}, participants {1})"))]
@@ -205,25 +203,37 @@ mod lib {
}
}
/// Calculate the lagrange coefficient for a signing set.
pub fn lagrange<F: PrimeField>(i: Participant, included: &[Participant]) -> F {
let i_f = F::from(u64::from(u16::from(i)));
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
pub(crate) enum Interpolation<F: Zeroize + PrimeField> {
Constant(Vec<F>),
Lagrange,
}
let mut num = F::ONE;
let mut denom = F::ONE;
for l in included {
if i == *l {
continue;
impl<F: Zeroize + PrimeField> Interpolation<F> {
pub(crate) fn interpolation_factor(&self, i: Participant, included: &[Participant]) -> F {
match self {
Interpolation::Constant(c) => c[usize::from(u16::from(i) - 1)],
Interpolation::Lagrange => {
let i_f = F::from(u64::from(u16::from(i)));
let mut num = F::ONE;
let mut denom = F::ONE;
for l in included {
if i == *l {
continue;
}
let share = F::from(u64::from(u16::from(*l)));
num *= share;
denom *= share - i_f;
}
// Safe as this will only be 0 if we're part of the above loop
// (which we have an if case to avoid)
num * denom.invert().unwrap()
}
}
let share = F::from(u64::from(u16::from(*l)));
num *= share;
denom *= share - i_f;
}
// Safe as this will only be 0 if we're part of the above loop
// (which we have an if case to avoid)
num * denom.invert().unwrap()
}
/// Keys and verification shares generated by a DKG.
@@ -232,6 +242,8 @@ mod lib {
pub struct ThresholdCore<C: Ciphersuite> {
/// Threshold Parameters.
pub(crate) params: ThresholdParams,
/// The interpolation method used.
pub(crate) interpolation: Interpolation<C::F>,
/// Secret share key.
pub(crate) secret_share: Zeroizing<C::F>,
@@ -246,6 +258,7 @@ mod lib {
fmt
.debug_struct("ThresholdCore")
.field("params", &self.params)
.field("interpolation", &self.interpolation)
.field("group_key", &self.group_key)
.field("verification_shares", &self.verification_shares)
.finish_non_exhaustive()
@@ -255,6 +268,7 @@ mod lib {
impl<C: Ciphersuite> Zeroize for ThresholdCore<C> {
fn zeroize(&mut self) {
self.params.zeroize();
self.interpolation.zeroize();
self.secret_share.zeroize();
self.group_key.zeroize();
for share in self.verification_shares.values_mut() {
@@ -266,16 +280,14 @@ mod lib {
impl<C: Ciphersuite> ThresholdCore<C> {
pub(crate) fn new(
params: ThresholdParams,
interpolation: Interpolation<C::F>,
secret_share: Zeroizing<C::F>,
verification_shares: HashMap<Participant, C::G>,
) -> ThresholdCore<C> {
let t = (1 ..= params.t()).map(Participant).collect::<Vec<_>>();
ThresholdCore {
params,
secret_share,
group_key: t.iter().map(|i| verification_shares[i] * lagrange::<C::F>(*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.
@@ -304,6 +316,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())?;
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();
@@ -352,6 +373,20 @@ mod lib {
)
};
let mut interpolation = [0];
reader.read_exact(&mut interpolation)?;
let interpolation = match interpolation[0] {
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"))?,
};
let secret_share = Zeroizing::new(C::read_F(reader)?);
let mut verification_shares = HashMap::new();
@@ -361,6 +396,7 @@ mod lib {
Ok(ThresholdCore::new(
ThresholdParams::new(t, n, i).map_err(|_| io::Error::other("invalid parameters"))?,
interpolation,
secret_share,
verification_shares,
))
@@ -383,6 +419,7 @@ mod lib {
/// View of keys, interpolated and offset for usage.
#[derive(Clone)]
pub struct ThresholdView<C: Ciphersuite> {
interpolation: Interpolation<C::F>,
offset: C::F,
group_key: C::G,
included: Vec<Participant>,
@@ -395,6 +432,7 @@ mod lib {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt
.debug_struct("ThresholdView")
.field("interpolation", &self.interpolation)
.field("offset", &self.offset)
.field("group_key", &self.group_key)
.field("included", &self.included)
@@ -480,12 +518,13 @@ mod lib {
included.sort();
let mut secret_share = Zeroizing::new(
lagrange::<C::F>(self.params().i(), &included) * self.secret_share().deref(),
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 *= lagrange::<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
@@ -496,6 +535,7 @@ mod lib {
*verification_shares.get_mut(&included[0]).unwrap() += C::generator() * offset;
Ok(ThresholdView {
interpolation: self.core.interpolation.clone(),
offset,
group_key: self.group_key(),
secret_share,
@@ -528,6 +568,14 @@ mod lib {
&self.included
}
/// Return the interpolation factor for a signer.
pub fn interpolation_factor(&self, participant: Participant) -> Option<C::F> {
if !self.included.contains(&participant) {
None?
}
Some(self.interpolation.interpolation_factor(participant, &self.included))
}
/// Return the interpolated, offset secret share.
pub fn secret_share(&self) -> &Zeroizing<C::F> {
&self.secret_share