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
|
||||
eliminating the `frost` module. You can adapt any calling code with e.g.
|
||||
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
|
||||
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
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::{
|
|||
use crate::{Ciphersuite, Error, Field, FieldError, Group, Scalar};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::ScalarSerialization;
|
||||
use crate::serialization::ScalarSerialization;
|
||||
|
||||
/// A FROST participant identifier.
|
||||
///
|
||||
|
|
|
@ -17,12 +17,13 @@ use rand_core::{CryptoRng, RngCore};
|
|||
use zeroize::{DefaultIsZeroes, Zeroize};
|
||||
|
||||
use crate::{
|
||||
Ciphersuite, Deserialize, Element, Error, Field, Group, Header, Identifier, Scalar, Serialize,
|
||||
SigningKey, VerifyingKey,
|
||||
serialization::{Deserialize, Serialize},
|
||||
Ciphersuite, Element, Error, Field, Group, Header, Identifier, Scalar, SigningKey,
|
||||
VerifyingKey,
|
||||
};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::{ElementSerialization, ScalarSerialization};
|
||||
use crate::serialization::{ElementSerialization, ScalarSerialization};
|
||||
|
||||
use super::compute_lagrange_coefficient;
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ pub mod round1 {
|
|||
use derive_getters::Getters;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::{Deserialize, Serialize};
|
||||
use crate::serialization::{Deserialize, Serialize};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -167,7 +167,7 @@ pub mod round2 {
|
|||
use derive_getters::Getters;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::{Deserialize, Serialize};
|
||||
use crate::serialization::{Deserialize, Serialize};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ use std::{
|
|||
default::Default,
|
||||
fmt::{self, Debug},
|
||||
marker::PhantomData,
|
||||
ops::{Add, Mul, Sub},
|
||||
};
|
||||
|
||||
use derive_getters::Getters;
|
||||
|
@ -24,10 +23,6 @@ use hex::FromHex;
|
|||
use rand_core::{CryptoRng, RngCore};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
// Re-export serde
|
||||
#[cfg(feature = "serde")]
|
||||
pub use serde;
|
||||
|
||||
pub mod batch;
|
||||
#[cfg(any(test, feature = "test-impl"))]
|
||||
pub mod benches;
|
||||
|
@ -37,339 +32,37 @@ pub mod keys;
|
|||
pub mod round1;
|
||||
pub mod round2;
|
||||
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 signing_key;
|
||||
#[cfg(any(test, feature = "test-impl"))]
|
||||
pub mod tests;
|
||||
mod traits;
|
||||
mod verifying_key;
|
||||
|
||||
pub use self::identifier::Identifier;
|
||||
use crate::scalar_mul::VartimeMultiscalarMul;
|
||||
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 signing_key::SigningKey;
|
||||
pub use traits::{Ciphersuite, Element, Field, Group, Scalar};
|
||||
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]_.
|
||||
///
|
||||
/// [challenge]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-signature-challenge-computa
|
||||
#[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>
|
||||
where
|
||||
|
@ -447,17 +140,17 @@ struct Header<C: Ciphersuite> {
|
|||
/// Format version
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(deserialize_with = "crate::version_deserialize::<_>")
|
||||
serde(deserialize_with = "crate::serialization::version_deserialize::<_>")
|
||||
)]
|
||||
version: u8,
|
||||
/// Ciphersuite ID
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(serialize_with = "crate::ciphersuite_serialize::<_, C>")
|
||||
serde(serialize_with = "crate::serialization::ciphersuite_serialize::<_, C>")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
|
||||
serde(deserialize_with = "crate::serialization::ciphersuite_deserialize::<_, C>")
|
||||
)]
|
||||
ciphersuite: (),
|
||||
#[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_ (ρ)
|
||||
///
|
||||
/// 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>
|
||||
#[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>
|
||||
where
|
||||
|
@ -611,7 +213,9 @@ where
|
|||
|
||||
/// A list of binding factors and their associated identifiers.
|
||||
#[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>
|
||||
where
|
||||
|
@ -847,19 +451,21 @@ where
|
|||
{
|
||||
/// Serialize the struct into a Vec.
|
||||
pub fn serialize(&self) -> Result<Vec<u8>, Error<C>> {
|
||||
Serialize::serialize(&self)
|
||||
serialization::Serialize::serialize(&self)
|
||||
}
|
||||
|
||||
/// Deserialize the struct from a slice of bytes.
|
||||
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
|
||||
/// final signature.
|
||||
#[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>
|
||||
where
|
||||
|
|
|
@ -13,10 +13,13 @@ use rand_core::{CryptoRng, RngCore};
|
|||
use zeroize::Zeroize;
|
||||
|
||||
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")]
|
||||
use crate::ElementSerialization;
|
||||
use crate::serialization::ElementSerialization;
|
||||
|
||||
use super::{keys::SigningShare, Identifier};
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
};
|
||||
|
||||
#[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
|
||||
// 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};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::ElementSerialization;
|
||||
use crate::serialization::ElementSerialization;
|
||||
|
||||
/// A valid verifying key for Schnorr signatures over a FROST [`Ciphersuite::Group`].
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -28,7 +28,7 @@ use frost_core::{
|
|||
#[cfg(feature = "serde")]
|
||||
use frost_core::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.
|
||||
// For the time being, we do not re-export this `rand_core`.
|
||||
|
|
Loading…
Reference in New Issue