use core::fmt::{self, Debug};
use crate as frost;
use crate::{
Challenge, Ciphersuite, Error, Field, Group, {round1, *},
};
#[derive(Clone, Copy, Eq, PartialEq, 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 SignatureShare<C: Ciphersuite> {
#[getter(skip)]
pub(crate) header: Header<C>,
pub(crate) share: SerializableScalar<C>,
}
impl<C> SignatureShare<C>
where
C: Ciphersuite,
{
pub(crate) fn new(
scalar: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
) -> Self {
Self {
header: Header::default(),
share: SerializableScalar(scalar),
}
}
pub(crate) fn to_scalar(
self,
) -> <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar {
self.share.0
}
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
Ok(Self {
header: Header::default(),
share: SerializableScalar::deserialize(bytes)?,
})
}
pub fn serialize(&self) -> Vec<u8> {
self.share.serialize()
}
#[cfg_attr(feature = "internals", visibility::make(pub))]
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
pub(crate) fn verify(
&self,
identifier: Identifier<C>,
group_commitment_share: &round1::GroupCommitmentShare<C>,
verifying_share: &frost::keys::VerifyingShare<C>,
lambda_i: Scalar<C>,
challenge: &Challenge<C>,
) -> Result<(), Error<C>> {
if (<C::Group>::generator() * self.to_scalar())
!= (group_commitment_share.to_element()
+ (verifying_share.to_element() * challenge.0 * lambda_i))
{
return Err(Error::InvalidSignatureShare {
culprits: vec![identifier],
});
}
Ok(())
}
}
impl<C> Debug for SignatureShare<C>
where
C: Ciphersuite,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("SignatureShare")
.field("share", &hex::encode(self.serialize()))
.finish()
}
}
#[cfg_attr(feature = "internals", visibility::make(pub))]
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
pub(super) fn compute_signature_share<C: Ciphersuite>(
signer_nonces: &round1::SigningNonces<C>,
binding_factor: BindingFactor<C>,
lambda_i: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar,
key_package: &keys::KeyPackage<C>,
challenge: Challenge<C>,
) -> SignatureShare<C> {
let z_share: <<C::Group as Group>::Field as Field>::Scalar = signer_nonces.hiding.to_scalar()
+ (signer_nonces.binding.to_scalar() * binding_factor.0)
+ (lambda_i * key_package.signing_share.to_scalar() * challenge.to_scalar());
SignatureShare::<C>::new(z_share)
}
pub fn sign<C: Ciphersuite>(
signing_package: &SigningPackage<C>,
signer_nonces: &round1::SigningNonces<C>,
key_package: &frost::keys::KeyPackage<C>,
) -> Result<SignatureShare<C>, Error<C>> {
if signing_package.signing_commitments().len() < key_package.min_signers as usize {
return Err(Error::IncorrectNumberOfCommitments);
}
let commitment = signing_package
.signing_commitments
.get(&key_package.identifier)
.ok_or(Error::MissingCommitment)?;
if &signer_nonces.commitments != commitment {
return Err(Error::IncorrectCommitment);
}
let (signing_package, signer_nonces, key_package) =
<C>::pre_sign(signing_package, signer_nonces, key_package)?;
let binding_factor_list: BindingFactorList<C> =
compute_binding_factor_list(&signing_package, &key_package.verifying_key, &[])?;
let binding_factor: frost::BindingFactor<C> = binding_factor_list
.get(&key_package.identifier)
.ok_or(Error::UnknownIdentifier)?
.clone();
let (signing_package, signer_nonces) =
<C>::pre_commitment_sign(&signing_package, &signer_nonces, &binding_factor_list)?;
let group_commitment = compute_group_commitment(&signing_package, &binding_factor_list)?;
let lambda_i = frost::derive_interpolating_value(key_package.identifier(), &signing_package)?;
let challenge = <C>::challenge(
&group_commitment.0,
&key_package.verifying_key,
signing_package.message(),
)?;
let signature_share = <C>::compute_signature_share(
&group_commitment,
&signer_nonces,
binding_factor,
lambda_i,
&key_package,
challenge,
);
Ok(signature_share)
}