use core::iter;
use alloc::collections::BTreeMap;
use rand_core::{CryptoRng, RngCore};
use crate::{
Challenge, Ciphersuite, Element, Error, Field, Group, Header, Identifier, Scalar, Signature,
SigningKey, VerifyingKey,
};
#[cfg(feature = "serialization")]
use crate::serialization::{Deserialize, Serialize};
use super::{
evaluate_polynomial, generate_coefficients, generate_secret_polynomial,
validate_num_of_signers, KeyPackage, PublicKeyPackage, SecretShare, SigningShare,
VerifiableSecretSharingCommitment,
};
pub mod round1 {
use alloc::vec::Vec;
use derive_getters::Getters;
use zeroize::{Zeroize, ZeroizeOnDrop};
use super::*;
use crate::serialization::SerializableScalar;
#[cfg(feature = "serialization")]
use crate::serialization::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Getters)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct Package<C: Ciphersuite> {
#[getter(skip)]
pub(crate) header: Header<C>,
pub(crate) commitment: VerifiableSecretSharingCommitment<C>,
pub(crate) proof_of_knowledge: Signature<C>,
}
impl<C> Package<C>
where
C: Ciphersuite,
{
pub fn new(
commitment: VerifiableSecretSharingCommitment<C>,
proof_of_knowledge: Signature<C>,
) -> Self {
Self {
header: Header::default(),
commitment,
proof_of_knowledge,
}
}
}
#[cfg(feature = "serialization")]
impl<C> Package<C>
where
C: Ciphersuite,
{
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}
#[derive(Clone, PartialEq, Eq, Getters, Zeroize, ZeroizeOnDrop)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct SecretPackage<C: Ciphersuite> {
#[zeroize(skip)]
pub(crate) identifier: Identifier<C>,
#[getter(skip)]
pub(crate) coefficients: Vec<SerializableScalar<C>>,
#[zeroize(skip)]
pub(crate) commitment: VerifiableSecretSharingCommitment<C>,
pub(crate) min_signers: u16,
pub(crate) max_signers: u16,
}
impl<C> SecretPackage<C>
where
C: Ciphersuite,
{
pub fn new(
identifier: Identifier<C>,
coefficients: Vec<Scalar<C>>,
commitment: VerifiableSecretSharingCommitment<C>,
min_signers: u16,
max_signers: u16,
) -> Self {
Self {
identifier,
coefficients: coefficients.into_iter().map(SerializableScalar).collect(),
commitment,
min_signers,
max_signers,
}
}
#[cfg_attr(feature = "internals", visibility::make(pub))]
pub(crate) fn coefficients(&self) -> Vec<Scalar<C>> {
self.coefficients.iter().map(|s| s.0).collect()
}
}
#[cfg(feature = "serialization")]
impl<C> SecretPackage<C>
where
C: Ciphersuite,
{
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}
impl<C> core::fmt::Debug for SecretPackage<C>
where
C: Ciphersuite,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SecretPackage")
.field("identifier", &self.identifier)
.field("coefficients", &"<redacted>")
.field("commitment", &self.commitment)
.field("min_signers", &self.min_signers)
.field("max_signers", &self.max_signers)
.finish()
}
}
}
pub mod round2 {
use derive_getters::Getters;
use zeroize::{Zeroize, ZeroizeOnDrop};
#[cfg(feature = "serialization")]
use alloc::vec::Vec;
use crate::serialization::SerializableScalar;
use super::*;
#[derive(Clone, Debug, PartialEq, Eq, Getters, Zeroize, ZeroizeOnDrop)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct Package<C: Ciphersuite> {
#[getter(skip)]
pub(crate) header: Header<C>,
pub(crate) signing_share: SigningShare<C>,
}
impl<C> Package<C>
where
C: Ciphersuite,
{
pub fn new(signing_share: SigningShare<C>) -> Self {
Self {
header: Header::default(),
signing_share,
}
}
}
#[cfg(feature = "serialization")]
impl<C> Package<C>
where
C: Ciphersuite,
{
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}
#[derive(Clone, PartialEq, Eq, Getters, Zeroize, ZeroizeOnDrop)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = "C: Ciphersuite"))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct SecretPackage<C: Ciphersuite> {
#[zeroize(skip)]
pub(crate) identifier: Identifier<C>,
#[zeroize(skip)]
pub(crate) commitment: VerifiableSecretSharingCommitment<C>,
#[getter(skip)]
pub(crate) secret_share: SerializableScalar<C>,
pub(crate) min_signers: u16,
pub(crate) max_signers: u16,
}
impl<C> SecretPackage<C>
where
C: Ciphersuite,
{
pub fn new(
identifier: Identifier<C>,
commitment: VerifiableSecretSharingCommitment<C>,
secret_share: Scalar<C>,
min_signers: u16,
max_signers: u16,
) -> Self {
Self {
identifier,
commitment,
secret_share: SerializableScalar(secret_share),
min_signers,
max_signers,
}
}
pub fn secret_share(&self) -> Scalar<C> {
self.secret_share.0
}
}
#[cfg(feature = "serialization")]
impl<C> SecretPackage<C>
where
C: Ciphersuite,
{
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
Serialize::serialize(&self)
}
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Deserialize::deserialize(bytes)
}
}
impl<C> core::fmt::Debug for SecretPackage<C>
where
C: Ciphersuite,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SecretPackage")
.field("identifier", &self.identifier)
.field("commitment", &self.commitment)
.field("secret_share", &"<redacted>")
.field("min_signers", &self.min_signers)
.field("max_signers", &self.max_signers)
.finish()
}
}
}
pub fn 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 secret: SigningKey<C> = SigningKey::new(&mut rng);
let coefficients = generate_coefficients::<C, R>(min_signers as usize - 1, &mut rng);
let (coefficients, commitment) =
generate_secret_polynomial(&secret, max_signers, min_signers, coefficients)?;
let proof_of_knowledge =
compute_proof_of_knowledge(identifier, &coefficients, &commitment, &mut rng)?;
let secret_package = round1::SecretPackage::new(
identifier,
coefficients,
commitment.clone(),
min_signers,
max_signers,
);
let package = round1::Package {
header: Header::default(),
commitment,
proof_of_knowledge,
};
Ok((secret_package, package))
}
fn challenge<C>(
identifier: Identifier<C>,
verifying_key: &VerifyingKey<C>,
R: &Element<C>,
) -> Result<Challenge<C>, Error<C>>
where
C: Ciphersuite,
{
let mut preimage = vec![];
preimage.extend_from_slice(identifier.serialize().as_ref());
preimage.extend_from_slice(<C::Group>::serialize(&verifying_key.to_element())?.as_ref());
preimage.extend_from_slice(<C::Group>::serialize(R)?.as_ref());
Ok(Challenge(
C::HDKG(&preimage[..]).ok_or(Error::DKGNotSupported)?,
))
}
#[cfg_attr(feature = "internals", visibility::make(pub))]
pub(crate) fn compute_proof_of_knowledge<C: Ciphersuite, R: RngCore + CryptoRng>(
identifier: Identifier<C>,
coefficients: &[Scalar<C>],
commitment: &VerifiableSecretSharingCommitment<C>,
mut rng: R,
) -> Result<Signature<C>, Error<C>> {
let (k, R_i) = <C>::generate_nonce(&mut rng);
let c_i = challenge::<C>(identifier, &commitment.verifying_key()?, &R_i)?;
let a_i0 = *coefficients.first().ok_or(Error::InvalidCoefficients)?;
let mu_i = k + a_i0 * c_i.0;
Ok(Signature { R: R_i, z: mu_i })
}
#[cfg_attr(feature = "internals", visibility::make(pub))]
pub(crate) fn verify_proof_of_knowledge<C: Ciphersuite>(
identifier: Identifier<C>,
commitment: &VerifiableSecretSharingCommitment<C>,
proof_of_knowledge: &Signature<C>,
) -> Result<(), Error<C>> {
let ell = identifier;
let R_ell = proof_of_knowledge.R;
let mu_ell = proof_of_knowledge.z;
let phi_ell0 = commitment.verifying_key()?;
let c_ell = challenge::<C>(ell, &phi_ell0, &R_ell)?;
if R_ell != <C::Group>::generator() * mu_ell - phi_ell0.to_element() * c_ell.0 {
return Err(Error::InvalidProofOfKnowledge { culprit: ell });
}
Ok(())
}
pub fn part2<C: Ciphersuite>(
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);
}
if round1_packages.contains_key(&secret_package.identifier) {
return Err(Error::UnknownIdentifier);
}
for package in round1_packages.values() {
if package.commitment.min_signers() != secret_package.min_signers {
return Err(Error::IncorrectNumberOfCommitments);
}
}
let mut round2_packages = BTreeMap::new();
for (sender_identifier, round1_package) in round1_packages {
let ell = *sender_identifier;
verify_proof_of_knowledge(
ell,
&round1_package.commitment,
&round1_package.proof_of_knowledge,
)?;
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());
Ok((
round2::SecretPackage::new(
secret_package.identifier,
secret_package.commitment.clone(),
fii,
secret_package.min_signers,
secret_package.max_signers,
),
round2_packages,
))
}
pub fn part3<C: Ciphersuite>(
round2_secret_package: &round2::SecretPackage<C>,
round1_packages: &BTreeMap<Identifier<C>, round1::Package<C>>,
round2_packages: &BTreeMap<Identifier<C>, round2::Package<C>>,
) -> Result<(KeyPackage<C>, PublicKeyPackage<C>), Error<C>> {
if round1_packages.len() != (round2_secret_package.max_signers - 1) as usize {
return Err(Error::IncorrectNumberOfPackages);
}
if round1_packages.contains_key(&round2_secret_package.identifier) {
return Err(Error::UnknownIdentifier);
}
if round2_packages.contains_key(&round2_secret_package.identifier) {
return Err(Error::UnknownIdentifier);
}
if round1_packages.len() != round2_packages.len() {
return Err(Error::IncorrectNumberOfPackages);
}
if round1_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 = &round1_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().map_err(|e| {
if let Error::InvalidSecretShare { .. } = e {
Error::InvalidSecretShare {
culprit: Some(*sender_identifier),
}
} else {
e
}
})?;
signing_share = signing_share + f_ell_i.to_scalar();
}
signing_share = signing_share + round2_secret_package.secret_share();
let signing_share = SigningShare::new(signing_share);
let verifying_share = signing_share.into();
let commitments: BTreeMap<_, _> = round1_packages
.iter()
.map(|(id, package)| (*id, &package.commitment))
.chain(iter::once((
round2_secret_package.identifier,
&round2_secret_package.commitment,
)))
.collect();
let public_key_package = PublicKeyPackage::from_dkg_commitments(&commitments)?;
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,
};
C::post_dkg(key_package, public_key_package)
}