use core::{marker::PhantomData, ops::Deref}; use std::{ io::{self, Read, Write}, sync::Arc, collections::HashMap, }; use rand_core::{RngCore, CryptoRng}; use ciphersuite::{group::GroupEncoding, Ciphersuite}; use transcript::{Transcript, RecommendedTranscript}; use dleq::DLEqProof; use crate::{Participant, DkgError, ThresholdCore, ThresholdKeys, validate_map}; /// Promote a set of keys to another Ciphersuite definition. pub trait CiphersuitePromote { fn promote(self) -> ThresholdKeys; } fn transcript(key: G, i: Participant) -> RecommendedTranscript { let mut transcript = RecommendedTranscript::new(b"DKG Generator Promotion v0.2"); transcript.append_message(b"group_key", key.to_bytes()); transcript.append_message(b"participant", i.to_bytes()); transcript } /// Proof of valid promotion to another generator. #[derive(Clone, Copy)] pub struct GeneratorProof { share: C::G, proof: DLEqProof, } impl GeneratorProof { pub fn write(&self, writer: &mut W) -> io::Result<()> { writer.write_all(self.share.to_bytes().as_ref())?; self.proof.write(writer) } pub fn read(reader: &mut R) -> io::Result> { Ok(GeneratorProof { share: ::read_G(reader)?, proof: DLEqProof::read(reader)?, }) } pub fn serialize(&self) -> Vec { let mut buf = vec![]; self.write(&mut buf).unwrap(); buf } } /// Promote a set of keys from one generator to another, where the elliptic curve is the same. /// /// Since the Ciphersuite trait additionally specifies a generator, this provides an O(n) way to /// update the generator used with keys. This outperforms the key generation protocol which is /// exponential. pub struct GeneratorPromotion { base: ThresholdKeys, proof: GeneratorProof, _c2: PhantomData, } impl GeneratorPromotion where C2: Ciphersuite, { /// Begin promoting keys from one generator to another. Returns a proof this share was properly /// promoted. pub fn promote( rng: &mut R, base: ThresholdKeys, ) -> (GeneratorPromotion, GeneratorProof) { // Do a DLEqProof for the new generator let proof = GeneratorProof { share: C2::generator() * base.secret_share().deref(), proof: DLEqProof::prove( rng, &mut transcript(base.core.group_key(), base.params().i), &[C1::generator(), C2::generator()], base.secret_share(), ), }; (GeneratorPromotion { base, proof, _c2: PhantomData:: }, proof) } /// Complete promotion by taking in the proofs from all other participants. pub fn complete( self, proofs: &HashMap>, ) -> Result, DkgError<()>> { let params = self.base.params(); validate_map(proofs, &(1 ..= params.n).map(Participant).collect::>(), params.i)?; let original_shares = self.base.verification_shares(); let mut verification_shares = HashMap::new(); verification_shares.insert(params.i, self.proof.share); for (i, proof) in proofs { let i = *i; proof .proof .verify( &mut transcript(self.base.core.group_key(), i), &[C1::generator(), C2::generator()], &[original_shares[&i], proof.share], ) .map_err(|_| DkgError::InvalidProofOfKnowledge(i))?; verification_shares.insert(i, proof.share); } Ok(ThresholdKeys { core: Arc::new(ThresholdCore::new( params, self.base.secret_share().clone(), verification_shares, )), offset: None, }) } }