frost-core: split part of lib.rs into traits.rs and serialization.rs (#569)
This commit is contained in:
parent
a0df08e30a
commit
408540fb86
|
@ -9,6 +9,8 @@ Entries are listed in reverse chronological order.
|
||||||
* The `frost-core::frost` module contents were merged into `frost-core`, thus
|
* The `frost-core::frost` module contents were merged into `frost-core`, thus
|
||||||
eliminating the `frost` module. You can adapt any calling code with e.g.
|
eliminating the `frost` module. You can adapt any calling code with e.g.
|
||||||
changing `use frost_core::frost::*` to `use frost-core::*`.
|
changing `use frost_core::frost::*` to `use frost-core::*`.
|
||||||
|
* `Challenge`, `BindingFactor`, `BindingFactorList` and `GroupCommitment`
|
||||||
|
are no longer public (you can use them with the `internals` feature).
|
||||||
* Both serde serialization and the default byte-oriented serialization now
|
* Both serde serialization and the default byte-oriented serialization now
|
||||||
include a version field (a u8) at the beginning which is always 0 for now. The
|
include a version field (a u8) at the beginning which is always 0 for now. The
|
||||||
ciphersuite ID field was moved from the last field to the second field, after
|
ciphersuite ID field was moved from the last field to the second field, after
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::{
|
||||||
use crate::{Ciphersuite, Error, Field, FieldError, Group, Scalar};
|
use crate::{Ciphersuite, Error, Field, FieldError, Group, Scalar};
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::ScalarSerialization;
|
use crate::serialization::ScalarSerialization;
|
||||||
|
|
||||||
/// A FROST participant identifier.
|
/// A FROST participant identifier.
|
||||||
///
|
///
|
||||||
|
|
|
@ -17,12 +17,13 @@ use rand_core::{CryptoRng, RngCore};
|
||||||
use zeroize::{DefaultIsZeroes, Zeroize};
|
use zeroize::{DefaultIsZeroes, Zeroize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Ciphersuite, Deserialize, Element, Error, Field, Group, Header, Identifier, Scalar, Serialize,
|
serialization::{Deserialize, Serialize},
|
||||||
SigningKey, VerifyingKey,
|
Ciphersuite, Element, Error, Field, Group, Header, Identifier, Scalar, SigningKey,
|
||||||
|
VerifyingKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::{ElementSerialization, ScalarSerialization};
|
use crate::serialization::{ElementSerialization, ScalarSerialization};
|
||||||
|
|
||||||
use super::compute_lagrange_coefficient;
|
use super::compute_lagrange_coefficient;
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ pub mod round1 {
|
||||||
use derive_getters::Getters;
|
use derive_getters::Getters;
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use crate::{Deserialize, Serialize};
|
use crate::serialization::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ pub mod round2 {
|
||||||
use derive_getters::Getters;
|
use derive_getters::Getters;
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use crate::{Deserialize, Serialize};
|
use crate::serialization::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ use std::{
|
||||||
default::Default,
|
default::Default,
|
||||||
fmt::{self, Debug},
|
fmt::{self, Debug},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::{Add, Mul, Sub},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use derive_getters::Getters;
|
use derive_getters::Getters;
|
||||||
|
@ -24,10 +23,6 @@ use hex::FromHex;
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand_core::{CryptoRng, RngCore};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
// Re-export serde
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
pub use serde;
|
|
||||||
|
|
||||||
pub mod batch;
|
pub mod batch;
|
||||||
#[cfg(any(test, feature = "test-impl"))]
|
#[cfg(any(test, feature = "test-impl"))]
|
||||||
pub mod benches;
|
pub mod benches;
|
||||||
|
@ -37,339 +32,37 @@ pub mod keys;
|
||||||
pub mod round1;
|
pub mod round1;
|
||||||
pub mod round2;
|
pub mod round2;
|
||||||
mod scalar_mul;
|
mod scalar_mul;
|
||||||
|
// We'd like to make this conditionally pub but the attribute below does
|
||||||
|
// not work yet (https://github.com/rust-lang/rust/issues/54727)
|
||||||
|
// #[cfg_attr(feature = "internals", visibility::make(pub))]
|
||||||
|
pub mod serialization;
|
||||||
mod signature;
|
mod signature;
|
||||||
mod signing_key;
|
mod signing_key;
|
||||||
#[cfg(any(test, feature = "test-impl"))]
|
#[cfg(any(test, feature = "test-impl"))]
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
|
mod traits;
|
||||||
mod verifying_key;
|
mod verifying_key;
|
||||||
|
|
||||||
pub use self::identifier::Identifier;
|
|
||||||
use crate::scalar_mul::VartimeMultiscalarMul;
|
|
||||||
pub use error::{Error, FieldError, GroupError};
|
pub use error::{Error, FieldError, GroupError};
|
||||||
|
pub use identifier::Identifier;
|
||||||
|
use scalar_mul::VartimeMultiscalarMul;
|
||||||
|
// Re-export serde
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub use serde;
|
||||||
pub use signature::Signature;
|
pub use signature::Signature;
|
||||||
pub use signing_key::SigningKey;
|
pub use signing_key::SigningKey;
|
||||||
|
pub use traits::{Ciphersuite, Element, Field, Group, Scalar};
|
||||||
pub use verifying_key::VerifyingKey;
|
pub use verifying_key::VerifyingKey;
|
||||||
|
|
||||||
/// A prime order finite field GF(q) over which all scalar values for our prime order group can be
|
|
||||||
/// multiplied are defined.
|
|
||||||
///
|
|
||||||
/// This trait does not have to be implemented for a finite field scalar itself, it can be a
|
|
||||||
/// pass-through, implemented for a type just for the ciphersuite, and calls through to another
|
|
||||||
/// implementation underneath, so that this trait does not have to be implemented for types you
|
|
||||||
/// don't own.
|
|
||||||
pub trait Field: Copy + Clone {
|
|
||||||
/// An element of the scalar field GF(p).
|
|
||||||
/// The Eq/PartialEq implementation MUST be constant-time.
|
|
||||||
type Scalar: Add<Output = Self::Scalar>
|
|
||||||
+ Copy
|
|
||||||
+ Clone
|
|
||||||
+ Eq
|
|
||||||
+ Mul<Output = Self::Scalar>
|
|
||||||
+ PartialEq
|
|
||||||
+ Sub<Output = Self::Scalar>;
|
|
||||||
|
|
||||||
/// A unique byte array buf of fixed length N.
|
|
||||||
type Serialization: AsRef<[u8]> + Debug + TryFrom<Vec<u8>>;
|
|
||||||
|
|
||||||
/// Returns the zero element of the field, the additive identity.
|
|
||||||
fn zero() -> Self::Scalar;
|
|
||||||
|
|
||||||
/// Returns the one element of the field, the multiplicative identity.
|
|
||||||
fn one() -> Self::Scalar;
|
|
||||||
|
|
||||||
/// 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, FieldError>;
|
|
||||||
|
|
||||||
/// Generate a random scalar from the entire space [0, l-1]
|
|
||||||
///
|
|
||||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.3>
|
|
||||||
fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
|
|
||||||
|
|
||||||
/// A member function of a [`Field`] that maps a [`Scalar`] to a unique byte array buf of
|
|
||||||
/// fixed length Ne.
|
|
||||||
///
|
|
||||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.8>
|
|
||||||
fn serialize(scalar: &Self::Scalar) -> Self::Serialization;
|
|
||||||
|
|
||||||
/// A member function of a [`Field`] that maps a [`Scalar`] to a unique byte array buf of
|
|
||||||
/// fixed length Ne, in little-endian order.
|
|
||||||
///
|
|
||||||
/// This is used internally.
|
|
||||||
fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization;
|
|
||||||
|
|
||||||
/// A member function of a [`Field`] that attempts to map a byte array `buf` to a [`Scalar`].
|
|
||||||
///
|
|
||||||
/// Fails if the input is not a valid byte representation of an [`Scalar`] of the
|
|
||||||
/// [`Field`]. This function can raise an [`Error`] if deserialization fails.
|
|
||||||
///
|
|
||||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.9>
|
|
||||||
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An element of the [`Ciphersuite`] `C`'s [`Group`]'s scalar [`Field`].
|
|
||||||
pub type Scalar<C> = <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar;
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
|
|
||||||
/// Helper struct to serialize a Scalar.
|
|
||||||
pub(crate) struct ScalarSerialization<C: Ciphersuite>(
|
|
||||||
pub <<<C as Ciphersuite>::Group as Group>::Field as Field>::Serialization,
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<C> serde::Serialize for ScalarSerialization<C>
|
|
||||||
where
|
|
||||||
C: Ciphersuite,
|
|
||||||
{
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
serdect::array::serialize_hex_lower_or_bin(&self.0.as_ref(), serializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'de, C> serde::Deserialize<'de> for ScalarSerialization<C>
|
|
||||||
where
|
|
||||||
C: Ciphersuite,
|
|
||||||
{
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
// Get size from the size of the zero scalar
|
|
||||||
let zero = <<C::Group as Group>::Field as Field>::zero();
|
|
||||||
let len = <<C::Group as Group>::Field as Field>::serialize(&zero)
|
|
||||||
.as_ref()
|
|
||||||
.len();
|
|
||||||
|
|
||||||
let mut bytes = vec![0u8; len];
|
|
||||||
serdect::array::deserialize_hex_or_bin(&mut bytes[..], deserializer)?;
|
|
||||||
let array = bytes
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| serde::de::Error::custom("invalid byte length"))?;
|
|
||||||
Ok(Self(array))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A prime-order group (or subgroup) that provides everything we need to create and verify Schnorr
|
|
||||||
/// signatures.
|
|
||||||
///
|
|
||||||
/// This trait does not have to be implemented for the curve/element/point itself, it can be a
|
|
||||||
/// pass-through, implemented for a type just for the ciphersuite, and calls through to another
|
|
||||||
/// implementation underneath, so that this trait does not have to be implemented for types you
|
|
||||||
/// don't own.
|
|
||||||
pub trait Group: Copy + Clone + PartialEq {
|
|
||||||
/// A prime order finite field GF(q) over which all scalar values for our prime order group can
|
|
||||||
/// be multiplied are defined.
|
|
||||||
type Field: Field;
|
|
||||||
|
|
||||||
/// An element of our group that we will be computing over.
|
|
||||||
type Element: Add<Output = Self::Element>
|
|
||||||
+ Copy
|
|
||||||
+ Clone
|
|
||||||
+ Eq
|
|
||||||
+ Mul<<Self::Field as Field>::Scalar, Output = Self::Element>
|
|
||||||
+ PartialEq
|
|
||||||
+ Sub<Output = Self::Element>;
|
|
||||||
|
|
||||||
/// A unique byte array buf of fixed length N.
|
|
||||||
///
|
|
||||||
/// Little-endian!
|
|
||||||
type Serialization: AsRef<[u8]> + Debug + TryFrom<Vec<u8>>;
|
|
||||||
|
|
||||||
/// The order of the the quotient group when the prime order subgroup divides the order of the
|
|
||||||
/// full curve group.
|
|
||||||
///
|
|
||||||
/// If using a prime order elliptic curve, the cofactor should be 1 in the scalar field.
|
|
||||||
fn cofactor() -> <Self::Field as Field>::Scalar;
|
|
||||||
|
|
||||||
/// Additive [identity] of the prime order group.
|
|
||||||
///
|
|
||||||
/// [identity]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.2
|
|
||||||
fn identity() -> Self::Element;
|
|
||||||
|
|
||||||
/// The fixed generator element of the prime order group.
|
|
||||||
///
|
|
||||||
/// The 'base' of ['ScalarBaseMult()'] from the spec.
|
|
||||||
///
|
|
||||||
/// [`ScalarBaseMult()`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.5
|
|
||||||
fn generator() -> Self::Element;
|
|
||||||
|
|
||||||
/// A member function of a group _G_ that maps an [`Element`] to a unique byte array buf of
|
|
||||||
/// fixed length Ne.
|
|
||||||
///
|
|
||||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.6>
|
|
||||||
fn serialize(element: &Self::Element) -> Self::Serialization;
|
|
||||||
|
|
||||||
/// A member function of a [`Group`] that attempts to map a byte array `buf` to an [`Element`].
|
|
||||||
///
|
|
||||||
/// Fails if the input is not a valid byte representation of an [`Element`] of the
|
|
||||||
/// [`Group`]. This function can raise an [`Error`] if deserialization fails or if the
|
|
||||||
/// resulting [`Element`] is the identity element of the group
|
|
||||||
///
|
|
||||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.7>
|
|
||||||
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An element of the [`Ciphersuite`] `C`'s [`Group`].
|
|
||||||
pub type Element<C> = <<C as Ciphersuite>::Group as Group>::Element;
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
pub(crate) struct ElementSerialization<C: Ciphersuite>(
|
|
||||||
<<C as Ciphersuite>::Group as Group>::Serialization,
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<C> serde::Serialize for ElementSerialization<C>
|
|
||||||
where
|
|
||||||
C: Ciphersuite,
|
|
||||||
{
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
serdect::array::serialize_hex_lower_or_bin(&self.0.as_ref(), serializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'de, C> serde::Deserialize<'de> for ElementSerialization<C>
|
|
||||||
where
|
|
||||||
C: Ciphersuite,
|
|
||||||
{
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
// Get size from the size of the generator
|
|
||||||
let generator = <C::Group>::generator();
|
|
||||||
let len = <C::Group>::serialize(&generator).as_ref().len();
|
|
||||||
|
|
||||||
let mut bytes = vec![0u8; len];
|
|
||||||
serdect::array::deserialize_hex_or_bin(&mut bytes[..], deserializer)?;
|
|
||||||
let array = bytes
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| serde::de::Error::custom("invalid byte length"))?;
|
|
||||||
Ok(Self(array))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [FROST ciphersuite] specifies the underlying prime-order group details and cryptographic hash
|
|
||||||
/// function.
|
|
||||||
///
|
|
||||||
/// [FROST ciphersuite]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-ciphersuites
|
|
||||||
pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
|
|
||||||
/// The ciphersuite ID string. It should be equal to the contextString in
|
|
||||||
/// the spec. For new ciphersuites, this should be a string that identifies
|
|
||||||
/// the ciphersuite; it's recommended to use a similar format to the
|
|
||||||
/// ciphersuites in the FROST spec, e.g. "FROST-RISTRETTO255-SHA512-v1".
|
|
||||||
const ID: &'static str;
|
|
||||||
|
|
||||||
/// The prime order group (or subgroup) that this ciphersuite operates over.
|
|
||||||
type Group: Group;
|
|
||||||
|
|
||||||
/// A unique byte array of fixed length.
|
|
||||||
type HashOutput: AsRef<[u8]>;
|
|
||||||
|
|
||||||
/// A unique byte array of fixed length that is the `Group::ElementSerialization` +
|
|
||||||
/// `Group::ScalarSerialization`
|
|
||||||
type SignatureSerialization: AsRef<[u8]> + TryFrom<Vec<u8>>;
|
|
||||||
|
|
||||||
/// [H1] for a FROST ciphersuite.
|
|
||||||
///
|
|
||||||
/// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field.
|
|
||||||
///
|
|
||||||
/// [H1]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function
|
|
||||||
fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar;
|
|
||||||
|
|
||||||
/// [H2] for a FROST ciphersuite.
|
|
||||||
///
|
|
||||||
/// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field.
|
|
||||||
///
|
|
||||||
/// [H2]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function
|
|
||||||
fn H2(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar;
|
|
||||||
|
|
||||||
/// [H3] for a FROST ciphersuite.
|
|
||||||
///
|
|
||||||
/// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field.
|
|
||||||
///
|
|
||||||
/// [H3]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function
|
|
||||||
fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar;
|
|
||||||
|
|
||||||
/// [H4] for a FROST ciphersuite.
|
|
||||||
///
|
|
||||||
/// Usually an an alias for the ciphersuite hash function _H_ with domain separation applied.
|
|
||||||
///
|
|
||||||
/// [H4]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function
|
|
||||||
fn H4(m: &[u8]) -> Self::HashOutput;
|
|
||||||
|
|
||||||
/// [H5] for a FROST ciphersuite.
|
|
||||||
///
|
|
||||||
/// Usually an an alias for the ciphersuite hash function _H_ with domain separation applied.
|
|
||||||
///
|
|
||||||
/// [H5]: https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#cryptographic-hash
|
|
||||||
fn H5(m: &[u8]) -> Self::HashOutput;
|
|
||||||
|
|
||||||
/// Hash function for a FROST ciphersuite, used for the DKG.
|
|
||||||
///
|
|
||||||
/// The DKG it not part of the specification, thus this is optional.
|
|
||||||
/// It can return None if DKG is not supported by the Ciphersuite. This is
|
|
||||||
/// the default implementation.
|
|
||||||
///
|
|
||||||
/// Maps arbitrary inputs to non-zero `Self::Scalar` elements of the prime-order group scalar field.
|
|
||||||
fn HDKG(_m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hash function for a FROST ciphersuite, used for deriving identifiers from strings.
|
|
||||||
///
|
|
||||||
/// This feature is not part of the specification and is just a convenient
|
|
||||||
/// way of creating identifiers. Therefore it can return None if this is not supported by the
|
|
||||||
/// Ciphersuite. This is the default implementation.
|
|
||||||
///
|
|
||||||
/// Maps arbitrary inputs to non-zero `Self::Scalar` elements of the prime-order group scalar field.
|
|
||||||
fn HID(_m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify a signature for this ciphersuite. The default implementation uses the "cofactored"
|
|
||||||
/// equation (it multiplies by the cofactor returned by [`Group::cofactor()`]).
|
|
||||||
///
|
|
||||||
/// # Cryptographic Safety
|
|
||||||
///
|
|
||||||
/// You may override this to provide a tailored implementation, but if the ciphersuite defines it,
|
|
||||||
/// it must also multiply by the cofactor to comply with the RFC. Note that batch verification
|
|
||||||
/// (see [`crate::batch::Verifier`]) also uses the default implementation regardless whether a
|
|
||||||
/// tailored implementation was provided.
|
|
||||||
fn verify_signature(
|
|
||||||
msg: &[u8],
|
|
||||||
signature: &Signature<Self>,
|
|
||||||
public_key: &VerifyingKey<Self>,
|
|
||||||
) -> Result<(), Error<Self>> {
|
|
||||||
let c = crate::challenge::<Self>(&signature.R, public_key, msg);
|
|
||||||
|
|
||||||
public_key.verify_prehashed(c, signature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The short 4-byte ID. Derived as the CRC-32 of the UTF-8
|
|
||||||
// encoded ID in big endian format.
|
|
||||||
const fn short_id<C>() -> [u8; 4]
|
|
||||||
where
|
|
||||||
C: Ciphersuite,
|
|
||||||
{
|
|
||||||
const_crc32::crc32(C::ID.as_bytes()).to_be_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A type refinement for the scalar field element representing the per-message _[challenge]_.
|
/// A type refinement for the scalar field element representing the per-message _[challenge]_.
|
||||||
///
|
///
|
||||||
/// [challenge]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-signature-challenge-computa
|
/// [challenge]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-signature-challenge-computa
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Challenge<C: Ciphersuite>(pub(crate) <<C::Group as Group>::Field as Field>::Scalar);
|
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
|
||||||
|
pub(crate) struct Challenge<C: Ciphersuite>(
|
||||||
|
pub(crate) <<C::Group as Group>::Field as Field>::Scalar,
|
||||||
|
);
|
||||||
|
|
||||||
impl<C> Challenge<C>
|
impl<C> Challenge<C>
|
||||||
where
|
where
|
||||||
|
@ -447,17 +140,17 @@ struct Header<C: Ciphersuite> {
|
||||||
/// Format version
|
/// Format version
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
serde(deserialize_with = "crate::version_deserialize::<_>")
|
serde(deserialize_with = "crate::serialization::version_deserialize::<_>")
|
||||||
)]
|
)]
|
||||||
version: u8,
|
version: u8,
|
||||||
/// Ciphersuite ID
|
/// Ciphersuite ID
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
serde(serialize_with = "crate::ciphersuite_serialize::<_, C>")
|
serde(serialize_with = "crate::serialization::ciphersuite_serialize::<_, C>")
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
|
serde(deserialize_with = "crate::serialization::ciphersuite_deserialize::<_, C>")
|
||||||
)]
|
)]
|
||||||
ciphersuite: (),
|
ciphersuite: (),
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
|
@ -477,99 +170,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize a placeholder ciphersuite field with the ciphersuite ID string.
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
pub(crate) fn ciphersuite_serialize<S, C>(_: &(), s: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
C: Ciphersuite,
|
|
||||||
{
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
if s.is_human_readable() {
|
|
||||||
C::ID.serialize(s)
|
|
||||||
} else {
|
|
||||||
serde::Serialize::serialize(&short_id::<C>(), s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize a placeholder ciphersuite field, checking if it's the ciphersuite ID string.
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
pub(crate) fn ciphersuite_deserialize<'de, D, C>(deserializer: D) -> Result<(), D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
C: Ciphersuite,
|
|
||||||
{
|
|
||||||
if deserializer.is_human_readable() {
|
|
||||||
let s: &str = serde::de::Deserialize::deserialize(deserializer)?;
|
|
||||||
if s != C::ID {
|
|
||||||
Err(serde::de::Error::custom("wrong ciphersuite"))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let buffer: [u8; 4] = serde::de::Deserialize::deserialize(deserializer)?;
|
|
||||||
if buffer != short_id::<C>() {
|
|
||||||
Err(serde::de::Error::custom("wrong ciphersuite"))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize a version. For now, since there is a single version 0,
|
|
||||||
/// simply validate if it's 0.
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
pub(crate) fn version_deserialize<'de, D>(deserializer: D) -> Result<u8, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let version: u8 = serde::de::Deserialize::deserialize(deserializer)?;
|
|
||||||
if version != 0 {
|
|
||||||
Err(serde::de::Error::custom(
|
|
||||||
"wrong format version, only 0 supported",
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default byte-oriented serialization for structs that need to be communicated.
|
|
||||||
//
|
|
||||||
// Note that we still manually implement these methods in each applicable type,
|
|
||||||
// instead of making these traits `pub` and asking users to import the traits.
|
|
||||||
// The reason is that ciphersuite traits would need to re-export these traits,
|
|
||||||
// parametrized with the ciphersuite, but trait aliases are not currently
|
|
||||||
// supported: <https://github.com/rust-lang/rust/issues/41517>
|
|
||||||
|
|
||||||
#[cfg(feature = "serialization")]
|
|
||||||
trait Serialize<C: Ciphersuite> {
|
|
||||||
/// Serialize the struct into a Vec.
|
|
||||||
fn serialize(&self) -> Result<Vec<u8>, Error<C>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialization")]
|
|
||||||
trait Deserialize<C: Ciphersuite> {
|
|
||||||
/// Deserialize the struct from a slice of bytes.
|
|
||||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>>
|
|
||||||
where
|
|
||||||
Self: std::marker::Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialization")]
|
|
||||||
impl<T: serde::Serialize, C: Ciphersuite> Serialize<C> for T {
|
|
||||||
fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
|
|
||||||
postcard::to_stdvec(self).map_err(|_| Error::SerializationError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialization")]
|
|
||||||
impl<T: for<'de> serde::Deserialize<'de>, C: Ciphersuite> Deserialize<C> for T {
|
|
||||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
|
|
||||||
postcard::from_bytes(bytes).map_err(|_| Error::DeserializationError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The binding factor, also known as _rho_ (ρ)
|
/// The binding factor, also known as _rho_ (ρ)
|
||||||
///
|
///
|
||||||
/// Ensures each signature share is strongly bound to a signing set, specific set
|
/// Ensures each signature share is strongly bound to a signing set, specific set
|
||||||
|
@ -577,7 +177,9 @@ impl<T: for<'de> serde::Deserialize<'de>, C: Ciphersuite> Deserialize<C> for T {
|
||||||
///
|
///
|
||||||
/// <https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md>
|
/// <https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md>
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct BindingFactor<C: Ciphersuite>(Scalar<C>);
|
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
|
||||||
|
pub(crate) struct BindingFactor<C: Ciphersuite>(Scalar<C>);
|
||||||
|
|
||||||
impl<C> BindingFactor<C>
|
impl<C> BindingFactor<C>
|
||||||
where
|
where
|
||||||
|
@ -611,7 +213,9 @@ where
|
||||||
|
|
||||||
/// A list of binding factors and their associated identifiers.
|
/// A list of binding factors and their associated identifiers.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BindingFactorList<C: Ciphersuite>(BTreeMap<Identifier<C>, BindingFactor<C>>);
|
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
|
||||||
|
pub(crate) struct BindingFactorList<C: Ciphersuite>(BTreeMap<Identifier<C>, BindingFactor<C>>);
|
||||||
|
|
||||||
impl<C> BindingFactorList<C>
|
impl<C> BindingFactorList<C>
|
||||||
where
|
where
|
||||||
|
@ -847,19 +451,21 @@ where
|
||||||
{
|
{
|
||||||
/// Serialize the struct into a Vec.
|
/// Serialize the struct into a Vec.
|
||||||
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
|
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
|
||||||
Serialize::serialize(&self)
|
serialization::Serialize::serialize(&self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialize the struct from a slice of bytes.
|
/// Deserialize the struct from a slice of bytes.
|
||||||
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
|
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
|
||||||
Deserialize::deserialize(bytes)
|
serialization::Deserialize::deserialize(bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The product of all signers' individual commitments, published as part of the
|
/// The product of all signers' individual commitments, published as part of the
|
||||||
/// final signature.
|
/// final signature.
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct GroupCommitment<C: Ciphersuite>(pub(crate) Element<C>);
|
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
|
||||||
|
pub(crate) struct GroupCommitment<C: Ciphersuite>(pub(crate) Element<C>);
|
||||||
|
|
||||||
impl<C> GroupCommitment<C>
|
impl<C> GroupCommitment<C>
|
||||||
where
|
where
|
||||||
|
|
|
@ -13,10 +13,13 @@ use rand_core::{CryptoRng, RngCore};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use crate as frost;
|
use crate as frost;
|
||||||
use crate::{Ciphersuite, Deserialize, Element, Error, Field, Group, Header, Scalar, Serialize};
|
use crate::{
|
||||||
|
serialization::{Deserialize, Serialize},
|
||||||
|
Ciphersuite, Element, Error, Field, Group, Header, Scalar,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::ElementSerialization;
|
use crate::serialization::ElementSerialization;
|
||||||
|
|
||||||
use super::{keys::SigningShare, Identifier};
|
use super::{keys::SigningShare, Identifier};
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::ScalarSerialization;
|
use crate::serialization::ScalarSerialization;
|
||||||
|
|
||||||
// Used to help encoding a SignatureShare. Since it has a Scalar<C> it can't
|
// Used to help encoding a SignatureShare. Since it has a Scalar<C> it can't
|
||||||
// be directly encoded with serde, so we use this struct to wrap the scalar.
|
// be directly encoded with serde, so we use this struct to wrap the scalar.
|
||||||
|
|
|
@ -0,0 +1,190 @@
|
||||||
|
//! Serialization support.
|
||||||
|
|
||||||
|
use crate::{Ciphersuite, Error, Field, Group};
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
|
||||||
|
/// Helper struct to serialize a Scalar.
|
||||||
|
pub(crate) struct ScalarSerialization<C: Ciphersuite>(
|
||||||
|
pub <<<C as Ciphersuite>::Group as Group>::Field as Field>::Serialization,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<C> serde::Serialize for ScalarSerialization<C>
|
||||||
|
where
|
||||||
|
C: Ciphersuite,
|
||||||
|
{
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serdect::array::serialize_hex_lower_or_bin(&self.0.as_ref(), serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de, C> serde::Deserialize<'de> for ScalarSerialization<C>
|
||||||
|
where
|
||||||
|
C: Ciphersuite,
|
||||||
|
{
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
// Get size from the size of the zero scalar
|
||||||
|
let zero = <<C::Group as Group>::Field as Field>::zero();
|
||||||
|
let len = <<C::Group as Group>::Field as Field>::serialize(&zero)
|
||||||
|
.as_ref()
|
||||||
|
.len();
|
||||||
|
|
||||||
|
let mut bytes = vec![0u8; len];
|
||||||
|
serdect::array::deserialize_hex_or_bin(&mut bytes[..], deserializer)?;
|
||||||
|
let array = bytes
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| serde::de::Error::custom("invalid byte length"))?;
|
||||||
|
Ok(Self(array))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub(crate) struct ElementSerialization<C: Ciphersuite>(
|
||||||
|
pub(crate) <<C as Ciphersuite>::Group as Group>::Serialization,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<C> serde::Serialize for ElementSerialization<C>
|
||||||
|
where
|
||||||
|
C: Ciphersuite,
|
||||||
|
{
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serdect::array::serialize_hex_lower_or_bin(&self.0.as_ref(), serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de, C> serde::Deserialize<'de> for ElementSerialization<C>
|
||||||
|
where
|
||||||
|
C: Ciphersuite,
|
||||||
|
{
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
// Get size from the size of the generator
|
||||||
|
let generator = <C::Group>::generator();
|
||||||
|
let len = <C::Group>::serialize(&generator).as_ref().len();
|
||||||
|
|
||||||
|
let mut bytes = vec![0u8; len];
|
||||||
|
serdect::array::deserialize_hex_or_bin(&mut bytes[..], deserializer)?;
|
||||||
|
let array = bytes
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| serde::de::Error::custom("invalid byte length"))?;
|
||||||
|
Ok(Self(array))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The short 4-byte ID. Derived as the CRC-32 of the UTF-8
|
||||||
|
// encoded ID in big endian format.
|
||||||
|
const fn short_id<C>() -> [u8; 4]
|
||||||
|
where
|
||||||
|
C: Ciphersuite,
|
||||||
|
{
|
||||||
|
const_crc32::crc32(C::ID.as_bytes()).to_be_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize a placeholder ciphersuite field with the ciphersuite ID string.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub(crate) fn ciphersuite_serialize<S, C>(_: &(), s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
C: Ciphersuite,
|
||||||
|
{
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
if s.is_human_readable() {
|
||||||
|
C::ID.serialize(s)
|
||||||
|
} else {
|
||||||
|
serde::Serialize::serialize(&short_id::<C>(), s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize a placeholder ciphersuite field, checking if it's the ciphersuite ID string.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub(crate) fn ciphersuite_deserialize<'de, D, C>(deserializer: D) -> Result<(), D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
C: Ciphersuite,
|
||||||
|
{
|
||||||
|
if deserializer.is_human_readable() {
|
||||||
|
let s: &str = serde::de::Deserialize::deserialize(deserializer)?;
|
||||||
|
if s != C::ID {
|
||||||
|
Err(serde::de::Error::custom("wrong ciphersuite"))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let buffer: [u8; 4] = serde::de::Deserialize::deserialize(deserializer)?;
|
||||||
|
if buffer != short_id::<C>() {
|
||||||
|
Err(serde::de::Error::custom("wrong ciphersuite"))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize a version. For now, since there is a single version 0,
|
||||||
|
/// simply validate if it's 0.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub(crate) fn version_deserialize<'de, D>(deserializer: D) -> Result<u8, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let version: u8 = serde::de::Deserialize::deserialize(deserializer)?;
|
||||||
|
if version != 0 {
|
||||||
|
Err(serde::de::Error::custom(
|
||||||
|
"wrong format version, only 0 supported",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default byte-oriented serialization for structs that need to be communicated.
|
||||||
|
//
|
||||||
|
// Note that we still manually implement these methods in each applicable type,
|
||||||
|
// instead of making these traits `pub` and asking users to import the traits.
|
||||||
|
// The reason is that ciphersuite traits would need to re-export these traits,
|
||||||
|
// parametrized with the ciphersuite, but trait aliases are not currently
|
||||||
|
// supported: <https://github.com/rust-lang/rust/issues/41517>
|
||||||
|
|
||||||
|
#[cfg(feature = "serialization")]
|
||||||
|
pub(crate) trait Serialize<C: Ciphersuite> {
|
||||||
|
/// Serialize the struct into a Vec.
|
||||||
|
fn serialize(&self) -> Result<Vec<u8>, Error<C>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialization")]
|
||||||
|
pub(crate) trait Deserialize<C: Ciphersuite> {
|
||||||
|
/// Deserialize the struct from a slice of bytes.
|
||||||
|
fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>>
|
||||||
|
where
|
||||||
|
Self: std::marker::Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialization")]
|
||||||
|
impl<T: serde::Serialize, C: Ciphersuite> Serialize<C> for T {
|
||||||
|
fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
|
||||||
|
postcard::to_stdvec(self).map_err(|_| Error::SerializationError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialization")]
|
||||||
|
impl<T: for<'de> serde::Deserialize<'de>, C: Ciphersuite> Deserialize<C> for T {
|
||||||
|
fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
|
||||||
|
postcard::from_bytes(bytes).map_err(|_| Error::DeserializationError)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,231 @@
|
||||||
|
//! Traits used to abstract Ciphersuites.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
ops::{Add, Mul, Sub},
|
||||||
|
};
|
||||||
|
|
||||||
|
use rand_core::{CryptoRng, RngCore};
|
||||||
|
|
||||||
|
use crate::{Error, FieldError, GroupError, Signature, VerifyingKey};
|
||||||
|
|
||||||
|
/// A prime order finite field GF(q) over which all scalar values for our prime order group can be
|
||||||
|
/// multiplied are defined.
|
||||||
|
///
|
||||||
|
/// This trait does not have to be implemented for a finite field scalar itself, it can be a
|
||||||
|
/// pass-through, implemented for a type just for the ciphersuite, and calls through to another
|
||||||
|
/// implementation underneath, so that this trait does not have to be implemented for types you
|
||||||
|
/// don't own.
|
||||||
|
pub trait Field: Copy + Clone {
|
||||||
|
/// An element of the scalar field GF(p).
|
||||||
|
/// The Eq/PartialEq implementation MUST be constant-time.
|
||||||
|
type Scalar: Add<Output = Self::Scalar>
|
||||||
|
+ Copy
|
||||||
|
+ Clone
|
||||||
|
+ Eq
|
||||||
|
+ Mul<Output = Self::Scalar>
|
||||||
|
+ PartialEq
|
||||||
|
+ Sub<Output = Self::Scalar>;
|
||||||
|
|
||||||
|
/// A unique byte array buf of fixed length N.
|
||||||
|
type Serialization: AsRef<[u8]> + Debug + TryFrom<Vec<u8>>;
|
||||||
|
|
||||||
|
/// Returns the zero element of the field, the additive identity.
|
||||||
|
fn zero() -> Self::Scalar;
|
||||||
|
|
||||||
|
/// Returns the one element of the field, the multiplicative identity.
|
||||||
|
fn one() -> Self::Scalar;
|
||||||
|
|
||||||
|
/// 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, FieldError>;
|
||||||
|
|
||||||
|
/// Generate a random scalar from the entire space [0, l-1]
|
||||||
|
///
|
||||||
|
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.3>
|
||||||
|
fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
|
||||||
|
|
||||||
|
/// A member function of a [`Field`] that maps a [`Scalar`] to a unique byte array buf of
|
||||||
|
/// fixed length Ne.
|
||||||
|
///
|
||||||
|
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.8>
|
||||||
|
fn serialize(scalar: &Self::Scalar) -> Self::Serialization;
|
||||||
|
|
||||||
|
/// A member function of a [`Field`] that maps a [`Scalar`] to a unique byte array buf of
|
||||||
|
/// fixed length Ne, in little-endian order.
|
||||||
|
///
|
||||||
|
/// This is used internally.
|
||||||
|
fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization;
|
||||||
|
|
||||||
|
/// A member function of a [`Field`] that attempts to map a byte array `buf` to a [`Scalar`].
|
||||||
|
///
|
||||||
|
/// Fails if the input is not a valid byte representation of an [`Scalar`] of the
|
||||||
|
/// [`Field`]. This function can raise an [`Error`] if deserialization fails.
|
||||||
|
///
|
||||||
|
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.9>
|
||||||
|
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An element of the [`Ciphersuite`] `C`'s [`Group`]'s scalar [`Field`].
|
||||||
|
pub type Scalar<C> = <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar;
|
||||||
|
|
||||||
|
/// A prime-order group (or subgroup) that provides everything we need to create and verify Schnorr
|
||||||
|
/// signatures.
|
||||||
|
///
|
||||||
|
/// This trait does not have to be implemented for the curve/element/point itself, it can be a
|
||||||
|
/// pass-through, implemented for a type just for the ciphersuite, and calls through to another
|
||||||
|
/// implementation underneath, so that this trait does not have to be implemented for types you
|
||||||
|
/// don't own.
|
||||||
|
pub trait Group: Copy + Clone + PartialEq {
|
||||||
|
/// A prime order finite field GF(q) over which all scalar values for our prime order group can
|
||||||
|
/// be multiplied are defined.
|
||||||
|
type Field: Field;
|
||||||
|
|
||||||
|
/// An element of our group that we will be computing over.
|
||||||
|
type Element: Add<Output = Self::Element>
|
||||||
|
+ Copy
|
||||||
|
+ Clone
|
||||||
|
+ Eq
|
||||||
|
+ Mul<<Self::Field as Field>::Scalar, Output = Self::Element>
|
||||||
|
+ PartialEq
|
||||||
|
+ Sub<Output = Self::Element>;
|
||||||
|
|
||||||
|
/// A unique byte array buf of fixed length N.
|
||||||
|
///
|
||||||
|
/// Little-endian!
|
||||||
|
type Serialization: AsRef<[u8]> + Debug + TryFrom<Vec<u8>>;
|
||||||
|
|
||||||
|
/// The order of the the quotient group when the prime order subgroup divides the order of the
|
||||||
|
/// full curve group.
|
||||||
|
///
|
||||||
|
/// If using a prime order elliptic curve, the cofactor should be 1 in the scalar field.
|
||||||
|
fn cofactor() -> <Self::Field as Field>::Scalar;
|
||||||
|
|
||||||
|
/// Additive [identity] of the prime order group.
|
||||||
|
///
|
||||||
|
/// [identity]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.2
|
||||||
|
fn identity() -> Self::Element;
|
||||||
|
|
||||||
|
/// The fixed generator element of the prime order group.
|
||||||
|
///
|
||||||
|
/// The 'base' of ['ScalarBaseMult()'] from the spec.
|
||||||
|
///
|
||||||
|
/// [`ScalarBaseMult()`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.5
|
||||||
|
fn generator() -> Self::Element;
|
||||||
|
|
||||||
|
/// A member function of a group _G_ that maps an [`Element`] to a unique byte array buf of
|
||||||
|
/// fixed length Ne.
|
||||||
|
///
|
||||||
|
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.6>
|
||||||
|
fn serialize(element: &Self::Element) -> Self::Serialization;
|
||||||
|
|
||||||
|
/// A member function of a [`Group`] that attempts to map a byte array `buf` to an [`Element`].
|
||||||
|
///
|
||||||
|
/// Fails if the input is not a valid byte representation of an [`Element`] of the
|
||||||
|
/// [`Group`]. This function can raise an [`Error`] if deserialization fails or if the
|
||||||
|
/// resulting [`Element`] is the identity element of the group
|
||||||
|
///
|
||||||
|
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.7>
|
||||||
|
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An element of the [`Ciphersuite`] `C`'s [`Group`].
|
||||||
|
pub type Element<C> = <<C as Ciphersuite>::Group as Group>::Element;
|
||||||
|
|
||||||
|
/// A [FROST ciphersuite] specifies the underlying prime-order group details and cryptographic hash
|
||||||
|
/// function.
|
||||||
|
///
|
||||||
|
/// [FROST ciphersuite]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-ciphersuites
|
||||||
|
pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
|
||||||
|
/// The ciphersuite ID string. It should be equal to the contextString in
|
||||||
|
/// the spec. For new ciphersuites, this should be a string that identifies
|
||||||
|
/// the ciphersuite; it's recommended to use a similar format to the
|
||||||
|
/// ciphersuites in the FROST spec, e.g. "FROST-RISTRETTO255-SHA512-v1".
|
||||||
|
const ID: &'static str;
|
||||||
|
|
||||||
|
/// The prime order group (or subgroup) that this ciphersuite operates over.
|
||||||
|
type Group: Group;
|
||||||
|
|
||||||
|
/// A unique byte array of fixed length.
|
||||||
|
type HashOutput: AsRef<[u8]>;
|
||||||
|
|
||||||
|
/// A unique byte array of fixed length that is the `Group::ElementSerialization` +
|
||||||
|
/// `Group::ScalarSerialization`
|
||||||
|
type SignatureSerialization: AsRef<[u8]> + TryFrom<Vec<u8>>;
|
||||||
|
|
||||||
|
/// [H1] for a FROST ciphersuite.
|
||||||
|
///
|
||||||
|
/// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field.
|
||||||
|
///
|
||||||
|
/// [H1]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function
|
||||||
|
fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar;
|
||||||
|
|
||||||
|
/// [H2] for a FROST ciphersuite.
|
||||||
|
///
|
||||||
|
/// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field.
|
||||||
|
///
|
||||||
|
/// [H2]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function
|
||||||
|
fn H2(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar;
|
||||||
|
|
||||||
|
/// [H3] for a FROST ciphersuite.
|
||||||
|
///
|
||||||
|
/// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field.
|
||||||
|
///
|
||||||
|
/// [H3]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function
|
||||||
|
fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar;
|
||||||
|
|
||||||
|
/// [H4] for a FROST ciphersuite.
|
||||||
|
///
|
||||||
|
/// Usually an an alias for the ciphersuite hash function _H_ with domain separation applied.
|
||||||
|
///
|
||||||
|
/// [H4]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function
|
||||||
|
fn H4(m: &[u8]) -> Self::HashOutput;
|
||||||
|
|
||||||
|
/// [H5] for a FROST ciphersuite.
|
||||||
|
///
|
||||||
|
/// Usually an an alias for the ciphersuite hash function _H_ with domain separation applied.
|
||||||
|
///
|
||||||
|
/// [H5]: https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#cryptographic-hash
|
||||||
|
fn H5(m: &[u8]) -> Self::HashOutput;
|
||||||
|
|
||||||
|
/// Hash function for a FROST ciphersuite, used for the DKG.
|
||||||
|
///
|
||||||
|
/// The DKG it not part of the specification, thus this is optional.
|
||||||
|
/// It can return None if DKG is not supported by the Ciphersuite. This is
|
||||||
|
/// the default implementation.
|
||||||
|
///
|
||||||
|
/// Maps arbitrary inputs to non-zero `Self::Scalar` elements of the prime-order group scalar field.
|
||||||
|
fn HDKG(_m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hash function for a FROST ciphersuite, used for deriving identifiers from strings.
|
||||||
|
///
|
||||||
|
/// This feature is not part of the specification and is just a convenient
|
||||||
|
/// way of creating identifiers. Therefore it can return None if this is not supported by the
|
||||||
|
/// Ciphersuite. This is the default implementation.
|
||||||
|
///
|
||||||
|
/// Maps arbitrary inputs to non-zero `Self::Scalar` elements of the prime-order group scalar field.
|
||||||
|
fn HID(_m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify a signature for this ciphersuite. The default implementation uses the "cofactored"
|
||||||
|
/// equation (it multiplies by the cofactor returned by [`Group::cofactor()`]).
|
||||||
|
///
|
||||||
|
/// # Cryptographic Safety
|
||||||
|
///
|
||||||
|
/// You may override this to provide a tailored implementation, but if the ciphersuite defines it,
|
||||||
|
/// it must also multiply by the cofactor to comply with the RFC. Note that batch verification
|
||||||
|
/// (see [`crate::batch::Verifier`]) also uses the default implementation regardless whether a
|
||||||
|
/// tailored implementation was provided.
|
||||||
|
fn verify_signature(
|
||||||
|
msg: &[u8],
|
||||||
|
signature: &Signature<Self>,
|
||||||
|
public_key: &VerifyingKey<Self>,
|
||||||
|
) -> Result<(), Error<Self>> {
|
||||||
|
let c = crate::challenge::<Self>(&signature.R, public_key, msg);
|
||||||
|
|
||||||
|
public_key.verify_prehashed(c, signature)
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ use hex::FromHex;
|
||||||
use crate::{Challenge, Ciphersuite, Element, Error, Group, Signature};
|
use crate::{Challenge, Ciphersuite, Element, Error, Group, Signature};
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::ElementSerialization;
|
use crate::serialization::ElementSerialization;
|
||||||
|
|
||||||
/// A valid verifying key for Schnorr signatures over a FROST [`Ciphersuite::Group`].
|
/// A valid verifying key for Schnorr signatures over a FROST [`Ciphersuite::Group`].
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
|
|
@ -28,7 +28,7 @@ use frost_core::{
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use frost_core::serde;
|
use frost_core::serde;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use frost_core::ScalarSerialization;
|
use frost_core::serialization::ScalarSerialization;
|
||||||
|
|
||||||
// When pulled into `reddsa`, that has its own sibling `rand_core` import.
|
// When pulled into `reddsa`, that has its own sibling `rand_core` import.
|
||||||
// For the time being, we do not re-export this `rand_core`.
|
// For the time being, we do not re-export this `rand_core`.
|
||||||
|
|
Loading…
Reference in New Issue