use Error everywhere and add enums as needed (#172)

* use Error everywhere and add enums as needed

* Apply suggestions from code review

Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>

* Update frost-core/src/error.rs

Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>

Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>
This commit is contained in:
Conrado Gouvea 2022-10-28 17:01:03 -03:00 committed by GitHub
parent ac5f44ade8
commit 6df6e32221
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 95 additions and 56 deletions

View File

@ -6,6 +6,15 @@ use thiserror::Error;
#[non_exhaustive]
#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)]
pub enum Error {
/// min_signers is invalid
#[error("min_signers must be at least 2 and not larger than max_signers")]
InvalidMinSigners,
/// max_signers is invalid
#[error("max_signers must be at least 2")]
InvalidMaxSigners,
/// max_signers is invalid
#[error("coefficients must have min_signers-1 elements")]
InvalidCoefficients,
/// This identifier is unserializable.
#[error("Malformed identifier is unserializable.")]
MalformedIdentifier,
@ -36,4 +45,31 @@ pub enum Error {
/// This element MUST have (large) prime order.
#[error("Invalid for this element to not have large prime order.")]
InvalidNonPrimeOrderElement,
/// Duplicated shares provided
#[error("Duplicated shares provided.")]
DuplicatedShares,
/// Commitment equals the identity
#[error("Commitment equals the identity.")]
IdentityCommitment,
/// Signature share verification failed.
#[error("Invalid signature share.")]
InvalidSignatureShare,
/// Secret share verification failed.
#[error("Invalid secret share.")]
InvalidSecretShare,
/// Round 1 package not found for Round 2 participant.
#[error("Round 1 package not found for Round 2 participant.")]
PackageNotFound,
/// Incorrect number of packages.
#[error("Incorrect number of packages.")]
IncorrectNumberOfPackages,
/// The incorrect package was specified.
#[error("The incorrect package was specified.")]
IncorrectPackage,
/// The ciphersuite does not support DKG.
#[error("The ciphersuite does not support DKG.")]
DKGNotSupported,
/// The proof of knowledge is not valid.
#[error("The proof of knowledge is not valid.")]
InvalidProofOfKnowledge,
}

View File

@ -117,6 +117,7 @@ where
}
}
#[cfg(any(test, feature = "test-impl"))]
impl<C> FromHex for BindingFactor<C>
where
C: Ciphersuite,
@ -138,7 +139,7 @@ where
fn derive_lagrange_coeff<C: Ciphersuite>(
signer_id: &Identifier<C>,
signing_package: &SigningPackage<C>,
) -> Result<Scalar<C>, &'static str> {
) -> Result<Scalar<C>, Error> {
let zero = <<C::Group as Group>::Field>::zero();
let mut num = <<C::Group as Group>::Field>::one();
@ -158,7 +159,7 @@ fn derive_lagrange_coeff<C: Ciphersuite>(
}
if den == zero {
return Err("Duplicate shares provided");
return Err(Error::DuplicatedShares);
}
// TODO(dconnolly): return this error if the inversion result == zero
@ -258,7 +259,7 @@ impl<C> TryFrom<&SigningPackage<C>> for GroupCommitment<C>
where
C: Ciphersuite,
{
type Error = &'static str;
type Error = Error;
/// Generates the group commitment which is published as part of the joint
/// Schnorr signature.
@ -266,7 +267,7 @@ where
/// Implements [`compute_group_commitment`] from the spec.
///
/// [`compute_group_commitment`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-4.5
fn try_from(signing_package: &SigningPackage<C>) -> Result<GroupCommitment<C>, &'static str> {
fn try_from(signing_package: &SigningPackage<C>) -> Result<GroupCommitment<C>, Error> {
let binding_factor_list: BindingFactorList<C> = signing_package.into();
let identity = <C::Group>::identity();
@ -280,7 +281,7 @@ where
// The following check prevents a party from accidentally revealing their share.
// Note that the '&&' operator would be sufficient.
if identity == commitment.binding.0 || identity == commitment.hiding.0 {
return Err("Commitment equals the identity.");
return Err(Error::IdentityCommitment);
}
let binding_factor = binding_factor_list[commitment.identifier].clone();
@ -316,7 +317,7 @@ pub fn aggregate<C>(
signing_package: &SigningPackage<C>,
signature_shares: &[round2::SignatureShare<C>],
pubkeys: &keys::PublicKeyPackage<C>,
) -> Result<Signature<C>, &'static str>
) -> Result<Signature<C>, Error>
where
C: Ciphersuite,
{

View File

@ -94,6 +94,7 @@ where
}
}
#[cfg(any(test, feature = "test-impl"))]
impl<C> FromHex for SharedSecret<C>
where
C: Ciphersuite,
@ -153,6 +154,7 @@ where
// Implements [`Zeroize`] by overwriting a value with the [`Default::default()`] value
impl<C> DefaultIsZeroes for SigningShare<C> where C: Ciphersuite {}
#[cfg(any(test, feature = "test-impl"))]
impl<C> FromHex for SigningShare<C>
where
C: Ciphersuite,
@ -277,12 +279,12 @@ where
/// but only for this participant.
///
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#appendix-C.2-4
pub fn verify(&self) -> Result<(VerifyingShare<C>, VerifyingKey<C>), &'static str> {
pub fn verify(&self) -> Result<(VerifyingShare<C>, VerifyingKey<C>), Error> {
let f_result = <C::Group>::generator() * self.value.0;
let result = evaluate_vss(&self.commitment, self.identifier)?;
let result = evaluate_vss(&self.commitment, self.identifier);
if !(f_result == result) {
return Err("SecretShare is invalid.");
return Err(Error::InvalidSecretShare);
}
let group_public = VerifyingKey {
@ -309,7 +311,7 @@ pub fn keygen_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
mut rng: R,
) -> Result<(Vec<SecretShare<C>>, PublicKeyPackage<C>), &'static str> {
) -> Result<(Vec<SecretShare<C>>, PublicKeyPackage<C>), Error> {
let mut bytes = [0; 64];
rng.fill_bytes(&mut bytes);
@ -345,7 +347,7 @@ pub fn keygen_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>(
fn evaluate_polynomial<C: Ciphersuite>(
identifier: Identifier<C>,
coefficients: &[Scalar<C>],
) -> Result<Scalar<C>, &'static str> {
) -> Scalar<C> {
let mut value = <<C::Group as Group>::Field>::zero();
let ell_scalar = identifier;
@ -354,7 +356,7 @@ fn evaluate_polynomial<C: Ciphersuite>(
value *= ell_scalar;
}
value = value + coefficients[0];
Ok(value)
value
}
/// Evaluates the right-hand side of the VSS verification equation, namely
@ -363,14 +365,14 @@ fn evaluate_polynomial<C: Ciphersuite>(
fn evaluate_vss<C: Ciphersuite>(
commitment: &VerifiableSecretSharingCommitment<C>,
identifier: Identifier<C>,
) -> Result<Element<C>, &'static str> {
) -> Element<C> {
let i = identifier;
let (_, result) = commitment.0.iter().fold(
(<<C::Group as Group>::Field>::one(), <C::Group>::identity()),
|(i_to_the_k, sum_so_far), comm_k| (i * i_to_the_k, sum_so_far + comm_k.0 * i_to_the_k),
);
Ok(result)
result
}
/// A FROST keypair, which can be generated either by a trusted dealer or using
@ -420,7 +422,7 @@ impl<C> TryFrom<SecretShare<C>> for KeyPackage<C>
where
C: Ciphersuite,
{
type Error = &'static str;
type Error = Error;
/// Tries to verify a share and construct a [`KeyPackage`] from it.
///
@ -430,7 +432,7 @@ where
/// every participant has the same view of the commitment issued by the
/// dealer, but implementations *MUST* make sure that all participants have
/// a consistent view of this commitment in practice.
fn try_from(secret_share: SecretShare<C>) -> Result<Self, &'static str> {
fn try_from(secret_share: SecretShare<C>) -> Result<Self, Error> {
let (public, group_public) = secret_share.verify()?;
Ok(KeyPackage {
@ -469,21 +471,21 @@ pub(crate) fn generate_secret_polynomial<C: Ciphersuite>(
max_signers: u16,
min_signers: u16,
mut coefficients: Vec<Scalar<C>>,
) -> Result<(Vec<Scalar<C>>, VerifiableSecretSharingCommitment<C>), &'static str> {
) -> Result<(Vec<Scalar<C>>, VerifiableSecretSharingCommitment<C>), Error> {
if min_signers < 2 {
return Err("min_signers cannot be less than 2");
return Err(Error::InvalidMinSigners);
}
if max_signers < 2 {
return Err("Number of signers cannot be less than the minimum threshold 2");
return Err(Error::InvalidMaxSigners);
}
if min_signers > max_signers {
return Err("min_signers cannot exceed max_signers");
return Err(Error::InvalidMinSigners);
}
if coefficients.len() != min_signers as usize - 1 {
return Err("Must pass min_signers-1 coefficients");
return Err(Error::InvalidCoefficients);
}
// Prepend the secret, which is the 0th coefficient
@ -524,14 +526,14 @@ pub(crate) fn generate_secret_shares<C: Ciphersuite>(
max_signers: u16,
min_signers: u16,
coefficients: Vec<Scalar<C>>,
) -> Result<Vec<SecretShare<C>>, &'static str> {
) -> Result<Vec<SecretShare<C>>, Error> {
let mut secret_shares: Vec<SecretShare<C>> = Vec::with_capacity(max_signers as usize);
let (coefficients, commitment) =
generate_secret_polynomial(secret, max_signers, min_signers, coefficients)?;
for id in (1..=max_signers as u16).map_while(|i| Identifier::<C>::try_from(i).ok()) {
let value = evaluate_polynomial(id, &coefficients)?;
let value = evaluate_polynomial(id, &coefficients);
secret_shares.push(SecretShare {
identifier: id,

View File

@ -5,7 +5,7 @@ use std::{collections::HashMap, iter};
use rand_core::{CryptoRng, RngCore};
use crate::{
frost::Identifier, Challenge, Ciphersuite, Element, Field, Group, Scalar, Signature,
frost::Identifier, Challenge, Ciphersuite, Element, Error, Field, Group, Scalar, Signature,
VerifyingKey,
};
@ -91,7 +91,7 @@ pub fn keygen_part1<C: Ciphersuite, R: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
mut rng: R,
) -> Result<(Round1SecretPackage<C>, Round1Package<C>), &'static str> {
) -> Result<(Round1SecretPackage<C>, Round1Package<C>), Error> {
let secret: SharedSecret<C> = SharedSecret::random(&mut rng);
// Round 1, Step 1
@ -115,8 +115,7 @@ pub fn keygen_part1<C: Ciphersuite, R: RngCore + CryptoRng>(
let k = <<C::Group as Group>::Field>::random(&mut rng);
let R_i = <C::Group>::generator() * k;
let c_i = challenge::<C>(identifier, &R_i, &commitment.0[0].0)
.ok_or("DKG not supported by ciphersuite")?;
let c_i = challenge::<C>(identifier, &R_i, &commitment.0[0].0).ok_or(Error::DKGNotSupported)?;
let mu_i = k + coefficients[0] * c_i.0;
let secret_package = Round1SecretPackage {
@ -162,9 +161,9 @@ where
pub fn keygen_part2<C: Ciphersuite>(
secret_package: Round1SecretPackage<C>,
round1_packages: &[Round1Package<C>],
) -> Result<(Round2SecretPackage<C>, Vec<Round2Package<C>>), &'static str> {
) -> Result<(Round2SecretPackage<C>, Vec<Round2Package<C>>), Error> {
if round1_packages.len() != (secret_package.max_signers - 1) as usize {
return Err("incorrect number of packages");
return Err(Error::IncorrectNumberOfPackages);
}
let mut round2_packages = Vec::new();
@ -179,11 +178,10 @@ pub fn keygen_part2<C: Ciphersuite>(
let R_ell = round1_package.proof_of_knowledge.R;
let mu_ell = round1_package.proof_of_knowledge.z;
let phi_ell0 = round1_package.commitment.0[0].0;
let c_ell =
challenge::<C>(ell, &R_ell, &phi_ell0).ok_or("DKG not supported by ciphersuite")?;
let c_ell = challenge::<C>(ell, &R_ell, &phi_ell0).ok_or(Error::DKGNotSupported)?;
if R_ell != <C::Group>::generator() * mu_ell - phi_ell0 * c_ell.0 {
return Err("Invalid proof of knowledge");
return Err(Error::InvalidProofOfKnowledge);
}
// Round 2, Step 1
@ -191,7 +189,7 @@ pub fn keygen_part2<C: Ciphersuite>(
// > Each P_i securely sends to each other participant P_ a secret share (, f_i()),
// > deleting f_i and each share afterward except for (i, f_i(i)),
// > which they keep for themselves.
let value = evaluate_polynomial(ell, &secret_package.coefficients)?;
let value = evaluate_polynomial(ell, &secret_package.coefficients);
round2_packages.push(Round2Package {
sender_identifier: secret_package.identifier,
@ -199,7 +197,7 @@ pub fn keygen_part2<C: Ciphersuite>(
secret_share: SigningShare(value),
});
}
let fii = evaluate_polynomial(secret_package.identifier, &secret_package.coefficients)?;
let fii = evaluate_polynomial(secret_package.identifier, &secret_package.coefficients);
Ok((
Round2SecretPackage {
identifier: secret_package.identifier,
@ -217,7 +215,7 @@ fn compute_verifying_keys<C: Ciphersuite>(
round2_packages: &[Round2Package<C>],
round1_packages_map: HashMap<Identifier<C>, &Round1Package<C>>,
round2_secret_package: &Round2SecretPackage<C>,
) -> Result<HashMap<Identifier<C>, VerifyingShare<C>>, &'static str> {
) -> Result<HashMap<Identifier<C>, VerifyingShare<C>>, Error> {
// Round 2, Step 4
//
// > Any participant can compute the public verification share of any other participant
@ -235,18 +233,17 @@ fn compute_verifying_keys<C: Ciphersuite>(
.iter()
.map(|p| {
// Get the commitment vector for this participant
Ok::<&VerifiableSecretSharingCommitment<C>, &'static str>(
Ok::<&VerifiableSecretSharingCommitment<C>, Error>(
&round1_packages_map
.get(&p.sender_identifier)
.ok_or("Round 1 package not found for Round 2 participant")?
.ok_or(Error::PackageNotFound)?
.commitment,
)
})
// Chain our own commitment vector
.chain(iter::once(Ok(&round2_secret_package.commitment)))
{
let result = evaluate_vss(commitments?, i)?;
y_i = y_i + result;
y_i = y_i + evaluate_vss(commitments?, i);
}
let y_i = VerifyingShare(y_i);
others_verifying_keys.insert(i, y_i);
@ -267,12 +264,12 @@ pub fn keygen_part3<C: Ciphersuite>(
round2_secret_package: &Round2SecretPackage<C>,
round1_packages: &[Round1Package<C>],
round2_packages: &[Round2Package<C>],
) -> Result<(KeyPackage<C>, PublicKeyPackage<C>), &'static str> {
) -> Result<(KeyPackage<C>, PublicKeyPackage<C>), Error> {
if round1_packages.len() != (round2_secret_package.max_signers - 1) as usize {
return Err("incorrect number of packages");
return Err(Error::IncorrectNumberOfPackages);
}
if round1_packages.len() != round2_packages.len() {
return Err("inconsistent number of packages");
return Err(Error::IncorrectNumberOfPackages);
}
let mut signing_share = <<C::Group as Group>::Field>::zero();
@ -286,7 +283,7 @@ pub fn keygen_part3<C: Ciphersuite>(
for round2_package in round2_packages {
// Sanity check; was the package really meant to us?
if round2_package.receiver_identifier != round2_secret_package.identifier {
return Err("Round 2 package receiver is not the current participant");
return Err(Error::IncorrectPackage);
}
// Round 2, Step 2
@ -299,7 +296,7 @@ pub fn keygen_part3<C: Ciphersuite>(
let commitment = &round1_packages_map
.get(&ell)
.ok_or("commitment package missing")?
.ok_or(Error::PackageNotFound)?
.commitment;
// The verification is exactly the same as the regular SecretShare verification;

View File

@ -76,6 +76,7 @@ where
// }
// }
#[cfg(any(test, feature = "test-impl"))]
impl<C> FromHex for Nonce<C>
where
C: Ciphersuite,
@ -139,6 +140,7 @@ where
}
}
#[cfg(any(test, feature = "test-impl"))]
impl<C> FromHex for NonceCommitment<C>
where
C: Ciphersuite,

View File

@ -86,11 +86,11 @@ where
public_key: &frost::keys::VerifyingShare<C>,
lambda_i: Scalar<C>,
challenge: &Challenge<C>,
) -> Result<(), &'static str> {
) -> Result<(), Error> {
if (<C::Group>::generator() * self.signature.z_share)
!= (group_commitment_share.0 + (public_key.0 * challenge.0 * lambda_i))
{
return Err("Invalid signature share");
return Err(Error::InvalidSignatureShare);
}
Ok(())
@ -131,7 +131,7 @@ pub fn sign<C: Ciphersuite>(
signing_package: &SigningPackage<C>,
signer_nonces: &round1::SigningNonces<C>,
key_package: &frost::keys::KeyPackage<C>,
) -> Result<SignatureShare<C>, &'static str> {
) -> Result<SignatureShare<C>, Error> {
// Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
// binding factor.
let binding_factor_list: frost::BindingFactorList<C> = signing_package.into();

View File

@ -72,6 +72,7 @@ where
}
}
#[cfg(any(test, feature = "test-impl"))]
impl<C> FromHex for VerifyingKey<C>
where
C: Ciphersuite,

View File

@ -175,7 +175,7 @@ pub mod keys {
max_signers: u16,
min_signers: u16,
mut rng: RNG,
) -> Result<(Vec<SecretShare>, PublicKeyPackage), &'static str> {
) -> Result<(Vec<SecretShare>, PublicKeyPackage), Error> {
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)
}
@ -266,7 +266,7 @@ pub mod round2 {
signing_package: &SigningPackage,
signer_nonces: &round1::SigningNonces,
key_package: &keys::KeyPackage,
) -> Result<SignatureShare, &'static str> {
) -> Result<SignatureShare, Error> {
frost::round2::sign(signing_package, signer_nonces, key_package)
}
}
@ -293,7 +293,7 @@ pub fn aggregate(
signing_package: &round2::SigningPackage,
signature_shares: &[round2::SignatureShare],
pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, &'static str> {
) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys)
}

View File

@ -253,7 +253,7 @@ pub mod keys {
max_signers: u16,
min_signers: u16,
mut rng: RNG,
) -> Result<(Vec<SecretShare>, PublicKeyPackage), &'static str> {
) -> Result<(Vec<SecretShare>, PublicKeyPackage), Error> {
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)
}
@ -343,7 +343,7 @@ pub mod round2 {
signing_package: &SigningPackage,
signer_nonces: &round1::SigningNonces,
key_package: &keys::KeyPackage,
) -> Result<SignatureShare, &'static str> {
) -> Result<SignatureShare, Error> {
frost::round2::sign(signing_package, signer_nonces, key_package)
}
}
@ -370,7 +370,7 @@ pub fn aggregate(
signing_package: &round2::SigningPackage,
signature_shares: &[round2::SignatureShare],
pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, &'static str> {
) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys)
}

View File

@ -231,7 +231,7 @@ pub mod keys {
max_signers: u16,
min_signers: u16,
mut rng: RNG,
) -> Result<(Vec<SecretShare>, PublicKeyPackage), &'static str> {
) -> Result<(Vec<SecretShare>, PublicKeyPackage), Error> {
frost::keys::keygen_with_dealer(max_signers, min_signers, &mut rng)
}
@ -322,7 +322,7 @@ pub mod round2 {
signing_package: &SigningPackage,
signer_nonces: &round1::SigningNonces,
key_package: &keys::KeyPackage,
) -> Result<SignatureShare, &'static str> {
) -> Result<SignatureShare, Error> {
frost::round2::sign(signing_package, signer_nonces, key_package)
}
}
@ -349,7 +349,7 @@ pub fn aggregate(
signing_package: &round2::SigningPackage,
signature_shares: &[round2::SignatureShare],
pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, &'static str> {
) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys)
}