2025-08-18 01:24:40 -04:00
|
|
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
|
|
|
#![doc = include_str!("../README.md")]
|
|
|
|
|
// This crate requires `dleq` which doesn't support no-std via std-shims
|
|
|
|
|
// #![cfg_attr(not(feature = "std"), no_std)]
|
|
|
|
|
|
2022-11-10 22:35:09 -05:00
|
|
|
use core::{marker::PhantomData, ops::Deref};
|
2022-08-13 08:49:38 -04:00
|
|
|
use std::{
|
|
|
|
|
io::{self, Read, Write},
|
|
|
|
|
collections::HashMap,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use rand_core::{RngCore, CryptoRng};
|
|
|
|
|
|
2025-08-18 01:24:40 -04:00
|
|
|
use ciphersuite::{group::GroupEncoding, Ciphersuite};
|
2022-10-29 03:54:42 -05:00
|
|
|
|
2022-08-13 08:49:38 -04:00
|
|
|
use transcript::{Transcript, RecommendedTranscript};
|
|
|
|
|
use dleq::DLEqProof;
|
|
|
|
|
|
2025-08-18 01:24:40 -04:00
|
|
|
pub use dkg::*;
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests;
|
|
|
|
|
|
|
|
|
|
/// Errors encountered when promoting keys.
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
|
|
|
|
|
pub enum PromotionError {
|
|
|
|
|
/// Invalid participant identifier.
|
|
|
|
|
#[error("invalid participant (1 <= participant <= {n}, yet participant is {participant})")]
|
|
|
|
|
InvalidParticipant {
|
|
|
|
|
/// The total amount of participants.
|
|
|
|
|
n: u16,
|
|
|
|
|
/// The specified participant.
|
|
|
|
|
participant: Participant,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// An incorrect amount of participants was specified.
|
|
|
|
|
#[error("incorrect amount of participants. {t} <= amount <= {n}, yet amount is {amount}")]
|
|
|
|
|
IncorrectAmountOfParticipants {
|
|
|
|
|
/// The threshold required.
|
|
|
|
|
t: u16,
|
|
|
|
|
/// The total amount of participants.
|
|
|
|
|
n: u16,
|
|
|
|
|
/// The amount of participants specified.
|
|
|
|
|
amount: usize,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// Participant provided an invalid proof.
|
|
|
|
|
#[error("invalid proof {0}")]
|
|
|
|
|
InvalidProof(Participant),
|
2022-08-13 08:49:38 -04:00
|
|
|
}
|
|
|
|
|
|
2023-12-17 00:01:41 -05:00
|
|
|
fn transcript<G: GroupEncoding>(key: &G, i: Participant) -> RecommendedTranscript {
|
2022-12-27 00:49:31 -05:00
|
|
|
let mut transcript = RecommendedTranscript::new(b"DKG Generator Promotion v0.2");
|
2022-11-05 18:43:36 -04:00
|
|
|
transcript.append_message(b"group_key", key.to_bytes());
|
2023-02-23 06:50:45 -05:00
|
|
|
transcript.append_message(b"participant", i.to_bytes());
|
2022-08-13 08:49:38 -04:00
|
|
|
transcript
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-29 07:08:20 -04:00
|
|
|
/// Proof of valid promotion to another generator.
|
2022-08-13 08:49:38 -04:00
|
|
|
#[derive(Clone, Copy)]
|
2022-10-29 03:54:42 -05:00
|
|
|
pub struct GeneratorProof<C: Ciphersuite> {
|
2022-08-13 08:49:38 -04:00
|
|
|
share: C::G,
|
|
|
|
|
proof: DLEqProof<C::G>,
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-29 03:54:42 -05:00
|
|
|
impl<C: Ciphersuite> GeneratorProof<C> {
|
|
|
|
|
pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
2022-08-13 08:49:38 -04:00
|
|
|
writer.write_all(self.share.to_bytes().as_ref())?;
|
2023-01-01 01:54:18 -05:00
|
|
|
self.proof.write(writer)
|
2022-08-13 08:49:38 -04:00
|
|
|
}
|
|
|
|
|
|
2022-10-29 03:54:42 -05:00
|
|
|
pub fn read<R: Read>(reader: &mut R) -> io::Result<GeneratorProof<C>> {
|
|
|
|
|
Ok(GeneratorProof {
|
|
|
|
|
share: <C as Ciphersuite>::read_G(reader)?,
|
2023-01-01 01:54:18 -05:00
|
|
|
proof: DLEqProof::read(reader)?,
|
2022-10-29 03:54:42 -05:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn serialize(&self) -> Vec<u8> {
|
|
|
|
|
let mut buf = vec![];
|
|
|
|
|
self.write(&mut buf).unwrap();
|
|
|
|
|
buf
|
2022-08-13 08:49:38 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-23 07:21:47 -05:00
|
|
|
/// Promote a set of keys from one generator to another, where the elliptic curve is the same.
|
2023-03-20 20:10:00 -04:00
|
|
|
///
|
2022-10-29 03:54:42 -05:00
|
|
|
/// Since the Ciphersuite trait additionally specifies a generator, this provides an O(n) way to
|
2023-02-23 07:21:47 -05:00
|
|
|
/// update the generator used with keys. This outperforms the key generation protocol which is
|
2023-03-20 20:10:00 -04:00
|
|
|
/// exponential.
|
2022-10-29 03:54:42 -05:00
|
|
|
pub struct GeneratorPromotion<C1: Ciphersuite, C2: Ciphersuite> {
|
|
|
|
|
base: ThresholdKeys<C1>,
|
2022-08-13 08:49:38 -04:00
|
|
|
proof: GeneratorProof<C1>,
|
|
|
|
|
_c2: PhantomData<C2>,
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-13 15:57:08 -04:00
|
|
|
impl<C1: Ciphersuite, C2: Ciphersuite<F = C1::F, G = C1::G>> GeneratorPromotion<C1, C2> {
|
2025-08-18 01:24:40 -04:00
|
|
|
/// Begin promoting keys from one generator to another.
|
|
|
|
|
///
|
|
|
|
|
/// Returns a proof this share was properly promoted.
|
2022-08-13 08:49:38 -04:00
|
|
|
pub fn promote<R: RngCore + CryptoRng>(
|
|
|
|
|
rng: &mut R,
|
2022-10-29 03:54:42 -05:00
|
|
|
base: ThresholdKeys<C1>,
|
2022-08-13 08:49:38 -04:00
|
|
|
) -> (GeneratorPromotion<C1, C2>, GeneratorProof<C1>) {
|
|
|
|
|
// Do a DLEqProof for the new generator
|
|
|
|
|
let proof = GeneratorProof {
|
2022-11-10 22:35:09 -05:00
|
|
|
share: C2::generator() * base.secret_share().deref(),
|
2022-08-13 08:49:38 -04:00
|
|
|
proof: DLEqProof::prove(
|
|
|
|
|
rng,
|
2025-08-18 01:24:40 -04:00
|
|
|
&mut transcript(&base.original_group_key(), base.params().i()),
|
2022-08-13 08:49:38 -04:00
|
|
|
&[C1::generator(), C2::generator()],
|
|
|
|
|
base.secret_share(),
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
(GeneratorPromotion { base, proof, _c2: PhantomData::<C2> }, proof)
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-29 07:08:20 -04:00
|
|
|
/// Complete promotion by taking in the proofs from all other participants.
|
2022-08-13 08:49:38 -04:00
|
|
|
pub fn complete(
|
|
|
|
|
self,
|
2023-02-23 06:50:45 -05:00
|
|
|
proofs: &HashMap<Participant, GeneratorProof<C1>>,
|
2025-08-18 01:24:40 -04:00
|
|
|
) -> Result<ThresholdKeys<C2>, PromotionError> {
|
2022-08-13 08:49:38 -04:00
|
|
|
let params = self.base.params();
|
2025-08-18 01:24:40 -04:00
|
|
|
if proofs.len() != (usize::from(params.n()) - 1) {
|
|
|
|
|
Err(PromotionError::IncorrectAmountOfParticipants {
|
|
|
|
|
t: params.n(),
|
|
|
|
|
n: params.n(),
|
|
|
|
|
amount: proofs.len() + 1,
|
|
|
|
|
})?;
|
|
|
|
|
}
|
|
|
|
|
for i in proofs.keys().copied() {
|
|
|
|
|
if u16::from(i) > params.n() {
|
|
|
|
|
Err(PromotionError::InvalidParticipant { n: params.n(), participant: i })?;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-13 08:49:38 -04:00
|
|
|
|
|
|
|
|
let mut verification_shares = HashMap::new();
|
2025-08-18 01:24:40 -04:00
|
|
|
verification_shares.insert(params.i(), self.proof.share);
|
|
|
|
|
for i in 1 ..= params.n() {
|
|
|
|
|
let i = Participant::new(i).unwrap();
|
|
|
|
|
if i == params.i() {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let proof = proofs.get(&i).unwrap();
|
2022-08-13 08:49:38 -04:00
|
|
|
proof
|
|
|
|
|
.proof
|
|
|
|
|
.verify(
|
2025-08-18 01:24:40 -04:00
|
|
|
&mut transcript(&self.base.original_group_key(), i),
|
2022-08-13 08:49:38 -04:00
|
|
|
&[C1::generator(), C2::generator()],
|
2025-08-18 01:24:40 -04:00
|
|
|
&[self.base.original_verification_share(i), proof.share],
|
2022-08-13 08:49:38 -04:00
|
|
|
)
|
2025-08-18 01:24:40 -04:00
|
|
|
.map_err(|_| PromotionError::InvalidProof(i))?;
|
2022-08-13 08:49:38 -04:00
|
|
|
verification_shares.insert(i, proof.share);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-18 01:24:40 -04:00
|
|
|
Ok(
|
|
|
|
|
ThresholdKeys::new(
|
2022-11-10 22:35:09 -05:00
|
|
|
params,
|
2025-08-18 01:24:40 -04:00
|
|
|
self.base.interpolation().clone(),
|
2022-11-10 22:35:09 -05:00
|
|
|
self.base.secret_share().clone(),
|
|
|
|
|
verification_shares,
|
2025-08-18 01:24:40 -04:00
|
|
|
)
|
|
|
|
|
.unwrap(),
|
|
|
|
|
)
|
2022-08-13 08:49:38 -04:00
|
|
|
}
|
|
|
|
|
}
|