add Identifier to InvalidSignatureShare and split Error type (#183)

* add Identifier to InvalidSignatureShare

* rustdoc

* also update secp256k1

* add Identifier::deserialize; make serialize public

* make it work with Ed448

* Some space

* Fixing spacing for rustfmt

* Revert  🤦‍♂️

Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>
This commit is contained in:
Conrado Gouvea 2022-12-12 19:04:10 -03:00 committed by GitHub
parent 359434b14f
commit e8dc692ca0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 196 additions and 133 deletions

View File

@ -49,7 +49,7 @@ where
/// [`VerifyingKey::verify`](crate::VerifyingKey::verify), which
/// requires borrowing the message data, the `Item` type is unlinked
/// from the lifetime of the message.
pub fn verify_single(self) -> Result<(), Error> {
pub fn verify_single(self) -> Result<(), Error<C>> {
self.vk.verify_prehashed(self.c, &self.sig)
}
}
@ -103,7 +103,7 @@ where
/// notation in the [protocol specification §B.1][ps].
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#reddsabatchverify
pub fn verify<R: RngCore + CryptoRng>(self, mut rng: R) -> Result<(), Error> {
pub fn verify<R: RngCore + CryptoRng>(self, mut rng: R) -> Result<(), Error<C>> {
let n = self.signatures.len();
let mut VK_coeffs = Vec::with_capacity(n);

View File

@ -2,10 +2,12 @@
use thiserror::Error;
use crate::{frost::Identifier, Ciphersuite};
/// An error related to FROST.
#[non_exhaustive]
#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)]
pub enum Error {
pub enum Error<C: Ciphersuite> {
/// min_signers is invalid
#[error("min_signers must be at least 2 and not larger than max_signers")]
InvalidMinSigners,
@ -18,12 +20,6 @@ pub enum Error {
/// This identifier is unserializable.
#[error("Malformed identifier is unserializable.")]
MalformedIdentifier,
/// The encoding of a group scalar was malformed.
#[error("Malformed scalar encoding.")]
MalformedScalar,
/// The encoding of a group element was malformed.
#[error("Malformed group element encoding.")]
MalformedElement,
/// The encoding of a signing key was malformed.
#[error("Malformed signing key encoding.")]
MalformedSigningKey,
@ -36,15 +32,6 @@ pub enum Error {
/// Signature verification failed.
#[error("Invalid signature.")]
InvalidSignature,
/// This scalar MUST NOT be zero.
#[error("Invalid for this scalar to be zero.")]
InvalidZeroScalar,
/// This element MUST NOT be the identity.
#[error("Invalid for this element to be the identity.")]
InvalidIdentityElement,
/// 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,
@ -53,7 +40,10 @@ pub enum Error {
IdentityCommitment,
/// Signature share verification failed.
#[error("Invalid signature share.")]
InvalidSignatureShare,
InvalidSignatureShare {
/// The identifier of the signer whose share validation failed.
signer: Identifier<C>,
},
/// Secret share verification failed.
#[error("Invalid secret share.")]
InvalidSecretShare,
@ -72,4 +62,37 @@ pub enum Error {
/// The proof of knowledge is not valid.
#[error("The proof of knowledge is not valid.")]
InvalidProofOfKnowledge,
/// Error in scalar Field.
#[error("Error in scalar Field.")]
FieldError(#[from] FieldError),
/// Error in elliptic curve Group.
#[error("Error in elliptic curve Group.")]
GroupError(#[from] GroupError),
}
/// An error related to a scalar Field.
#[non_exhaustive]
#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)]
pub enum FieldError {
/// The encoding of a group scalar was malformed.
#[error("Malformed scalar encoding.")]
MalformedScalar,
/// This scalar MUST NOT be zero.
#[error("Invalid for this scalar to be zero.")]
InvalidZeroScalar,
}
/// An error related to a Group (usually an elliptic curve or constructed from one) or one of its Elements.
#[non_exhaustive]
#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)]
pub enum GroupError {
/// The encoding of a group element was malformed.
#[error("Malformed group element encoding.")]
MalformedElement,
/// This element MUST NOT be the identity.
#[error("Invalid for this element to be the identity.")]
InvalidIdentityElement,
/// This element MUST have (large) prime order.
#[error("Invalid for this element to not have large prime order.")]
InvalidNonPrimeOrderElement,
}

View File

@ -44,8 +44,10 @@ where
/// Deserializes [`BindingFactor`] from bytes.
pub fn from_bytes(
bytes: <<C::Group as Group>::Field as Field>::Serialization,
) -> Result<Self, Error> {
<<C::Group as Group>::Field>::deserialize(&bytes).map(|scalar| Self(scalar))
) -> Result<Self, Error<C>> {
<<C::Group as Group>::Field>::deserialize(&bytes)
.map(|scalar| Self(scalar))
.map_err(|e| e.into())
}
/// Serializes [`BindingFactor`] to bytes.
@ -139,7 +141,7 @@ where
fn derive_lagrange_coeff<C: Ciphersuite>(
signer_id: &Identifier<C>,
signing_package: &SigningPackage<C>,
) -> Result<Scalar<C>, Error> {
) -> Result<Scalar<C>, Error<C>> {
let zero = <<C::Group as Group>::Field>::zero();
let mut num = <<C::Group as Group>::Field>::one();
@ -259,7 +261,7 @@ impl<C> TryFrom<&SigningPackage<C>> for GroupCommitment<C>
where
C: Ciphersuite,
{
type Error = Error;
type Error = Error<C>;
/// Generates the group commitment which is published as part of the joint
/// Schnorr signature.
@ -267,7 +269,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>, Error> {
fn try_from(signing_package: &SigningPackage<C>) -> Result<GroupCommitment<C>, Error<C>> {
let binding_factor_list: BindingFactorList<C> = signing_package.into();
let identity = <C::Group>::identity();
@ -317,7 +319,7 @@ pub fn aggregate<C>(
signing_package: &SigningPackage<C>,
signature_shares: &[round2::SignatureShare<C>],
pubkeys: &keys::PublicKeyPackage<C>,
) -> Result<Signature<C>, Error>
) -> Result<Signature<C>, Error<C>>
where
C: Ciphersuite,
{

View File

@ -5,7 +5,7 @@ use std::{
hash::{Hash, Hasher},
};
use crate::{Ciphersuite, Error, Field, Group, Scalar};
use crate::{Ciphersuite, Error, Field, FieldError, Group, Scalar};
/// A FROST participant identifier.
///
@ -19,10 +19,23 @@ impl<C> Identifier<C>
where
C: Ciphersuite,
{
// Serialize the underlying scalar.
pub(crate) fn serialize(&self) -> <<C::Group as Group>::Field as Field>::Serialization {
/// Serialize the identifier using the ciphersuite encoding.
pub fn serialize(&self) -> <<C::Group as Group>::Field as Field>::Serialization {
<<C::Group as Group>::Field>::serialize(&self.0)
}
/// Deserialize an Identifier from a serialized buffer.
/// Returns an error if it attempts to deserialize zero.
pub fn deserialize(
buf: &<<C::Group as Group>::Field as Field>::Serialization,
) -> Result<Self, Error<C>> {
let scalar = <<C::Group as Group>::Field>::deserialize(buf)?;
if scalar == <<C::Group as Group>::Field>::zero() {
Err(FieldError::InvalidZeroScalar.into())
} else {
Ok(Self(scalar))
}
}
}
impl<C> Eq for Identifier<C> where C: Ciphersuite {}
@ -109,11 +122,11 @@ impl<C> TryFrom<u16> for Identifier<C>
where
C: Ciphersuite,
{
type Error = Error;
type Error = Error<C>;
fn try_from(n: u16) -> Result<Identifier<C>, Self::Error> {
if n == 0 {
Err(Self::Error::InvalidZeroScalar)
Err(FieldError::InvalidZeroScalar.into())
} else {
// Classic left-to-right double-and-add algorithm that skips the first bit 1 (since
// identifiers are never zero, there is always a bit 1), thus `sum` starts with 1 too.

View File

@ -1,4 +1,5 @@
//! FROST keys, keygen, key shares
#![allow(clippy::type_complexity)]
use std::{
collections::HashMap,
@ -44,8 +45,10 @@ where
/// Deserialize from bytes
pub fn from_bytes(
bytes: <<C::Group as Group>::Field as Field>::Serialization,
) -> Result<Self, Error> {
<<C::Group as Group>::Field>::deserialize(&bytes).map(|scalar| Self(scalar))
) -> Result<Self, Error<C>> {
<<C::Group as Group>::Field>::deserialize(&bytes)
.map(|scalar| Self(scalar))
.map_err(|e| e.into())
}
/// Serialize to bytes
@ -124,8 +127,10 @@ where
/// Deserialize from bytes
pub fn from_bytes(
bytes: <<C::Group as Group>::Field as Field>::Serialization,
) -> Result<Self, Error> {
<<C::Group as Group>::Field>::deserialize(&bytes).map(|scalar| Self(scalar))
) -> Result<Self, Error<C>> {
<<C::Group as Group>::Field>::deserialize(&bytes)
.map(|scalar| Self(scalar))
.map_err(|e| e.into())
}
/// Serialize to bytes
@ -184,8 +189,10 @@ where
C: Ciphersuite,
{
/// Deserialize from bytes
pub fn from_bytes(bytes: <C::Group as Group>::Serialization) -> Result<Self, Error> {
<C::Group as Group>::deserialize(&bytes).map(|element| Self(element))
pub fn from_bytes(bytes: <C::Group as Group>::Serialization) -> Result<Self, Error<C>> {
<C::Group as Group>::deserialize(&bytes)
.map(|element| Self(element))
.map_err(|e| e.into())
}
/// Serialize to bytes
@ -282,7 +289,7 @@ 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>), Error> {
pub fn verify(&self) -> Result<(VerifyingShare<C>, VerifyingKey<C>), Error<C>> {
let f_result = <C::Group>::generator() * self.value.0;
let result = evaluate_vss(&self.commitment, self.identifier);
@ -314,7 +321,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>), Error> {
) -> Result<(Vec<SecretShare<C>>, PublicKeyPackage<C>), Error<C>> {
let mut bytes = [0; 64];
rng.fill_bytes(&mut bytes);
@ -425,7 +432,7 @@ impl<C> TryFrom<SecretShare<C>> for KeyPackage<C>
where
C: Ciphersuite,
{
type Error = Error;
type Error = Error<C>;
/// Tries to verify a share and construct a [`KeyPackage`] from it.
///
@ -435,7 +442,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, Error> {
fn try_from(secret_share: SecretShare<C>) -> Result<Self, Error<C>> {
let (public, group_public) = secret_share.verify()?;
Ok(KeyPackage {
@ -474,7 +481,7 @@ 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>), Error> {
) -> Result<(Vec<Scalar<C>>, VerifiableSecretSharingCommitment<C>), Error<C>> {
if min_signers < 2 {
return Err(Error::InvalidMinSigners);
}
@ -529,7 +536,7 @@ pub(crate) fn generate_secret_shares<C: Ciphersuite>(
max_signers: u16,
min_signers: u16,
coefficients: Vec<Scalar<C>>,
) -> Result<Vec<SecretShare<C>>, Error> {
) -> Result<Vec<SecretShare<C>>, Error<C>> {
let mut secret_shares: Vec<SecretShare<C>> = Vec::with_capacity(max_signers as usize);
let (coefficients, commitment) =

View File

@ -97,7 +97,7 @@ pub fn keygen_part1<C: Ciphersuite, R: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
mut rng: R,
) -> Result<(Round1SecretPackage<C>, Round1Package<C>), Error> {
) -> Result<(Round1SecretPackage<C>, Round1Package<C>), Error<C>> {
let secret: SharedSecret<C> = SharedSecret::random(&mut rng);
// Round 1, Step 1
@ -167,7 +167,7 @@ where
pub fn keygen_part2<C: Ciphersuite>(
secret_package: Round1SecretPackage<C>,
round1_packages: &[Round1Package<C>],
) -> Result<(Round2SecretPackage<C>, Vec<Round2Package<C>>), Error> {
) -> Result<(Round2SecretPackage<C>, Vec<Round2Package<C>>), Error<C>> {
if round1_packages.len() != (secret_package.max_signers - 1) as usize {
return Err(Error::IncorrectNumberOfPackages);
}
@ -221,7 +221,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>>, Error> {
) -> Result<HashMap<Identifier<C>, VerifyingShare<C>>, Error<C>> {
// Round 2, Step 4
//
// > Any participant can compute the public verification share of any other participant
@ -239,7 +239,7 @@ fn compute_verifying_keys<C: Ciphersuite>(
.iter()
.map(|p| {
// Get the commitment vector for this participant
Ok::<&VerifiableSecretSharingCommitment<C>, Error>(
Ok::<&VerifiableSecretSharingCommitment<C>, Error<C>>(
&round1_packages_map
.get(&p.sender_identifier)
.ok_or(Error::PackageNotFound)?
@ -270,7 +270,7 @@ pub fn keygen_part3<C: Ciphersuite>(
round2_secret_package: &Round2SecretPackage<C>,
round1_packages: &[Round1Package<C>],
round2_packages: &[Round2Package<C>],
) -> Result<(KeyPackage<C>, PublicKeyPackage<C>), Error> {
) -> Result<(KeyPackage<C>, PublicKeyPackage<C>), Error<C>> {
if round1_packages.len() != (round2_secret_package.max_signers - 1) as usize {
return Err(Error::IncorrectNumberOfPackages);
}

View File

@ -57,8 +57,10 @@ where
/// Deserialize [`Nonce`] from bytes
pub fn from_bytes(
bytes: <<C::Group as Group>::Field as Field>::Serialization,
) -> Result<Self, Error> {
<<C::Group as Group>::Field>::deserialize(&bytes).map(|scalar| Self(scalar))
) -> Result<Self, Error<C>> {
<<C::Group as Group>::Field>::deserialize(&bytes)
.map(|scalar| Self(scalar))
.map_err(|e| e.into())
}
/// Serialize [`Nonce`] to bytes
@ -101,8 +103,10 @@ where
C: Ciphersuite,
{
/// Deserialize [`NonceCommitment`] from bytes
pub fn from_bytes(bytes: <C::Group as Group>::Serialization) -> Result<Self, Error> {
<C::Group>::deserialize(&bytes).map(|element| Self(element))
pub fn from_bytes(bytes: <C::Group as Group>::Serialization) -> Result<Self, Error<C>> {
<C::Group>::deserialize(&bytes)
.map(|element| Self(element))
.map_err(|e| e.into())
}
/// Serialize [`NonceCommitment`] to bytes

View File

@ -22,8 +22,10 @@ where
/// Deserialize [`SignatureResponse`] from bytes
pub fn from_bytes(
bytes: <<C::Group as Group>::Field as Field>::Serialization,
) -> Result<Self, Error> {
<<C::Group as Group>::Field>::deserialize(&bytes).map(|scalar| Self { z_share: scalar })
) -> Result<Self, Error<C>> {
<<C::Group as Group>::Field>::deserialize(&bytes)
.map(|scalar| Self { z_share: scalar })
.map_err(|e| e.into())
}
/// Serialize [`SignatureResponse`] to bytes
@ -86,11 +88,13 @@ where
public_key: &frost::keys::VerifyingShare<C>,
lambda_i: Scalar<C>,
challenge: &Challenge<C>,
) -> Result<(), Error> {
) -> Result<(), Error<C>> {
if (<C::Group>::generator() * self.signature.z_share)
!= (group_commitment_share.0 + (public_key.0 * challenge.0 * lambda_i))
{
return Err(Error::InvalidSignatureShare);
return Err(Error::InvalidSignatureShare {
signer: self.identifier,
});
}
Ok(())
@ -131,7 +135,7 @@ pub fn sign<C: Ciphersuite>(
signing_package: &SigningPackage<C>,
signer_nonces: &round1::SigningNonces<C>,
key_package: &frost::keys::KeyPackage<C>,
) -> Result<SignatureShare<C>, Error> {
) -> Result<SignatureShare<C>, Error<C>> {
// 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

@ -23,7 +23,7 @@ mod signing_key;
pub mod tests;
mod verifying_key;
pub use error::Error;
pub use error::{Error, FieldError, GroupError};
pub use signature::Signature;
pub use signing_key::SigningKey;
pub use verifying_key::VerifyingKey;
@ -57,7 +57,7 @@ pub trait Field: Copy + Clone {
/// Computes the multiplicative inverse of an element of the scalar field, failing if the
/// element is zero.
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, Error>;
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError>;
/// Generate a random scalar from the entire space [0, l-1]
///
@ -83,7 +83,7 @@ pub trait Field: Copy + Clone {
/// resulting [`Scalar`] is zero
///
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-3.1-3.9>
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, Error>;
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError>;
}
/// An element of the [`Ciphersuite`] `C`'s [`Group`]'s scalar [`Field`].
@ -146,7 +146,7 @@ pub trait Group: Copy + Clone + PartialEq {
/// resulting [`Element`] is the identity element of the group
///
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-3.1-3.7>
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, Error>;
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError>;
}
/// An element of the [`Ciphersuite`] `C`'s [`Group`].
@ -156,7 +156,7 @@ pub type Element<C> = <<C as Ciphersuite>::Group as Group>::Element;
/// function.
///
/// [FROST ciphersuite]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-ciphersuites
pub trait Ciphersuite: Copy + Clone + PartialEq {
pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
/// The prime order group (or subgroup) that this ciphersuite operates over.
type Group: Group;
@ -226,7 +226,7 @@ pub trait Ciphersuite: Copy + Clone + PartialEq {
msg: &[u8],
signature: &Signature<Self>,
public_key: &VerifyingKey<Self>,
) -> Result<(), Error> {
) -> Result<(), Error<Self>> {
let c = crate::challenge::<Self>(&signature.R, &public_key.element, msg);
public_key.verify_prehashed(c, signature)

View File

@ -21,7 +21,7 @@ where
<C::Group as Group>::Field: Field,
{
/// Converts bytes as [`Ciphersuite::SignatureSerialization`] into a `Signature<C>`.
pub fn from_bytes(bytes: C::SignatureSerialization) -> Result<Self, Error> {
pub fn from_bytes(bytes: C::SignatureSerialization) -> Result<Self, Error<C>> {
// To compute the expected length of the encoded point, encode the generator
// and get its length. Note that we can't use the identity because it can be encoded
// shorter in some cases (e.g. P-256, which uses SEC1 encoding).

View File

@ -27,9 +27,10 @@ where
/// Deserialize from bytes
pub fn from_bytes(
bytes: <<C::Group as Group>::Field as Field>::Serialization,
) -> Result<SigningKey<C>, Error> {
) -> Result<SigningKey<C>, Error<C>> {
<<C::Group as Group>::Field as Field>::deserialize(&bytes)
.map(|scalar| SigningKey { scalar })
.map_err(|e| e.into())
}
/// Serialize `SigningKey` to bytes

View File

@ -24,8 +24,12 @@ where
// }
/// Deserialize from bytes
pub fn from_bytes(bytes: <C::Group as Group>::Serialization) -> Result<VerifyingKey<C>, Error> {
<C::Group>::deserialize(&bytes).map(|element| VerifyingKey { element })
pub fn from_bytes(
bytes: <C::Group as Group>::Serialization,
) -> Result<VerifyingKey<C>, Error<C>> {
<C::Group>::deserialize(&bytes)
.map(|element| VerifyingKey { element })
.map_err(|e| e.into())
}
/// Serialize `VerifyingKey` to bytes
@ -39,7 +43,7 @@ where
&self,
challenge: Challenge<C>,
signature: &Signature<C>,
) -> Result<(), Error> {
) -> Result<(), Error<C>> {
// Verify check is h * ( - z * B + R + c * A) == 0
// h * ( z * B - c * A - R) == 0
//
@ -56,7 +60,7 @@ where
}
/// Verify a purported `signature` over `msg` made by this verification key.
pub fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<(), Error> {
pub fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<(), Error<C>> {
C::verify_signature(msg, signature, self)
}
}

View File

@ -24,7 +24,7 @@ let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers
let key_packages: HashMap<_, _> = shares
.into_iter()
.map(|share| Ok((share.identifier, frost::keys::KeyPackage::try_from(share)?)))
.collect::<Result<_, _>>()?;
.collect::<Result<_, frost::Error>>()?;
let mut nonces = HashMap::new();
let mut commitments = HashMap::new();

View File

@ -11,12 +11,13 @@ use curve25519_dalek::{
use rand_core::{CryptoRng, RngCore};
use sha2::{digest::Update, Digest, Sha512};
use frost_core::{frost, Ciphersuite, Field, Group};
use frost_core::{frost, Ciphersuite, Field, Group, GroupError};
#[cfg(test)]
mod tests;
pub use frost_core::Error;
/// An error.
pub type Error = frost_core::Error<Ed25519Sha512>;
/// An implementation of the FROST(Ed25519, SHA-512) ciphersuite scalar field.
pub type Ed25519ScalarField = frost_ristretto255::RistrettoScalarField;
@ -48,18 +49,18 @@ impl Group for Ed25519Group {
element.compress().to_bytes()
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, Error> {
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
match CompressedEdwardsY::from_slice(buf.as_ref()).decompress() {
Some(point) => {
if point == Self::identity() {
Err(Error::InvalidIdentityElement)
Err(GroupError::InvalidIdentityElement)
} else if point.is_torsion_free() {
Ok(point)
} else {
Err(Error::InvalidNonPrimeOrderElement)
Err(GroupError::InvalidNonPrimeOrderElement)
}
}
None => Err(Error::MalformedElement),
None => Err(GroupError::MalformedElement),
}
}
}
@ -69,7 +70,7 @@ impl Group for Ed25519Group {
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-6.1-1
const CONTEXT_STRING: &str = "FROST-ED25519-SHA512-v11";
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
/// An implementation of the FROST(Ed25519, SHA-512) ciphersuite.
pub struct Ed25519Sha512;

View File

@ -1,5 +1,5 @@
use curve25519_dalek::{edwards::EdwardsPoint, traits::Identity};
use frost_core::{Ciphersuite, Group};
use frost_core::{Ciphersuite, Group, GroupError};
use frost_ed25519::*;
use rand::thread_rng;
@ -36,5 +36,5 @@ fn check_deserialize_identity() {
let encoded_identity = EdwardsPoint::identity().compress().to_bytes();
let r = <<Ed25519Sha512 as Ciphersuite>::Group as Group>::deserialize(&encoded_identity);
assert_eq!(r, Err(Error::InvalidIdentityElement));
assert_eq!(r, Err(GroupError::InvalidIdentityElement));
}

View File

@ -24,7 +24,7 @@ let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers
let key_packages: HashMap<_, _> = shares
.into_iter()
.map(|share| Ok((share.identifier, frost::keys::KeyPackage::try_from(share)?)))
.collect::<Result<_, _>>()?;
.collect::<Result<_, frost::Error>>()?;
let mut nonces = HashMap::new();
let mut commitments = HashMap::new();

View File

@ -12,12 +12,13 @@ use sha3::{
Shake256,
};
use frost_core::{frost, Ciphersuite, Field, Group};
use frost_core::{frost, Ciphersuite, Field, FieldError, Group, GroupError};
#[cfg(test)]
mod tests;
pub use frost_core::Error;
/// An error.
pub type Error = frost_core::Error<Ed448Shake256>;
#[derive(Clone, Copy)]
/// An implementation of the FROST(Ed448, SHAKE256) ciphersuite scalar field.
@ -36,9 +37,9 @@ impl Field for Ed448ScalarField {
Scalar::one()
}
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, Error> {
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
if *scalar == <Self as Field>::zero() {
Err(Error::InvalidZeroScalar)
Err(FieldError::InvalidZeroScalar)
} else {
Ok(scalar.invert())
}
@ -53,10 +54,10 @@ impl Field for Ed448ScalarField {
std::array::from_fn(|i| if i < 56 { bytes[i] } else { 0 })
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, Error> {
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
match Scalar::from_canonical_bytes(*buf) {
Some(s) => Ok(s),
None => Err(Error::MalformedScalar),
None => Err(FieldError::MalformedScalar),
}
}
@ -92,18 +93,18 @@ impl Group for Ed448Group {
element.compress().0
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, Error> {
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
match CompressedEdwardsY(*buf).decompress() {
Some(point) => {
if point == Self::identity() {
Err(Error::InvalidIdentityElement)
Err(GroupError::InvalidIdentityElement)
} else if point.is_torsion_free() {
Ok(point)
} else {
Err(Error::InvalidNonPrimeOrderElement)
Err(GroupError::InvalidNonPrimeOrderElement)
}
}
None => Err(Error::MalformedElement),
None => Err(GroupError::MalformedElement),
}
}
}
@ -129,7 +130,7 @@ fn hash_to_scalar(inputs: &[&[u8]]) -> Scalar {
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-6.3-1
const CONTEXT_STRING: &str = "FROST-ED448-SHAKE256-v11";
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
/// An implementation of the FROST(Ed448, SHAKE256) ciphersuite.
pub struct Ed448Shake256;

View File

@ -1,5 +1,5 @@
use ed448_goldilocks::curve::ExtendedPoint;
use frost_core::{Ciphersuite, Group};
use frost_core::{Ciphersuite, Group, GroupError};
use frost_ed448::*;
use rand::thread_rng;
@ -40,5 +40,5 @@ fn check_deserialize_identity() {
let encoded_identity = ExtendedPoint::identity().compress().0;
let r = <<Ed448Shake256 as Ciphersuite>::Group as Group>::deserialize(&encoded_identity);
assert_eq!(r, Err(Error::InvalidIdentityElement));
assert_eq!(r, Err(GroupError::InvalidIdentityElement));
}

View File

@ -24,7 +24,7 @@ let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers
let key_packages: HashMap<_, _> = shares
.into_iter()
.map(|share| Ok((share.identifier, frost::keys::KeyPackage::try_from(share)?)))
.collect::<Result<_, _>>()?;
.collect::<Result<_, frost::Error>>()?;
let mut nonces = HashMap::new();
let mut commitments = HashMap::new();

View File

@ -14,12 +14,13 @@ use p256::{
use rand_core::{CryptoRng, RngCore};
use sha2::{digest::Update, Digest, Sha256};
use frost_core::{frost, Ciphersuite, Field, Group};
use frost_core::{frost, Ciphersuite, Field, FieldError, Group, GroupError};
#[cfg(test)]
mod tests;
pub use frost_core::Error;
/// An error.
pub type Error = frost_core::Error<P256Sha256>;
#[derive(Clone, Copy)]
/// An implementation of the FROST(P-256, SHA-256) ciphersuite scalar field.
@ -38,11 +39,11 @@ impl Field for P256ScalarField {
Scalar::ONE
}
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, Error> {
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
// [`p256::Scalar`]'s Eq/PartialEq does a constant-time comparison using
// `ConstantTimeEq`
if *scalar == <Self as Field>::zero() {
Err(Error::InvalidZeroScalar)
Err(FieldError::InvalidZeroScalar)
} else {
Ok(scalar.invert().unwrap())
}
@ -56,11 +57,11 @@ impl Field for P256ScalarField {
scalar.to_bytes().into()
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, Error> {
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
let field_bytes: &p256::FieldBytes = buf.into();
match Scalar::from_repr(*field_bytes).into() {
Some(s) => Ok(s),
None => Err(Error::MalformedScalar),
None => Err(FieldError::MalformedScalar),
}
}
@ -119,9 +120,9 @@ impl Group for P256Group {
fixed_serialized
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, Error> {
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
let encoded_point =
p256::EncodedPoint::from_bytes(buf).map_err(|_| Error::MalformedElement)?;
p256::EncodedPoint::from_bytes(buf).map_err(|_| GroupError::MalformedElement)?;
match Option::<AffinePoint>::from(AffinePoint::from_encoded_point(&encoded_point)) {
Some(point) => {
@ -129,12 +130,12 @@ impl Group for P256Group {
// This is actually impossible since the identity is encoded a a single byte
// which will never happen since we receive a 33-byte buffer.
// We leave the check for consistency.
Err(Error::InvalidIdentityElement)
Err(GroupError::InvalidIdentityElement)
} else {
Ok(ProjectivePoint::from(point))
}
}
None => Err(Error::MalformedElement),
None => Err(GroupError::MalformedElement),
}
}
}
@ -144,7 +145,7 @@ impl Group for P256Group {
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-6.4-1
const CONTEXT_STRING: &str = "FROST-P256-SHA256-v11";
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
/// An implementation of the FROST(P-256, SHA-256) ciphersuite.
pub struct P256Sha256;

View File

@ -1,4 +1,4 @@
use frost_core::{Ciphersuite, Group};
use frost_core::{Ciphersuite, Group, GroupError};
use frost_p256::*;
use rand::thread_rng;
@ -37,5 +37,5 @@ fn check_deserialize_identity() {
let encoded_identity = [0u8; 33];
let r = <<P256Sha256 as Ciphersuite>::Group as Group>::deserialize(&encoded_identity);
assert_eq!(r, Err(Error::MalformedElement));
assert_eq!(r, Err(GroupError::MalformedElement));
}

View File

@ -24,7 +24,7 @@ let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers
let key_packages: HashMap<_, _> = shares
.into_iter()
.map(|share| Ok((share.identifier, frost::keys::KeyPackage::try_from(share)?)))
.collect::<Result<_, _>>()?;
.collect::<Result<_, frost::Error>>()?;
let mut nonces = HashMap::new();
let mut commitments = HashMap::new();

View File

@ -11,12 +11,13 @@ use curve25519_dalek::{
use rand_core::{CryptoRng, RngCore};
use sha2::{digest::Update, Digest, Sha512};
use frost_core::{frost, Ciphersuite, Field, Group};
use frost_core::{frost, Ciphersuite, Field, FieldError, Group, GroupError};
#[cfg(test)]
mod tests;
pub use frost_core::Error;
/// An error.
pub type Error = frost_core::Error<Ristretto255Sha512>;
#[derive(Clone, Copy)]
/// An implementation of the FROST(ristretto255, SHA-512) ciphersuite scalar field.
@ -35,11 +36,11 @@ impl Field for RistrettoScalarField {
Scalar::one()
}
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, Error> {
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
// [`curve25519_dalek::scalar::Scalar`]'s Eq/PartialEq does a constant-time comparison using
// `ConstantTimeEq`
if *scalar == <Self as Field>::zero() {
Err(Error::InvalidZeroScalar)
Err(FieldError::InvalidZeroScalar)
} else {
Ok(scalar.invert())
}
@ -53,10 +54,10 @@ impl Field for RistrettoScalarField {
scalar.to_bytes()
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, Error> {
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
match Scalar::from_canonical_bytes(*buf) {
Some(s) => Ok(s),
None => Err(Error::MalformedScalar),
None => Err(FieldError::MalformedScalar),
}
}
@ -92,16 +93,16 @@ impl Group for RistrettoGroup {
element.compress().to_bytes()
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, Error> {
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
match CompressedRistretto::from_slice(buf.as_ref()).decompress() {
Some(point) => {
if point == Self::identity() {
Err(Error::InvalidIdentityElement)
Err(GroupError::InvalidIdentityElement)
} else {
Ok(point)
}
}
None => Err(Error::MalformedElement),
None => Err(GroupError::MalformedElement),
}
}
}
@ -111,7 +112,7 @@ impl Group for RistrettoGroup {
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-6.2-1
const CONTEXT_STRING: &str = "FROST-RISTRETTO255-SHA512-v11";
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
/// An implementation of the FROST(ristretto255, SHA-512) ciphersuite.
pub struct Ristretto255Sha512;

View File

@ -1,5 +1,5 @@
use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity};
use frost_core::{Ciphersuite, Group};
use frost_core::{Ciphersuite, Group, GroupError};
use frost_ristretto255::*;
use rand::thread_rng;
@ -36,5 +36,5 @@ fn check_deserialize_identity() {
let encoded_identity = RistrettoPoint::identity().compress().to_bytes();
let r = <<Ristretto255Sha512 as Ciphersuite>::Group as Group>::deserialize(&encoded_identity);
assert_eq!(r, Err(Error::InvalidIdentityElement));
assert_eq!(r, Err(GroupError::InvalidIdentityElement));
}

View File

@ -24,7 +24,7 @@ let (shares, pubkeys) = frost::keys::keygen_with_dealer(max_signers, min_signers
let key_packages: HashMap<_, _> = shares
.into_iter()
.map(|share| Ok((share.identifier, frost::keys::KeyPackage::try_from(share)?)))
.collect::<Result<_, _>>()?;
.collect::<Result<_, frost::Error>>()?;
let mut nonces = HashMap::new();
let mut commitments = HashMap::new();

View File

@ -16,12 +16,13 @@ use k256::{
use rand_core::{CryptoRng, RngCore};
use sha2::{digest::Update, Digest, Sha256};
use frost_core::{frost, Ciphersuite, Field, Group};
use frost_core::{frost, Ciphersuite, Field, FieldError, Group, GroupError};
#[cfg(test)]
mod tests;
pub use frost_core::Error;
/// An error.
pub type Error = frost_core::Error<Secp256K1Sha256>;
#[derive(Clone, Copy)]
/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite scalar field.
@ -40,10 +41,10 @@ impl Field for Secp256K1ScalarField {
Scalar::ONE
}
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, Error> {
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
// [`Scalar`]'s Eq/PartialEq does a constant-time comparison
if *scalar == <Self as Field>::zero() {
Err(Error::InvalidZeroScalar)
Err(FieldError::InvalidZeroScalar)
} else {
Ok(scalar.invert().unwrap())
}
@ -57,11 +58,11 @@ impl Field for Secp256K1ScalarField {
scalar.to_bytes().into()
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, Error> {
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
let field_bytes: &k256::FieldBytes = buf.into();
match Scalar::from_repr(*field_bytes).into() {
Some(s) => Ok(s),
None => Err(Error::MalformedScalar),
None => Err(FieldError::MalformedScalar),
}
}
@ -121,9 +122,9 @@ impl Group for Secp256K1Group {
fixed_serialized
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, Error> {
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
let encoded_point =
k256::EncodedPoint::from_bytes(buf).map_err(|_| Error::MalformedElement)?;
k256::EncodedPoint::from_bytes(buf).map_err(|_| GroupError::MalformedElement)?;
match Option::<AffinePoint>::from(AffinePoint::from_encoded_point(&encoded_point)) {
Some(point) => {
@ -131,12 +132,12 @@ impl Group for Secp256K1Group {
// This is actually impossible since the identity is encoded a a single byte
// which will never happen since we receive a 33-byte buffer.
// We leave the check for consistency.
Err(Error::InvalidIdentityElement)
Err(GroupError::InvalidIdentityElement)
} else {
Ok(ProjectivePoint::from(point))
}
}
None => Err(Error::MalformedElement),
None => Err(GroupError::MalformedElement),
}
}
}
@ -172,7 +173,7 @@ fn hash_to_field(msg: &[u8], dst: &[u8]) -> Scalar {
/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-6.5-1
const CONTEXT_STRING: &str = "FROST-secp256k1-SHA256-v11";
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite.
pub struct Secp256K1Sha256;

View File

@ -1,4 +1,4 @@
use frost_core::{Ciphersuite, Group};
use frost_core::{Ciphersuite, Group, GroupError};
use frost_secp256k1::*;
use rand::thread_rng;
@ -37,5 +37,5 @@ fn check_deserialize_identity() {
let encoded_identity = [0u8; 33];
let r = <<Secp256K1Sha256 as Ciphersuite>::Group as Group>::deserialize(&encoded_identity);
assert_eq!(r, Err(Error::MalformedElement));
assert_eq!(r, Err(GroupError::MalformedElement));
}