use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use crate::{
keys::dkg::{compute_proof_of_knowledge, round1, round2},
keys::{
evaluate_polynomial, generate_coefficients, generate_secret_polynomial,
generate_secret_shares, validate_num_of_signers, CoefficientCommitment, PublicKeyPackage,
SigningKey, SigningShare, VerifyingShare,
},
Ciphersuite, CryptoRng, Error, Field, Group, Header, Identifier, RngCore,
};
use core::iter;
use super::{dkg::round1::Package, KeyPackage, SecretShare, VerifiableSecretSharingCommitment};
pub fn compute_refreshing_shares<C: Ciphersuite, R: RngCore + CryptoRng>(
pub_key_package: PublicKeyPackage<C>,
identifiers: &[Identifier<C>],
rng: &mut R,
) -> Result<(Vec<SecretShare<C>>, PublicKeyPackage<C>), Error<C>> {
let min_signers = pub_key_package
.min_signers
.ok_or(Error::InvalidMinSigners)?;
let signers = identifiers.len() as u16;
validate_num_of_signers(min_signers, signers)?;
if identifiers
.iter()
.any(|i| !pub_key_package.verifying_shares().contains_key(i))
{
return Err(Error::UnknownIdentifier);
}
let refreshing_key = SigningKey {
scalar: <<C::Group as Group>::Field>::zero(),
};
let coefficients = generate_coefficients::<C, R>(min_signers as usize - 1, rng);
let refreshing_shares = generate_secret_shares(
&refreshing_key,
signers,
min_signers,
coefficients,
identifiers,
)?;
let mut refreshed_verifying_shares: BTreeMap<Identifier<C>, VerifyingShare<C>> =
BTreeMap::new();
let mut refreshing_shares_minus_identity: Vec<SecretShare<C>> = Vec::new();
for mut share in refreshing_shares {
let refreshing_verifying_share: VerifyingShare<C> = SigningShare::into(share.signing_share);
let verifying_share = pub_key_package.verifying_shares.get(&share.identifier);
match verifying_share {
Some(verifying_share) => {
let refreshed_verifying_share =
refreshing_verifying_share.to_element() + verifying_share.to_element();
refreshed_verifying_shares.insert(
share.identifier,
VerifyingShare::new(refreshed_verifying_share),
);
}
None => return Err(Error::UnknownIdentifier),
};
share.commitment.0.remove(0);
refreshing_shares_minus_identity.push(share);
}
let refreshed_pub_key_package = PublicKeyPackage::<C> {
header: pub_key_package.header,
verifying_shares: refreshed_verifying_shares,
verifying_key: pub_key_package.verifying_key,
min_signers: Some(pub_key_package.min_signers.unwrap_or(min_signers)),
};
Ok((refreshing_shares_minus_identity, refreshed_pub_key_package))
}
pub fn refresh_share<C: Ciphersuite>(
mut refreshing_share: SecretShare<C>,
current_key_package: &KeyPackage<C>,
) -> Result<KeyPackage<C>, Error<C>> {
let identity_commitment: Vec<CoefficientCommitment<C>> =
vec![CoefficientCommitment::new(C::Group::identity())];
let refreshing_share_commitments: Vec<CoefficientCommitment<C>> = identity_commitment
.into_iter()
.chain(refreshing_share.commitment.0.clone())
.collect();
refreshing_share.commitment =
VerifiableSecretSharingCommitment::<C>::new(refreshing_share_commitments);
let refreshed_share_package = KeyPackage::<C>::try_from(refreshing_share)?;
if refreshed_share_package.min_signers() != current_key_package.min_signers() {
return Err(Error::InvalidMinSigners);
}
let signing_share: SigningShare<C> = SigningShare::new(
refreshed_share_package.signing_share.to_scalar()
+ current_key_package.signing_share.to_scalar(),
);
let mut new_key_package = current_key_package.clone();
new_key_package.signing_share = signing_share;
Ok(new_key_package)
}
pub fn refresh_dkg_part1<C: Ciphersuite, R: RngCore + CryptoRng>(
identifier: Identifier<C>,
max_signers: u16,
min_signers: u16,
mut rng: R,
) -> Result<(round1::SecretPackage<C>, round1::Package<C>), Error<C>> {
validate_num_of_signers::<C>(min_signers, max_signers)?;
let refreshing_key = SigningKey {
scalar: <<C::Group as Group>::Field>::zero(),
};
let coefficients = generate_coefficients::<C, R>(min_signers as usize - 1, &mut rng);
let (coefficients, commitment) =
generate_secret_polynomial(&refreshing_key, max_signers, min_signers, coefficients)?;
let mut coeff_comms = commitment.0;
coeff_comms.remove(0);
let commitment = VerifiableSecretSharingCommitment::new(coeff_comms.clone());
let proof_of_knowledge =
compute_proof_of_knowledge(identifier, &coefficients, &commitment, &mut rng)?;
let secret_package = round1::SecretPackage::new(
identifier,
coefficients.clone(),
commitment.clone(),
min_signers,
max_signers,
);
let package = round1::Package {
header: Header::default(),
commitment,
proof_of_knowledge,
};
Ok((secret_package, package))
}
pub fn refresh_dkg_part2<C: Ciphersuite>(
mut secret_package: round1::SecretPackage<C>,
round1_packages: &BTreeMap<Identifier<C>, round1::Package<C>>,
) -> Result<
(
round2::SecretPackage<C>,
BTreeMap<Identifier<C>, round2::Package<C>>,
),
Error<C>,
> {
if round1_packages.len() != (secret_package.max_signers - 1) as usize {
return Err(Error::IncorrectNumberOfPackages);
}
let identity_commitment: Vec<CoefficientCommitment<C>> =
vec![CoefficientCommitment::new(C::Group::identity())];
let refreshing_secret_share_commitments: Vec<CoefficientCommitment<C>> = identity_commitment
.into_iter()
.chain(secret_package.commitment.0.clone())
.collect();
secret_package.commitment =
VerifiableSecretSharingCommitment::<C>::new(refreshing_secret_share_commitments);
let mut round2_packages = BTreeMap::new();
for (sender_identifier, round1_package) in round1_packages {
let identity_commitment: Vec<CoefficientCommitment<C>> =
vec![CoefficientCommitment::new(C::Group::identity())];
let refreshing_share_commitments: Vec<CoefficientCommitment<C>> = identity_commitment
.into_iter()
.chain(round1_package.commitment.0.clone())
.collect();
if refreshing_share_commitments.clone().len() != secret_package.min_signers as usize {
return Err(Error::IncorrectNumberOfCommitments);
}
let ell = *sender_identifier;
let signing_share = SigningShare::from_coefficients(&secret_package.coefficients(), ell);
round2_packages.insert(
ell,
round2::Package {
header: Header::default(),
signing_share,
},
);
}
let fii = evaluate_polynomial(secret_package.identifier, &secret_package.coefficients());
secret_package.commitment.0.remove(0);
Ok((
round2::SecretPackage::new(
secret_package.identifier,
secret_package.commitment.clone(),
fii,
secret_package.min_signers,
secret_package.max_signers,
),
round2_packages,
))
}
pub fn refresh_dkg_shares<C: Ciphersuite>(
round2_secret_package: &round2::SecretPackage<C>,
round1_packages: &BTreeMap<Identifier<C>, round1::Package<C>>,
round2_packages: &BTreeMap<Identifier<C>, round2::Package<C>>,
old_pub_key_package: PublicKeyPackage<C>,
old_key_package: KeyPackage<C>,
) -> Result<(KeyPackage<C>, PublicKeyPackage<C>), Error<C>> {
if round2_secret_package.min_signers() != old_key_package.min_signers() {
return Err(Error::InvalidMinSigners);
}
let mut commitment = round2_secret_package.commitment.0.clone();
commitment.insert(0, CoefficientCommitment::new(C::Group::identity()));
let round2_secret_package = round2::SecretPackage::new(
round2_secret_package.identifier,
VerifiableSecretSharingCommitment::<C>::new(commitment),
round2_secret_package.secret_share.0,
round2_secret_package.min_signers,
round2_secret_package.max_signers,
);
let mut new_round_1_packages = BTreeMap::new();
for (sender_identifier, round1_package) in round1_packages {
let identity_commitment: Vec<CoefficientCommitment<C>> =
vec![CoefficientCommitment::new(C::Group::identity())];
let refreshing_share_commitments: Vec<CoefficientCommitment<C>> = identity_commitment
.into_iter()
.chain(round1_package.commitment.0.clone())
.collect();
let new_commitments =
VerifiableSecretSharingCommitment::<C>::new(refreshing_share_commitments);
let new_round_1_package = Package {
header: round1_package.header,
commitment: new_commitments,
proof_of_knowledge: round1_package.proof_of_knowledge,
};
new_round_1_packages.insert(*sender_identifier, new_round_1_package);
}
if new_round_1_packages.len() != (round2_secret_package.max_signers - 1) as usize {
return Err(Error::IncorrectNumberOfPackages);
}
if new_round_1_packages.len() != round2_packages.len() {
return Err(Error::IncorrectNumberOfPackages);
}
if new_round_1_packages
.keys()
.any(|id| !round2_packages.contains_key(id))
{
return Err(Error::IncorrectPackage);
}
let mut signing_share = <<C::Group as Group>::Field>::zero();
for (sender_identifier, round2_package) in round2_packages {
let ell = *sender_identifier;
let f_ell_i = round2_package.signing_share;
let commitment = &new_round_1_packages
.get(&ell)
.ok_or(Error::PackageNotFound)?
.commitment;
let secret_share = SecretShare {
header: Header::default(),
identifier: round2_secret_package.identifier,
signing_share: f_ell_i,
commitment: commitment.clone(),
};
let _ = secret_share.verify()?;
signing_share = signing_share + f_ell_i.to_scalar();
}
signing_share = signing_share + round2_secret_package.secret_share();
let old_signing_share = old_key_package.signing_share.to_scalar();
signing_share = signing_share + old_signing_share;
let signing_share = SigningShare::new(signing_share);
let verifying_share = signing_share.into();
let commitments: BTreeMap<_, _> = new_round_1_packages
.iter()
.map(|(id, package)| (*id, &package.commitment))
.chain(iter::once((
round2_secret_package.identifier,
&round2_secret_package.commitment,
)))
.collect();
let zero_shares_public_key_package = PublicKeyPackage::from_dkg_commitments(&commitments)?;
let mut new_verifying_shares = BTreeMap::new();
for (identifier, verifying_share) in zero_shares_public_key_package.verifying_shares {
let new_verifying_share = verifying_share.to_element()
+ old_pub_key_package
.verifying_shares
.get(&identifier)
.ok_or(Error::UnknownIdentifier)?
.to_element();
new_verifying_shares.insert(identifier, VerifyingShare::new(new_verifying_share));
}
let public_key_package = PublicKeyPackage {
header: old_pub_key_package.header,
verifying_shares: new_verifying_shares,
verifying_key: old_pub_key_package.verifying_key,
min_signers: Some(round2_secret_package.min_signers),
};
let key_package = KeyPackage {
header: Header::default(),
identifier: round2_secret_package.identifier,
signing_share,
verifying_share,
verifying_key: public_key_package.verifying_key,
min_signers: round2_secret_package.min_signers,
};
Ok((key_package, public_key_package))
}