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:
Conrado Gouvea 2023-06-23 06:58:22 -03:00 committed by GitHub
parent 8b09d9d698
commit 47121537e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1289 additions and 55 deletions

View File

@ -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

View File

@ -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

View File

@ -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"]

View File

@ -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),

View File

@ -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: (),
}
}

View File

@ -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>

View File

@ -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: (),
});
}

View File

@ -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))

View File

@ -129,5 +129,6 @@ pub fn repair_share_step_3<C: Ciphersuite>(
identifier,
value: SigningShare(share),
commitment: commitment.clone(),
ciphersuite: (),
}
}

View File

@ -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: (),
}
}
}

View File

@ -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: (),
}
}

View File

@ -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(())
}
}

View File

@ -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")

View File

@ -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)

View File

@ -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);
}

View File

@ -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,

View File

@ -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

View File

@ -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
////////////////////////////////////////////////////////////////////////////

View File

@ -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];

View File

@ -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
////////////////////////////////////////////////////////////////////////////

View File

@ -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];

View File

@ -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
////////////////////////////////////////////////////////////////////////////

View File

@ -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];

View File

@ -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

View File

@ -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

View File

@ -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
////////////////////////////////////////////////////////////////////////////

View File

@ -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];

View File

@ -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());
}

View File

@ -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]

View File

@ -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
////////////////////////////////////////////////////////////////////////////

View File

@ -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];