Support serde (#398)
* Implement serde for network messages. * Make sure marker type implements serde. * add serde support to all required structs * use serdect * gate under serde feature * ci: add build with default features job * add serde tests and required changes/fixes * add support for encoding ciphersuite ID --------- Co-authored-by: David Craven <david@craven.ch>
This commit is contained in:
parent
8b09d9d698
commit
47121537e8
|
@ -10,14 +10,26 @@ on:
|
|||
|
||||
jobs:
|
||||
|
||||
build_default:
|
||||
name: build with default features
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.5.3
|
||||
- uses: actions-rs/toolchain@v1.0.7
|
||||
with:
|
||||
toolchain: beta
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1.0.3
|
||||
with:
|
||||
command: build
|
||||
|
||||
test_beta:
|
||||
name: test on beta
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.5.3
|
||||
# Because we use nightly features for building docs,
|
||||
# using --all-features will fail without nightly toolchain.
|
||||
- uses: actions-rs/toolchain@v1.0.7
|
||||
with:
|
||||
toolchain: beta
|
||||
|
|
|
@ -7,6 +7,12 @@ Entries are listed in reverse chronological order.
|
|||
## 0.5.0
|
||||
|
||||
* add SigningShare type to ciphersuite libraries
|
||||
* most structs now have a private field which mean that they can no longer be
|
||||
instantiated directly. `new()` methods have been added to them.
|
||||
* change `SigningPackage::new()` to take `&[u8]P instead of `Vec<u8>`
|
||||
* expose `NonceCommitment` and `SignatureResponse` in ciphersuite crates
|
||||
* add `serde` support under `serde` feature to allow encoding structs which
|
||||
need to be communicated between participants.
|
||||
|
||||
## Released
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ debugless-unwrap = "0.0.4"
|
|||
digest = "0.10"
|
||||
hex = { version = "0.4.3", features = ["serde"] }
|
||||
rand_core = "0.6"
|
||||
serde = { version = "1.0.160", features = ["derive"], optional = true }
|
||||
serdect = { version = "0.2.0", optional = true }
|
||||
thiserror = "1.0"
|
||||
visibility = "0.0.1"
|
||||
zeroize = { version = "1.5.4", default-features = false, features = ["derive"] }
|
||||
|
@ -46,6 +48,7 @@ sha2 = "0.10.2"
|
|||
nightly = []
|
||||
default = []
|
||||
internals = []
|
||||
serde = ["dep:serde", "dep:serdect"]
|
||||
# Exposes ciphersuite-generic tests for other crates to use
|
||||
test-impl = ["proptest", "proptest-derive", "serde_json", "criterion"]
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
|
|||
|
||||
let message = "message to sign".as_bytes();
|
||||
let comms = commitments.clone().into_values().collect();
|
||||
let signing_package = frost::SigningPackage::new(comms, message.to_vec());
|
||||
let signing_package = frost::SigningPackage::new(comms, message);
|
||||
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("Round 2", min_signers),
|
||||
|
|
|
@ -183,7 +183,9 @@ fn derive_interpolating_value<C: Ciphersuite>(
|
|||
|
||||
/// Generated by the coordinator of the signing operation and distributed to
|
||||
/// each signing party
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
|
||||
pub struct SigningPackage<C: Ciphersuite> {
|
||||
/// The set of commitments participants published in the first round of the
|
||||
/// protocol.
|
||||
|
@ -192,26 +194,44 @@ pub struct SigningPackage<C: Ciphersuite> {
|
|||
///
|
||||
/// Each signer should perform protocol-specific verification on the
|
||||
/// message.
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(
|
||||
serialize_with = "serdect::slice::serialize_hex_lower_or_bin",
|
||||
deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec"
|
||||
)
|
||||
)]
|
||||
message: Vec<u8>,
|
||||
/// Ciphersuite ID for serialization
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(serialize_with = "crate::ciphersuite_serialize::<_, C>")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
|
||||
)]
|
||||
ciphersuite: (),
|
||||
}
|
||||
|
||||
impl<C> SigningPackage<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Create a new `SigingPackage`
|
||||
/// Create a new `SigningPackage`
|
||||
///
|
||||
/// The `signing_commitments` are sorted by participant `identifier`.
|
||||
pub fn new(
|
||||
signing_commitments: Vec<round1::SigningCommitments<C>>,
|
||||
message: Vec<u8>,
|
||||
message: &[u8],
|
||||
) -> SigningPackage<C> {
|
||||
SigningPackage {
|
||||
signing_commitments: signing_commitments
|
||||
.into_iter()
|
||||
.map(|s| (s.identifier, s))
|
||||
.collect(),
|
||||
message,
|
||||
message: message.to_vec(),
|
||||
ciphersuite: (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,12 +7,18 @@ use std::{
|
|||
|
||||
use crate::{Ciphersuite, Error, Field, FieldError, Group, Scalar};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::ScalarSerialization;
|
||||
|
||||
/// A FROST participant identifier.
|
||||
///
|
||||
/// The identifier is a field element in the scalar field that the secret polynomial is defined
|
||||
/// over, corresponding to some x-coordinate for a polynomial f(x) = y. MUST NOT be zero in the
|
||||
/// field, as f(0) = the shared secret.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(try_from = "ScalarSerialization<C>"))]
|
||||
#[cfg_attr(feature = "serde", serde(into = "ScalarSerialization<C>"))]
|
||||
pub struct Identifier<C: Ciphersuite>(Scalar<C>);
|
||||
|
||||
impl<C> Identifier<C>
|
||||
|
@ -20,8 +26,7 @@ where
|
|||
C: Ciphersuite,
|
||||
{
|
||||
/// Serialize the identifier using the ciphersuite encoding.
|
||||
#[cfg_attr(feature = "internals", visibility::make(pub))]
|
||||
pub(crate) fn serialize(&self) -> <<C::Group as Group>::Field as Field>::Serialization {
|
||||
pub fn serialize(&self) -> <<C::Group as Group>::Field as Field>::Serialization {
|
||||
<<C::Group as Group>::Field>::serialize(&self.0)
|
||||
}
|
||||
|
||||
|
@ -39,6 +44,28 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> TryFrom<ScalarSerialization<C>> for Identifier<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
type Error = Error<C>;
|
||||
|
||||
fn try_from(value: ScalarSerialization<C>) -> Result<Self, Self::Error> {
|
||||
Self::deserialize(&value.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> From<Identifier<C>> for ScalarSerialization<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn from(value: Identifier<C>) -> Self {
|
||||
Self(value.serialize())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Eq for Identifier<C> where C: Ciphersuite {}
|
||||
|
||||
impl<C> Debug for Identifier<C>
|
||||
|
|
|
@ -19,6 +19,9 @@ use crate::{
|
|||
frost::Identifier, Ciphersuite, Element, Error, Field, Group, Scalar, SigningKey, VerifyingKey,
|
||||
};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::{ElementSerialization, ScalarSerialization};
|
||||
|
||||
pub mod dkg;
|
||||
pub mod repairable;
|
||||
|
||||
|
@ -34,6 +37,9 @@ pub(crate) fn generate_coefficients<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
|
||||
/// A secret scalar value representing a signer's share of the group secret.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(try_from = "ScalarSerialization<C>"))]
|
||||
#[cfg_attr(feature = "serde", serde(into = "ScalarSerialization<C>"))]
|
||||
pub struct SigningShare<C: Ciphersuite>(pub(crate) Scalar<C>);
|
||||
|
||||
impl<C> SigningShare<C>
|
||||
|
@ -94,8 +100,33 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> TryFrom<ScalarSerialization<C>> for SigningShare<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
type Error = Error<C>;
|
||||
|
||||
fn try_from(value: ScalarSerialization<C>) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> From<SigningShare<C>> for ScalarSerialization<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn from(value: SigningShare<C>) -> Self {
|
||||
Self(value.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
/// A public group element that represents a single signer's public verification share.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(try_from = "ElementSerialization<C>"))]
|
||||
#[cfg_attr(feature = "serde", serde(into = "ElementSerialization<C>"))]
|
||||
pub struct VerifyingShare<C>(pub(super) Element<C>)
|
||||
where
|
||||
C: Ciphersuite;
|
||||
|
@ -137,11 +168,36 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> TryFrom<ElementSerialization<C>> for VerifyingShare<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
type Error = Error<C>;
|
||||
|
||||
fn try_from(value: ElementSerialization<C>) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> From<VerifyingShare<C>> for ElementSerialization<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn from(value: VerifyingShare<C>) -> Self {
|
||||
Self(value.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Group::Element`] newtype that is a commitment to one coefficient of our secret polynomial.
|
||||
///
|
||||
/// This is a (public) commitment to one coefficient of a secret polynomial used for performing
|
||||
/// verifiable secret sharing for a Shamir secret share.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(try_from = "ElementSerialization<C>"))]
|
||||
#[cfg_attr(feature = "serde", serde(into = "ElementSerialization<C>"))]
|
||||
pub struct CoefficientCommitment<C: Ciphersuite>(pub(crate) Element<C>);
|
||||
|
||||
impl<C> CoefficientCommitment<C>
|
||||
|
@ -166,6 +222,28 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> TryFrom<ElementSerialization<C>> for CoefficientCommitment<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
type Error = Error<C>;
|
||||
|
||||
fn try_from(value: ElementSerialization<C>) -> Result<Self, Self::Error> {
|
||||
Self::deserialize(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> From<CoefficientCommitment<C>> for ElementSerialization<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn from(value: CoefficientCommitment<C>) -> Self {
|
||||
Self(value.serialize())
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the commitments to the coefficients for our secret polynomial _f_,
|
||||
/// used to generate participants' key shares.
|
||||
///
|
||||
|
@ -178,7 +256,8 @@ where
|
|||
/// [`VerifiableSecretSharingCommitment`], either by performing pairwise comparison, or by using
|
||||
/// some agreed-upon public location for publication, where each participant can
|
||||
/// ensure that they received the correct (and same) value.
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct VerifiableSecretSharingCommitment<C: Ciphersuite>(
|
||||
pub(crate) Vec<CoefficientCommitment<C>>,
|
||||
);
|
||||
|
@ -219,7 +298,9 @@ where
|
|||
///
|
||||
/// To derive a FROST keypair, the receiver of the [`SecretShare`] *must* call
|
||||
/// .into(), which under the hood also performs validation.
|
||||
#[derive(Clone, Zeroize)]
|
||||
#[derive(Clone, Zeroize, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
|
||||
pub struct SecretShare<C: Ciphersuite> {
|
||||
/// The participant identifier of this [`SecretShare`].
|
||||
#[zeroize(skip)]
|
||||
|
@ -229,12 +310,36 @@ pub struct SecretShare<C: Ciphersuite> {
|
|||
#[zeroize(skip)]
|
||||
/// The commitments to be distributed among signers.
|
||||
pub commitment: VerifiableSecretSharingCommitment<C>,
|
||||
/// Ciphersuite ID for serialization
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(serialize_with = "crate::ciphersuite_serialize::<_, C>")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
|
||||
)]
|
||||
ciphersuite: (),
|
||||
}
|
||||
|
||||
impl<C> SecretShare<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Create a new [`SecretShare`] instance.
|
||||
pub fn new(
|
||||
identifier: Identifier<C>,
|
||||
value: SigningShare<C>,
|
||||
commitment: VerifiableSecretSharingCommitment<C>,
|
||||
) -> Self {
|
||||
SecretShare {
|
||||
identifier,
|
||||
value,
|
||||
commitment,
|
||||
ciphersuite: (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the inner [`SigningShare`] value.
|
||||
pub fn secret(&self) -> &SigningShare<C> {
|
||||
&self.value
|
||||
|
@ -332,6 +437,7 @@ pub fn split<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
PublicKeyPackage {
|
||||
signer_pubkeys,
|
||||
group_public,
|
||||
ciphersuite: (),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -379,7 +485,9 @@ fn evaluate_vss<C: Ciphersuite>(
|
|||
/// When using a central dealer, [`SecretShare`]s are distributed to
|
||||
/// participants, who then perform verification, before deriving
|
||||
/// [`KeyPackage`]s, which they store to later use during signing.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
|
||||
pub struct KeyPackage<C: Ciphersuite> {
|
||||
/// Denotes the participant identifier each secret share key package is owned by.
|
||||
pub identifier: Identifier<C>,
|
||||
|
@ -389,12 +497,38 @@ pub struct KeyPackage<C: Ciphersuite> {
|
|||
pub public: VerifyingShare<C>,
|
||||
/// The public signing key that represents the entire group.
|
||||
pub group_public: VerifyingKey<C>,
|
||||
/// Ciphersuite ID for serialization
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(serialize_with = "crate::ciphersuite_serialize::<_, C>")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
|
||||
)]
|
||||
ciphersuite: (),
|
||||
}
|
||||
|
||||
impl<C> KeyPackage<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Create a new [`KeyPackage`] instance.
|
||||
pub fn new(
|
||||
identifier: Identifier<C>,
|
||||
secret_share: SigningShare<C>,
|
||||
public: VerifyingShare<C>,
|
||||
group_public: VerifyingKey<C>,
|
||||
) -> Self {
|
||||
Self {
|
||||
identifier,
|
||||
secret_share,
|
||||
public,
|
||||
group_public,
|
||||
ciphersuite: (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the participant identifier associated with this [`KeyPackage`].
|
||||
pub fn identifier(&self) -> &Identifier<C> {
|
||||
&self.identifier
|
||||
|
@ -438,6 +572,7 @@ where
|
|||
secret_share: secret_share.value,
|
||||
public,
|
||||
group_public,
|
||||
ciphersuite: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -446,6 +581,9 @@ where
|
|||
/// group public key.
|
||||
///
|
||||
/// Used for verification purposes before publishing a signature.
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
|
||||
pub struct PublicKeyPackage<C: Ciphersuite> {
|
||||
/// When performing signing, the coordinator must ensure that they have the
|
||||
/// correct view of participants' public keys to perform verification before
|
||||
|
@ -454,6 +592,33 @@ pub struct PublicKeyPackage<C: Ciphersuite> {
|
|||
pub signer_pubkeys: HashMap<Identifier<C>, VerifyingShare<C>>,
|
||||
/// The joint public key for the entire group.
|
||||
pub group_public: VerifyingKey<C>,
|
||||
/// Ciphersuite ID for serialization
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(serialize_with = "crate::ciphersuite_serialize::<_, C>")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
|
||||
)]
|
||||
ciphersuite: (),
|
||||
}
|
||||
|
||||
impl<C> PublicKeyPackage<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Create a new [`PublicKeyPackage`] instance.
|
||||
pub fn new(
|
||||
signer_pubkeys: HashMap<Identifier<C>, VerifyingShare<C>>,
|
||||
group_public: VerifyingKey<C>,
|
||||
) -> Self {
|
||||
Self {
|
||||
signer_pubkeys,
|
||||
group_public,
|
||||
ciphersuite: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a secret polynomial to use in secret sharing, for the given
|
||||
|
@ -538,6 +703,7 @@ pub(crate) fn generate_secret_shares<C: Ciphersuite>(
|
|||
identifier: id,
|
||||
value: SigningShare(value),
|
||||
commitment: commitment.clone(),
|
||||
ciphersuite: (),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,9 @@ pub mod round1 {
|
|||
|
||||
/// The package that must be broadcast by each participant to all other participants
|
||||
/// between the first and second parts of the DKG protocol (round 1).
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
|
||||
pub struct Package<C: Ciphersuite> {
|
||||
/// The identifier of the participant who is sending the package (i).
|
||||
pub sender_identifier: Identifier<C>,
|
||||
|
@ -59,6 +61,35 @@ pub mod round1 {
|
|||
pub commitment: VerifiableSecretSharingCommitment<C>,
|
||||
/// The proof of knowledge of the temporary secret (σ_i = (R_i, μ_i))
|
||||
pub proof_of_knowledge: Signature<C>,
|
||||
/// Ciphersuite ID for serialization
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(serialize_with = "crate::ciphersuite_serialize::<_, C>")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
|
||||
)]
|
||||
pub(super) ciphersuite: (),
|
||||
}
|
||||
|
||||
impl<C> Package<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Create a new [`Package`] instance.
|
||||
pub fn new(
|
||||
sender_identifier: Identifier<C>,
|
||||
commitment: VerifiableSecretSharingCommitment<C>,
|
||||
proof_of_knowledge: Signature<C>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sender_identifier,
|
||||
commitment,
|
||||
proof_of_knowledge,
|
||||
ciphersuite: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The secret package that must be kept in memory by the participant
|
||||
|
@ -92,7 +123,9 @@ pub mod round2 {
|
|||
/// # Security
|
||||
///
|
||||
/// The package must be sent on an *confidential* and *authenticated* channel.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
|
||||
pub struct Package<C: Ciphersuite> {
|
||||
/// The identifier of the participant that generated the package (i).
|
||||
pub sender_identifier: Identifier<C>,
|
||||
|
@ -100,6 +133,35 @@ pub mod round2 {
|
|||
pub receiver_identifier: Identifier<C>,
|
||||
/// The secret share being sent.
|
||||
pub secret_share: SigningShare<C>,
|
||||
/// Ciphersuite ID for serialization
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(serialize_with = "crate::ciphersuite_serialize::<_, C>")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
|
||||
)]
|
||||
pub(super) ciphersuite: (),
|
||||
}
|
||||
|
||||
impl<C> Package<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Create a new [`Package`] instance.
|
||||
pub fn new(
|
||||
sender_identifier: Identifier<C>,
|
||||
receiver_identifier: Identifier<C>,
|
||||
secret_share: SigningShare<C>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sender_identifier,
|
||||
receiver_identifier,
|
||||
secret_share,
|
||||
ciphersuite: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The secret package that must be kept in memory by the participant
|
||||
|
@ -168,6 +230,7 @@ pub fn part1<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
sender_identifier: identifier,
|
||||
commitment,
|
||||
proof_of_knowledge: Signature { R: R_i, z: mu_i },
|
||||
ciphersuite: (),
|
||||
};
|
||||
|
||||
Ok((secret_package, package))
|
||||
|
@ -235,6 +298,7 @@ pub fn part2<C: Ciphersuite>(
|
|||
sender_identifier: secret_package.identifier,
|
||||
receiver_identifier: ell,
|
||||
secret_share: SigningShare(value),
|
||||
ciphersuite: (),
|
||||
});
|
||||
}
|
||||
let fii = evaluate_polynomial(secret_package.identifier, &secret_package.coefficients);
|
||||
|
@ -346,6 +410,7 @@ pub fn part3<C: Ciphersuite>(
|
|||
identifier: round2_secret_package.identifier,
|
||||
value: f_ell_i,
|
||||
commitment: commitment.clone(),
|
||||
ciphersuite: (),
|
||||
};
|
||||
|
||||
// Verify the share. We don't need the result.
|
||||
|
@ -390,10 +455,12 @@ pub fn part3<C: Ciphersuite>(
|
|||
secret_share: signing_share,
|
||||
public: verifying_key,
|
||||
group_public,
|
||||
ciphersuite: (),
|
||||
};
|
||||
let public_key_package = PublicKeyPackage {
|
||||
signer_pubkeys: all_verifying_keys,
|
||||
group_public,
|
||||
ciphersuite: (),
|
||||
};
|
||||
|
||||
Ok((key_package, public_key_package))
|
||||
|
|
|
@ -129,5 +129,6 @@ pub fn repair_share_step_3<C: Ciphersuite>(
|
|||
identifier,
|
||||
value: SigningShare(share),
|
||||
commitment: commitment.clone(),
|
||||
ciphersuite: (),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@ use zeroize::Zeroize;
|
|||
|
||||
use crate::{frost, Ciphersuite, Element, Error, Field, Group, Scalar};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::ElementSerialization;
|
||||
|
||||
use super::{keys::SigningShare, Identifier};
|
||||
|
||||
/// A scalar that is a signing nonce.
|
||||
|
@ -106,7 +109,10 @@ where
|
|||
}
|
||||
|
||||
/// A Ristretto point that is a commitment to a signing nonce share.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(try_from = "ElementSerialization<C>"))]
|
||||
#[cfg_attr(feature = "serde", serde(into = "ElementSerialization<C>"))]
|
||||
pub struct NonceCommitment<C: Ciphersuite>(pub(super) Element<C>);
|
||||
|
||||
impl<C> NonceCommitment<C>
|
||||
|
@ -126,6 +132,28 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> TryFrom<ElementSerialization<C>> for NonceCommitment<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
type Error = Error<C>;
|
||||
|
||||
fn try_from(value: ElementSerialization<C>) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> From<NonceCommitment<C>> for ElementSerialization<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn from(value: NonceCommitment<C>) -> Self {
|
||||
Self(value.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Debug for NonceCommitment<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
|
@ -219,7 +247,9 @@ where
|
|||
///
|
||||
/// This step can be batched if desired by the implementation. Each
|
||||
/// SigningCommitment can be used for exactly *one* signature.
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
|
||||
pub struct SigningCommitments<C: Ciphersuite> {
|
||||
/// The participant identifier.
|
||||
pub identifier: Identifier<C>,
|
||||
|
@ -227,12 +257,36 @@ pub struct SigningCommitments<C: Ciphersuite> {
|
|||
pub hiding: NonceCommitment<C>,
|
||||
/// Commitment to the binding [`Nonce`].
|
||||
pub binding: NonceCommitment<C>,
|
||||
/// Ciphersuite ID for serialization
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(serialize_with = "crate::ciphersuite_serialize::<_, C>")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
|
||||
)]
|
||||
ciphersuite: (),
|
||||
}
|
||||
|
||||
impl<C> SigningCommitments<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Create new SigningCommitments
|
||||
pub fn new(
|
||||
identifier: Identifier<C>,
|
||||
hiding: NonceCommitment<C>,
|
||||
binding: NonceCommitment<C>,
|
||||
) -> Self {
|
||||
Self {
|
||||
identifier,
|
||||
hiding,
|
||||
binding,
|
||||
ciphersuite: (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the [signature commitment share] from these round one signing commitments.
|
||||
///
|
||||
/// [signature commitment share]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-signature-share-verificatio
|
||||
|
@ -264,6 +318,7 @@ where
|
|||
identifier,
|
||||
hiding: nonces.hiding.clone().into(),
|
||||
binding: nonces.binding.clone().into(),
|
||||
ciphersuite: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,14 @@ use crate::{
|
|||
Challenge, Ciphersuite, Error, Field, Group,
|
||||
};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::ScalarSerialization;
|
||||
|
||||
/// A representation of a single signature share used in FROST structures and messages.
|
||||
#[derive(Clone, Copy)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(try_from = "ScalarSerialization<C>"))]
|
||||
#[cfg_attr(feature = "serde", serde(into = "ScalarSerialization<C>"))]
|
||||
pub struct SignatureResponse<C: Ciphersuite> {
|
||||
/// The scalar contribution to the group signature.
|
||||
pub z_share: Scalar<C>,
|
||||
|
@ -34,6 +40,28 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> TryFrom<ScalarSerialization<C>> for SignatureResponse<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
type Error = Error<C>;
|
||||
|
||||
fn try_from(value: ScalarSerialization<C>) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> From<SignatureResponse<C>> for ScalarSerialization<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn from(value: SignatureResponse<C>) -> Self {
|
||||
Self(value.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Debug for SignatureResponse<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
|
@ -60,17 +88,38 @@ where
|
|||
/// A participant's signature share, which the coordinator will aggregate with all other signer's
|
||||
/// shares into the joint signature.
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
|
||||
pub struct SignatureShare<C: Ciphersuite> {
|
||||
/// Represents the participant identifier.
|
||||
pub identifier: Identifier<C>,
|
||||
/// This participant's signature over the message.
|
||||
pub signature: SignatureResponse<C>,
|
||||
/// Ciphersuite ID for serialization
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(serialize_with = "crate::ciphersuite_serialize::<_, C>")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
|
||||
)]
|
||||
ciphersuite: (),
|
||||
}
|
||||
|
||||
impl<C> SignatureShare<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
/// Create a new [`SignatureShare`].
|
||||
pub fn new(identifier: Identifier<C>, signature: SignatureResponse<C>) -> Self {
|
||||
Self {
|
||||
identifier,
|
||||
signature,
|
||||
ciphersuite: (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the participant identifier associated with this [`SignatureShare`].
|
||||
pub fn identifier(&self) -> &Identifier<C> {
|
||||
&self.identifier
|
||||
|
@ -129,6 +178,7 @@ fn compute_signature_share<C: Ciphersuite>(
|
|||
SignatureShare::<C> {
|
||||
identifier: *key_package.identifier(),
|
||||
signature: SignatureResponse::<C> { z_share },
|
||||
ciphersuite: (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@ use std::{
|
|||
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
// Re-export serde
|
||||
#[cfg(feature = "serde")]
|
||||
pub use serde;
|
||||
|
||||
pub mod batch;
|
||||
#[cfg(any(test, feature = "test-impl"))]
|
||||
pub mod benches;
|
||||
|
@ -91,6 +95,41 @@ pub trait Field: Copy + Clone {
|
|||
/// 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")]
|
||||
pub(crate) struct ScalarSerialization<C: Ciphersuite>(
|
||||
<<<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::slice::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>,
|
||||
{
|
||||
let bytes = serdect::slice::deserialize_hex_or_bin_vec(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.
|
||||
///
|
||||
|
@ -154,11 +193,49 @@ pub trait Group: Copy + Clone + PartialEq {
|
|||
/// 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::slice::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>,
|
||||
{
|
||||
let bytes = serdect::slice::deserialize_hex_or_bin_vec(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-11.html#name-ciphersuites
|
||||
pub trait Ciphersuite: Copy + Clone + PartialEq + Debug {
|
||||
/// The ciphersuite ID string
|
||||
const ID: &'static str;
|
||||
|
||||
/// The prime order group (or subgroup) that this ciphersuite operates over.
|
||||
type Group: Group;
|
||||
|
||||
|
@ -308,3 +385,28 @@ pub(crate) fn random_nonzero<C: Ciphersuite, R: RngCore + CryptoRng>(rng: &mut R
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
{
|
||||
s.serialize_str(C::ID)
|
||||
}
|
||||
|
||||
/// 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,
|
||||
{
|
||||
let s: &str = serde::de::Deserialize::deserialize(deserializer)?;
|
||||
if s != C::ID {
|
||||
Err(serde::de::Error::custom("wrong ciphersuite"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,42 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> serde::Serialize for Signature<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
C::Group: Group,
|
||||
<C::Group as Group>::Field: Field,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serdect::slice::serialize_hex_lower_or_bin(&self.to_bytes().as_ref(), serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, C> serde::Deserialize<'de> for Signature<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
C::Group: Group,
|
||||
<C::Group as Group>::Field: Field,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?;
|
||||
let array = bytes
|
||||
.try_into()
|
||||
.map_err(|_| serde::de::Error::custom("invalid byte length"))?;
|
||||
let identifier = Signature::from_bytes(array)
|
||||
.map_err(|err| serde::de::Error::custom(format!("{err}")))?;
|
||||
Ok(identifier)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> std::fmt::Debug for Signature<C> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("Signature")
|
||||
|
|
|
@ -114,7 +114,7 @@ fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
|
|||
let mut signature_shares = Vec::new();
|
||||
let message = "message to sign".as_bytes();
|
||||
let comms = commitments_map.clone().into_values().collect();
|
||||
let signing_package = frost::SigningPackage::new(comms, message.to_vec());
|
||||
let signing_package = frost::SigningPackage::new(comms, message);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Round 2: each participant generates their signature share
|
||||
|
@ -315,10 +315,7 @@ where
|
|||
assert!(verifying_keys_for_participant.signer_pubkeys == verifying_keys);
|
||||
}
|
||||
|
||||
let pubkeys = frost::keys::PublicKeyPackage {
|
||||
signer_pubkeys: verifying_keys,
|
||||
group_public: group_public.unwrap(),
|
||||
};
|
||||
let pubkeys = frost::keys::PublicKeyPackage::new(verifying_keys, group_public.unwrap());
|
||||
|
||||
// Proceed with the signing test.
|
||||
check_sign(min_signers, key_packages, rng, pubkeys)
|
||||
|
|
|
@ -180,11 +180,11 @@ pub fn check_repair_share_step_3<C: Ciphersuite, R: RngCore + CryptoRng>(
|
|||
|
||||
let actual_sigma: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar =
|
||||
generate_scalar_from_byte_string::<C>(sigmas["sigma_sum"].as_str().unwrap());
|
||||
let actual: SecretShare<C> = SecretShare {
|
||||
identifier: Identifier::try_from(2).unwrap(),
|
||||
value: SigningShare(actual_sigma),
|
||||
let actual: SecretShare<C> = SecretShare::new(
|
||||
Identifier::try_from(2).unwrap(),
|
||||
SigningShare(actual_sigma),
|
||||
commitment,
|
||||
};
|
||||
);
|
||||
|
||||
assert!(actual.value == expected.value);
|
||||
}
|
||||
|
|
|
@ -66,12 +66,12 @@ pub fn parse_test_vectors<C: Ciphersuite>(json_vectors: &Value) -> TestVectors<C
|
|||
.unwrap();
|
||||
let signer_public = secret.into();
|
||||
|
||||
let key_package = KeyPackage::<C> {
|
||||
identifier: u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
secret_share: secret,
|
||||
public: signer_public,
|
||||
let key_package = KeyPackage::<C>::new(
|
||||
u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
secret,
|
||||
signer_public,
|
||||
group_public,
|
||||
};
|
||||
);
|
||||
|
||||
key_packages.insert(*key_package.identifier(), key_package);
|
||||
}
|
||||
|
@ -109,15 +109,12 @@ pub fn parse_test_vectors<C: Ciphersuite>(json_vectors: &Value) -> TestVectors<C
|
|||
|
||||
signer_nonces.insert(identifier, signing_nonces);
|
||||
|
||||
let signing_commitments = SigningCommitments::<C> {
|
||||
let signing_commitments = SigningCommitments::<C>::new(
|
||||
identifier,
|
||||
hiding: NonceCommitment::from_hex(signer["hiding_nonce_commitment"].as_str().unwrap())
|
||||
NonceCommitment::from_hex(signer["hiding_nonce_commitment"].as_str().unwrap()).unwrap(),
|
||||
NonceCommitment::from_hex(signer["binding_nonce_commitment"].as_str().unwrap())
|
||||
.unwrap(),
|
||||
binding: NonceCommitment::from_hex(
|
||||
signer["binding_nonce_commitment"].as_str().unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
};
|
||||
);
|
||||
|
||||
signer_commitments.insert(identifier, signing_commitments);
|
||||
|
||||
|
@ -148,12 +145,12 @@ pub fn parse_test_vectors<C: Ciphersuite>(json_vectors: &Value) -> TestVectors<C
|
|||
)
|
||||
.debugless_unwrap();
|
||||
|
||||
let signature_share = SignatureShare::<C> {
|
||||
identifier: u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
signature: SignatureResponse {
|
||||
let signature_share = SignatureShare::<C>::new(
|
||||
u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
SignatureResponse {
|
||||
z_share: <<C::Group as Group>::Field>::deserialize(&sig_share).unwrap(),
|
||||
},
|
||||
};
|
||||
);
|
||||
|
||||
signature_shares.insert(
|
||||
u16::from_str(i).unwrap().try_into().unwrap(),
|
||||
|
@ -275,7 +272,7 @@ pub fn check_sign_with_test_vectors<C: Ciphersuite>(json_vectors: &Value) {
|
|||
|
||||
let signer_commitments_vec = signer_commitments.into_values().collect();
|
||||
|
||||
let signing_package = frost::SigningPackage::new(signer_commitments_vec, message_bytes);
|
||||
let signing_package = frost::SigningPackage::new(signer_commitments_vec, &message_bytes);
|
||||
|
||||
for (identifier, input) in signing_package.binding_factor_preimages(&[]).iter() {
|
||||
assert_eq!(*input, binding_factor_inputs[identifier]);
|
||||
|
@ -310,10 +307,7 @@ pub fn check_sign_with_test_vectors<C: Ciphersuite>(json_vectors: &Value) {
|
|||
.map(|(i, key_package)| (i, *key_package.public()))
|
||||
.collect();
|
||||
|
||||
let pubkey_package = frost::keys::PublicKeyPackage {
|
||||
signer_pubkeys,
|
||||
group_public,
|
||||
};
|
||||
let pubkey_package = frost::keys::PublicKeyPackage::new(signer_pubkeys, group_public);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Aggregation: collects the signing shares from all participants,
|
||||
|
|
|
@ -5,8 +5,14 @@ use hex::FromHex;
|
|||
|
||||
use crate::{Challenge, Ciphersuite, Element, Error, Group, Signature};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::ElementSerialization;
|
||||
|
||||
/// A valid verifying key for Schnorr signatures over a FROST [`Ciphersuite::Group`].
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(try_from = "ElementSerialization<C>"))]
|
||||
#[cfg_attr(feature = "serde", serde(into = "ElementSerialization<C>"))]
|
||||
pub struct VerifyingKey<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
|
@ -105,6 +111,28 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> TryFrom<ElementSerialization<C>> for VerifyingKey<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
type Error = Error<C>;
|
||||
|
||||
fn try_from(value: ElementSerialization<C>) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<C> From<VerifyingKey<C>> for ElementSerialization<C>
|
||||
where
|
||||
C: Ciphersuite,
|
||||
{
|
||||
fn from(value: VerifyingKey<C>) -> Self {
|
||||
Self(value.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// impl<C: Ciphersuite> From<VerifyingKey<C>> for <C::Group as Group>::ElementSerialization {
|
||||
// fn from(pk: VerifyingKey<C>) -> <C::Group as Group>::ElementSerialization {
|
||||
// pk.bytes.bytes
|
||||
|
|
|
@ -70,7 +70,7 @@ let message = "message to sign".as_bytes();
|
|||
# // In practice, the SigningPackage must be sent to all participants
|
||||
# // involved in the current signing (at least min_signers participants),
|
||||
# // using an authenticate channel (and confidential if the message is secret).
|
||||
let signing_package = frost::SigningPackage::new(commitments_received, message.to_vec());
|
||||
let signing_package = frost::SigningPackage::new(commitments_received, message);
|
||||
# // ANCHOR_END: round2_package
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -150,6 +150,8 @@ const CONTEXT_STRING: &str = "FROST-ED25519-SHA512-v11";
|
|||
pub struct Ed25519Sha512;
|
||||
|
||||
impl Ciphersuite for Ed25519Sha512 {
|
||||
const ID: &'static str = "FROST(Ed25519, SHA-512)";
|
||||
|
||||
type Group = Ed25519Group;
|
||||
|
||||
type HashOutput = [u8; 64];
|
||||
|
|
|
@ -70,7 +70,7 @@ let message = "message to sign".as_bytes();
|
|||
# // In practice, the SigningPackage must be sent to all participants
|
||||
# // involved in the current signing (at least min_signers participants),
|
||||
# // using an authenticate channel (and confidential if the message is secret).
|
||||
let signing_package = frost::SigningPackage::new(commitments_received, message.to_vec());
|
||||
let signing_package = frost::SigningPackage::new(commitments_received, message);
|
||||
# // ANCHOR_END: round2_package
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -145,6 +145,8 @@ const CONTEXT_STRING: &str = "FROST-ED448-SHAKE256-v11";
|
|||
pub struct Ed448Shake256;
|
||||
|
||||
impl Ciphersuite for Ed448Shake256 {
|
||||
const ID: &'static str = "FROST(Ed448, SHAKE256)";
|
||||
|
||||
type Group = Ed448Group;
|
||||
|
||||
type HashOutput = [u8; 114];
|
||||
|
|
|
@ -70,7 +70,7 @@ let message = "message to sign".as_bytes();
|
|||
# // In practice, the SigningPackage must be sent to all participants
|
||||
# // involved in the current signing (at least min_signers participants),
|
||||
# // using an authenticate channel (and confidential if the message is secret).
|
||||
let signing_package = frost::SigningPackage::new(commitments_received, message.to_vec());
|
||||
let signing_package = frost::SigningPackage::new(commitments_received, message);
|
||||
# // ANCHOR_END: round2_package
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -170,6 +170,8 @@ const CONTEXT_STRING: &str = "FROST-P256-SHA256-v11";
|
|||
pub struct P256Sha256;
|
||||
|
||||
impl Ciphersuite for P256Sha256 {
|
||||
const ID: &'static str = "FROST(P-256, SHA-256)";
|
||||
|
||||
type Group = P256Group;
|
||||
|
||||
type HashOutput = [u8; 32];
|
||||
|
|
|
@ -61,7 +61,7 @@ pub fn check_randomized_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>
|
|||
let mut signature_shares: Vec<frost::round2::SignatureShare<_>> = Vec::new();
|
||||
let message = "message to sign".as_bytes();
|
||||
let comms = commitments.clone().into_values().collect();
|
||||
let signing_package = frost::SigningPackage::new(comms, message.to_vec());
|
||||
let signing_package = frost::SigningPackage::new(comms, message);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Round 2: each participant generates their signature share
|
||||
|
|
|
@ -28,6 +28,7 @@ bincode = "1"
|
|||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
ed25519-dalek = "1.0.1"
|
||||
ed25519-zebra = "4.0.0"
|
||||
hex = "0.4.3"
|
||||
lazy_static = "1.4"
|
||||
proptest = "1.0"
|
||||
proptest-derive = "0.3"
|
||||
|
@ -38,6 +39,7 @@ serde_json = "1.0"
|
|||
[features]
|
||||
nightly = []
|
||||
default = []
|
||||
serde = ["frost-core/serde"]
|
||||
|
||||
[lib]
|
||||
# Disables non-criterion benchmark which is not used; prevents errors
|
||||
|
|
|
@ -70,7 +70,7 @@ let message = "message to sign".as_bytes();
|
|||
# // In practice, the SigningPackage must be sent to all participants
|
||||
# // involved in the current signing (at least min_signers participants),
|
||||
# // using an authenticate channel (and confidential if the message is secret).
|
||||
let signing_package = frost::SigningPackage::new(commitments_received, message.to_vec());
|
||||
let signing_package = frost::SigningPackage::new(commitments_received, message);
|
||||
# // ANCHOR_END: round2_package
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -13,6 +13,9 @@ use sha2::{Digest, Sha512};
|
|||
|
||||
use frost_core::frost;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use frost_core::serde;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
|
@ -136,9 +139,13 @@ const CONTEXT_STRING: &str = "FROST-RISTRETTO255-SHA512-v11";
|
|||
|
||||
/// An implementation of the FROST(ristretto255, SHA-512) ciphersuite.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(crate = "self::serde"))]
|
||||
pub struct Ristretto255Sha512;
|
||||
|
||||
impl Ciphersuite for Ristretto255Sha512 {
|
||||
const ID: &'static str = "FROST(ristretto255, SHA-512)";
|
||||
|
||||
type Group = RistrettoGroup;
|
||||
|
||||
type HashOutput = [u8; 64];
|
||||
|
|
|
@ -0,0 +1,651 @@
|
|||
#![cfg(feature = "serde")]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use frost_core::{
|
||||
frost::keys::{SigningShare, VerifyingShare},
|
||||
Ciphersuite, Group,
|
||||
};
|
||||
use frost_ristretto255::{
|
||||
keys::{
|
||||
dkg::{round1, round2},
|
||||
KeyPackage, PublicKeyPackage, SecretShare, VerifiableSecretSharingCommitment,
|
||||
},
|
||||
round1::{NonceCommitment, SigningCommitments},
|
||||
round2::{SignatureResponse, SignatureShare},
|
||||
Signature, SigningPackage, VerifyingKey,
|
||||
};
|
||||
|
||||
type C = frost_ristretto255::Ristretto255Sha512;
|
||||
|
||||
fn build_sample_signing_commitments() -> SigningCommitments {
|
||||
let element1 = <C as Ciphersuite>::Group::generator();
|
||||
let element2 = element1 + element1;
|
||||
let serialized_element1 = <C as Ciphersuite>::Group::serialize(&element1);
|
||||
let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2);
|
||||
let hiding_nonce_commitment = NonceCommitment::from_bytes(serialized_element1).unwrap();
|
||||
let binding_nonce_commitment = NonceCommitment::from_bytes(serialized_element2).unwrap();
|
||||
let identifier = 42u16.try_into().unwrap();
|
||||
|
||||
SigningCommitments::new(
|
||||
identifier,
|
||||
hiding_nonce_commitment,
|
||||
binding_nonce_commitment,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_signing_commitments_serialization() {
|
||||
let commitments = build_sample_signing_commitments();
|
||||
|
||||
let json = serde_json::to_string_pretty(&commitments).unwrap();
|
||||
println!("{}", json);
|
||||
|
||||
let decoded_commitments: SigningCommitments = serde_json::from_str(&json).unwrap();
|
||||
assert!(commitments == decoded_commitments);
|
||||
|
||||
let json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
let decoded_commitments: SigningCommitments = serde_json::from_str(json).unwrap();
|
||||
assert!(commitments == decoded_commitments);
|
||||
|
||||
let invalid_json = "{}";
|
||||
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
|
||||
|
||||
// Wrong ciphersuite
|
||||
let invalid_json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
|
||||
"ciphersuite": "FROST(Ed25519, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
|
||||
|
||||
// Invalid identifier
|
||||
let invalid_json = r#"{
|
||||
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
|
||||
|
||||
// Invalid field
|
||||
let invalid_json = r#"{
|
||||
"foo": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
|
||||
|
||||
// Missing field
|
||||
let invalid_json = r#"{
|
||||
"foo": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
|
||||
|
||||
// Extra field
|
||||
let invalid_json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
|
||||
"extra": 1
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_signing_package_serialization() {
|
||||
let commitments = build_sample_signing_commitments();
|
||||
let message = "hello world".as_bytes();
|
||||
|
||||
let signing_package = SigningPackage::new(vec![commitments], message);
|
||||
|
||||
let json = serde_json::to_string_pretty(&signing_package).unwrap();
|
||||
println!("{}", json);
|
||||
|
||||
let decoded_signing_package: SigningPackage = serde_json::from_str(&json).unwrap();
|
||||
assert!(signing_package == decoded_signing_package);
|
||||
|
||||
let invalid_json = "{}";
|
||||
assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
|
||||
|
||||
let json = r#"{
|
||||
"signing_commitments": {
|
||||
"2a00000000000000000000000000000000000000000000000000000000000000": {
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}
|
||||
},
|
||||
"message": "68656c6c6f20776f726c64",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
let decoded_signing_package: SigningPackage = serde_json::from_str(json).unwrap();
|
||||
assert!(signing_package == decoded_signing_package);
|
||||
|
||||
// Invalid identifier
|
||||
let invalid_json = r#"{
|
||||
"signing_commitments": {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000": {
|
||||
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}
|
||||
},
|
||||
"message": "68656c6c6f20776f726c64",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
|
||||
|
||||
// Invalid field
|
||||
let invalid_json = r#"{
|
||||
"signing_commitments": {
|
||||
"2a00000000000000000000000000000000000000000000000000000000000000": {
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"foo": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}
|
||||
},
|
||||
"message": "68656c6c6f20776f726c64",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
|
||||
|
||||
// Missing field
|
||||
let invalid_json = r#"{
|
||||
"signing_commitments": {
|
||||
"2a00000000000000000000000000000000000000000000000000000000000000": {
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}
|
||||
},
|
||||
"message": "68656c6c6f20776f726c64",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
|
||||
|
||||
// Extra field
|
||||
let invalid_json = r#"{
|
||||
"signing_commitments": {
|
||||
"2a00000000000000000000000000000000000000000000000000000000000000": {
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}
|
||||
},
|
||||
"message": "68656c6c6f20776f726c64",
|
||||
"extra": 1,
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}
|
||||
"#;
|
||||
assert!(serde_json::from_str::<SigningPackage>(invalid_json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_signature_share_serialization() {
|
||||
let identifier = 42u16.try_into().unwrap();
|
||||
let serialized_scalar =
|
||||
hex::decode("a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let signature_response = SignatureResponse::from_bytes(serialized_scalar).unwrap();
|
||||
|
||||
let signature_share = SignatureShare::new(identifier, signature_response);
|
||||
|
||||
let json = serde_json::to_string_pretty(&signature_share).unwrap();
|
||||
println!("{}", json);
|
||||
|
||||
let decoded_signature_share: SignatureShare = serde_json::from_str(&json).unwrap();
|
||||
assert!(signature_share == decoded_signature_share);
|
||||
|
||||
let json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"signature": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap();
|
||||
assert!(signature_share == decoded_commitments);
|
||||
|
||||
let invalid_json = "{}";
|
||||
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
|
||||
|
||||
// Invalid identifier
|
||||
let invalid_json = r#"{
|
||||
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"signature": "e660b88149e1dd06d7cace3c5ee32a71b4b718e2719583630ba916579fe8320d",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
|
||||
|
||||
// Invalid field
|
||||
let invalid_json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"foo": "e660b88149e1dd06d7cace3c5ee32a71b4b718e2719583630ba916579fe8320d",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
|
||||
|
||||
// Missing field
|
||||
let invalid_json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",,
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
|
||||
|
||||
// Extra field
|
||||
let invalid_json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"signature": "e660b88149e1dd06d7cace3c5ee32a71b4b718e2719583630ba916579fe8320d",
|
||||
"extra": 1,
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_secret_share_serialization() {
|
||||
let identifier = 42u16.try_into().unwrap();
|
||||
let serialized_scalar =
|
||||
hex::decode("a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let serialized_element =
|
||||
hex::decode("e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
|
||||
let vss_commitment =
|
||||
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
|
||||
|
||||
let secret_share = SecretShare::new(identifier, signing_share, vss_commitment);
|
||||
|
||||
let json = serde_json::to_string_pretty(&secret_share).unwrap();
|
||||
println!("{}", json);
|
||||
|
||||
let decoded_secret_share: SecretShare = serde_json::from_str(&json).unwrap();
|
||||
assert!(secret_share == decoded_secret_share);
|
||||
|
||||
let json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"value": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"commitment": [
|
||||
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
],
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
let decoded_secret_share: SecretShare = serde_json::from_str(json).unwrap();
|
||||
assert!(secret_share == decoded_secret_share);
|
||||
|
||||
let invalid_json = "{}";
|
||||
assert!(serde_json::from_str::<SecretShare>(invalid_json).is_err());
|
||||
|
||||
// Invalid identifier
|
||||
let invalid_json = r#"{
|
||||
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"value": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"commitment": [
|
||||
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
],
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SecretShare>(invalid_json).is_err());
|
||||
|
||||
// Invalid field
|
||||
let invalid_json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"foo": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"commitment": [
|
||||
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
],
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SecretShare>(invalid_json).is_err());
|
||||
|
||||
// Missing field
|
||||
let invalid_json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"commitment": [
|
||||
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
],
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SecretShare>(invalid_json).is_err());
|
||||
|
||||
// Extra field
|
||||
let invalid_json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"value": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"commitment": [
|
||||
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
],
|
||||
"extra": 1,
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<SecretShare>(invalid_json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_key_package_serialization() {
|
||||
let identifier = 42u16.try_into().unwrap();
|
||||
let serialized_scalar =
|
||||
hex::decode("a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let serialized_element =
|
||||
hex::decode("e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
|
||||
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
|
||||
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
|
||||
|
||||
let key_package = KeyPackage::new(identifier, signing_share, verifying_share, verifying_key);
|
||||
|
||||
let json = serde_json::to_string_pretty(&key_package).unwrap();
|
||||
println!("{}", json);
|
||||
|
||||
let decoded_key_package: KeyPackage = serde_json::from_str(&json).unwrap();
|
||||
assert!(key_package == decoded_key_package);
|
||||
|
||||
let json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"secret_share": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"group_public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
let decoded_key_package: KeyPackage = serde_json::from_str(json).unwrap();
|
||||
assert!(key_package == decoded_key_package);
|
||||
|
||||
let invalid_json = "{}";
|
||||
assert!(serde_json::from_str::<KeyPackage>(invalid_json).is_err());
|
||||
|
||||
// Invalid identifier
|
||||
let invalid_json = r#"{
|
||||
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"secret_share": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"group_public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<KeyPackage>(invalid_json).is_err());
|
||||
|
||||
// Invalid field
|
||||
let invalid_json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"foo": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"group_public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<KeyPackage>(invalid_json).is_err());
|
||||
|
||||
// Missing field
|
||||
let invalid_json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"group_public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<KeyPackage>(invalid_json).is_err());
|
||||
|
||||
// Extra field
|
||||
let invalid_json = r#"{
|
||||
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"secret_share": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"group_public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"extra_field": 1,
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<KeyPackage>(invalid_json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_public_key_package_serialization() {
|
||||
let identifier = 42u16.try_into().unwrap();
|
||||
let serialized_element =
|
||||
hex::decode("e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
|
||||
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
|
||||
|
||||
let public_key_package = PublicKeyPackage::new(
|
||||
HashMap::from([(identifier, verifying_share)]),
|
||||
verifying_key,
|
||||
);
|
||||
|
||||
let json = serde_json::to_string_pretty(&public_key_package).unwrap();
|
||||
println!("{}", json);
|
||||
|
||||
let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(&json).unwrap();
|
||||
assert!(public_key_package == decoded_public_key_package);
|
||||
|
||||
let json = r#"{
|
||||
"signer_pubkeys": {
|
||||
"2a00000000000000000000000000000000000000000000000000000000000000": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
},
|
||||
"group_public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
let decoded_public_key_package: PublicKeyPackage = serde_json::from_str(json).unwrap();
|
||||
assert!(public_key_package == decoded_public_key_package);
|
||||
|
||||
let invalid_json = "{}";
|
||||
assert!(serde_json::from_str::<PublicKeyPackage>(invalid_json).is_err());
|
||||
|
||||
// Invalid identifier
|
||||
let invalid_json = r#"{
|
||||
"signer_pubkeys": {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
},
|
||||
"group_public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<PublicKeyPackage>(invalid_json).is_err());
|
||||
|
||||
// Invalid field
|
||||
let invalid_json = r#"{
|
||||
"signer_pubkeys": {
|
||||
"2a00000000000000000000000000000000000000000000000000000000000000": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
},
|
||||
"foo": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<PublicKeyPackage>(invalid_json).is_err());
|
||||
|
||||
// Missing field
|
||||
let invalid_json = r#"{
|
||||
"signer_pubkeys": {
|
||||
"2a00000000000000000000000000000000000000000000000000000000000000": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
},
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<PublicKeyPackage>(invalid_json).is_err());
|
||||
|
||||
// Extra field
|
||||
let invalid_json = r#"{
|
||||
"signer_pubkeys": {
|
||||
"2a00000000000000000000000000000000000000000000000000000000000000": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
},
|
||||
"group_public": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
|
||||
"extra": 1,
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<PublicKeyPackage>(invalid_json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_round1_package_serialization() {
|
||||
let identifier = 42u16.try_into().unwrap();
|
||||
let serialized_scalar: [u8; 32] =
|
||||
hex::decode("a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let serialized_element: [u8; 32] =
|
||||
hex::decode("e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let serialized_signature: [u8; 64] = serialized_element
|
||||
.iter()
|
||||
.chain(serialized_scalar.iter())
|
||||
.cloned()
|
||||
.collect::<Vec<u8>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let vss_commitment =
|
||||
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
|
||||
let signature = Signature::from_bytes(serialized_signature).unwrap();
|
||||
|
||||
let round1_package = round1::Package::new(identifier, vss_commitment, signature);
|
||||
|
||||
let json = serde_json::to_string_pretty(&round1_package).unwrap();
|
||||
println!("{}", json);
|
||||
|
||||
let decoded_round1_package: round1::Package = serde_json::from_str(&json).unwrap();
|
||||
assert!(round1_package == decoded_round1_package);
|
||||
|
||||
let json = r#"{
|
||||
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"commitment": [
|
||||
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
],
|
||||
"proof_of_knowledge": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
let decoded_round1_package: round1::Package = serde_json::from_str(json).unwrap();
|
||||
assert!(round1_package == decoded_round1_package);
|
||||
|
||||
let invalid_json = "{}";
|
||||
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
|
||||
|
||||
// Invalid identifier
|
||||
let invalid_json = r#"{
|
||||
"sender_identifier": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"commitment": [
|
||||
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
],
|
||||
"proof_of_knowledge": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
|
||||
|
||||
// Invalid field
|
||||
let invalid_json = r#"{
|
||||
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"commitment": [
|
||||
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
],
|
||||
"foo": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
|
||||
|
||||
// Missing field
|
||||
let invalid_json = r#"{
|
||||
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"commitment": [
|
||||
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
],
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
|
||||
|
||||
// Extra field
|
||||
let invalid_json = r#"{
|
||||
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"commitment": [
|
||||
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
|
||||
],
|
||||
"proof_of_knowledge": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"extra": 1,
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_round2_package_serialization() {
|
||||
let identifier = 42u16.try_into().unwrap();
|
||||
let serialized_scalar: [u8; 32] =
|
||||
hex::decode("a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
|
||||
|
||||
let round2_package = round2::Package::new(identifier, identifier, signing_share);
|
||||
|
||||
let json = serde_json::to_string_pretty(&round2_package).unwrap();
|
||||
println!("{}", json);
|
||||
|
||||
let decoded_round2_package: round2::Package = serde_json::from_str(&json).unwrap();
|
||||
assert!(round2_package == decoded_round2_package);
|
||||
|
||||
let json = r#"{
|
||||
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiver_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"secret_share": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
let decoded_round2_package: round2::Package = serde_json::from_str(json).unwrap();
|
||||
assert!(round2_package == decoded_round2_package);
|
||||
|
||||
let invalid_json = "{}";
|
||||
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
|
||||
|
||||
// Invalid identifier
|
||||
let invalid_json = r#"{
|
||||
"sender_identifier": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiver_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"secret_share": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
|
||||
|
||||
// Invalid field
|
||||
let invalid_json = r#"{
|
||||
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"foo": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"secret_share": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
|
||||
|
||||
// Missing field
|
||||
let invalid_json = r#"{
|
||||
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"secret_share": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
|
||||
|
||||
// Extra field
|
||||
let invalid_json = r#"{
|
||||
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiver_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
|
||||
"secret_share": "a0bdb9f6bf7b44c092dc285e66ee0484bce85c2d83babe03442510ab37603b0f",
|
||||
"extra": 1,
|
||||
"ciphersuite": "FROST(ristretto255, SHA-512)"
|
||||
}"#;
|
||||
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
|
||||
}
|
|
@ -24,6 +24,7 @@ features = ["nightly"]
|
|||
frost-core = { path = "../frost-core", version = "0.4.0", features = ["test-impl"] }
|
||||
k256 = { version = "0.13.0", features = ["arithmetic", "expose-field", "hash2curve"] }
|
||||
rand_core = "0.6"
|
||||
serde = { version = "1.0.160", features = ["derive"] }
|
||||
sha2 = "0.10.2"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -70,7 +70,7 @@ let message = "message to sign".as_bytes();
|
|||
# // In practice, the SigningPackage must be sent to all participants
|
||||
# // involved in the current signing (at least min_signers participants),
|
||||
# // using an authenticate channel (and confidential if the message is secret).
|
||||
let signing_package = frost::SigningPackage::new(commitments_received, message.to_vec());
|
||||
let signing_package = frost::SigningPackage::new(commitments_received, message);
|
||||
# // ANCHOR_END: round2_package
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -12,6 +12,7 @@ use k256::{
|
|||
AffinePoint, ProjectivePoint, Scalar,
|
||||
};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use frost_core::frost;
|
||||
|
@ -167,10 +168,12 @@ fn hash_to_scalar(domain: &[u8], msg: &[u8]) -> Scalar {
|
|||
const CONTEXT_STRING: &str = "FROST-secp256k1-SHA256-v11";
|
||||
|
||||
/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)]
|
||||
pub struct Secp256K1Sha256;
|
||||
|
||||
impl Ciphersuite for Secp256K1Sha256 {
|
||||
const ID: &'static str = "FROST(secp256k1, SHA-256)";
|
||||
|
||||
type Group = Secp256K1Group;
|
||||
|
||||
type HashOutput = [u8; 32];
|
||||
|
|
Loading…
Reference in New Issue