Remove pub from fields, add getters (#401)

* derive getters; remove any existing ones; use BTreeMap for signing commitments

* add recreation tests

* make tests ciphersuite-specific
This commit is contained in:
Conrado Gouvea 2023-06-23 11:07:34 -03:00 committed by GitHub
parent 47121537e8
commit c851bbb8fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1408 additions and 122 deletions

View File

@ -19,6 +19,7 @@ features = ["nightly"]
[dependencies]
byteorder = "1.4"
debugless-unwrap = "0.0.4"
derive-getters = "0.3.0"
digest = "0.10"
hex = { version = "0.4.3", features = ["serde"] }
rand_core = "0.6"

View File

@ -11,11 +11,12 @@
//! Sharing, where shares are generated using Shamir Secret Sharing.
use std::{
collections::{BTreeMap, HashMap},
collections::BTreeMap,
fmt::{self, Debug},
ops::Index,
};
use derive_getters::Getters;
#[cfg(any(test, feature = "test-impl"))]
use hex::FromHex;
@ -161,7 +162,7 @@ fn derive_interpolating_value<C: Ciphersuite>(
// Ala the sorting of B, just always sort by identifier in ascending order
//
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
for commitment in signing_package.signing_commitments() {
for commitment in signing_package.signing_commitments().values() {
if commitment.identifier == *signer_id {
continue;
}
@ -183,13 +184,13 @@ fn derive_interpolating_value<C: Ciphersuite>(
/// Generated by the coordinator of the signing operation and distributed to
/// each signing party
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, Getters)]
#[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.
signing_commitments: HashMap<Identifier<C>, round1::SigningCommitments<C>>,
/// protocol, ordered by their identifiers.
signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
/// Message which each participant will sign.
///
/// Each signer should perform protocol-specific verification on the
@ -211,6 +212,7 @@ pub struct SigningPackage<C: Ciphersuite> {
feature = "serde",
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
)]
#[getter(skip)]
ciphersuite: (),
}
@ -240,19 +242,6 @@ where
self.signing_commitments[identifier]
}
/// Get the signing commitments, sorted by the participant indices
pub fn signing_commitments(&self) -> Vec<round1::SigningCommitments<C>> {
let mut signing_commitments: Vec<round1::SigningCommitments<C>> =
self.signing_commitments.values().cloned().collect();
signing_commitments.sort_by_key(|a| a.identifier);
signing_commitments
}
/// Get the message to be signed
pub fn message(&self) -> &Vec<u8> {
&self.message
}
/// Compute the preimages to H1 to compute the per-signer binding factors
// We separate this out into its own method so it can be tested
#[cfg_attr(feature = "internals", visibility::make(pub))]
@ -272,7 +261,7 @@ where
binding_factor_input_prefix.extend_from_slice(additional_prefix);
self.signing_commitments()
.iter()
.values()
.map(|c| {
let mut binding_factor_input = vec![];
@ -336,7 +325,7 @@ where
// Ala the sorting of B, just always sort by identifier in ascending order
//
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
for commitment in signing_package.signing_commitments() {
for commitment in signing_package.signing_commitments().values() {
// The following check prevents a party from accidentally revealing their share.
// Note that the '&&' operator would be sufficient.
if identity == commitment.binding.0 || identity == commitment.hiding.0 {

View File

@ -9,6 +9,7 @@ use std::{
iter,
};
use derive_getters::Getters;
#[cfg(any(test, feature = "test-impl"))]
use hex::FromHex;
@ -298,18 +299,18 @@ where
///
/// To derive a FROST keypair, the receiver of the [`SecretShare`] *must* call
/// .into(), which under the hood also performs validation.
#[derive(Clone, Zeroize, PartialEq, Eq)]
#[derive(Clone, Zeroize, PartialEq, Eq, Getters)]
#[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)]
pub identifier: Identifier<C>,
pub(crate) identifier: Identifier<C>,
/// Secret Key.
pub value: SigningShare<C>,
pub(crate) value: SigningShare<C>,
#[zeroize(skip)]
/// The commitments to be distributed among signers.
pub commitment: VerifiableSecretSharingCommitment<C>,
pub(crate) commitment: VerifiableSecretSharingCommitment<C>,
/// Ciphersuite ID for serialization
#[cfg_attr(
feature = "serde",
@ -319,6 +320,7 @@ pub struct SecretShare<C: Ciphersuite> {
feature = "serde",
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
)]
#[getter(skip)]
ciphersuite: (),
}
@ -485,18 +487,18 @@ 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, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, Getters)]
#[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>,
pub(crate) identifier: Identifier<C>,
/// This participant's secret share.
pub secret_share: SigningShare<C>,
pub(crate) secret_share: SigningShare<C>,
/// This participant's public key.
pub public: VerifyingShare<C>,
pub(crate) public: VerifyingShare<C>,
/// The public signing key that represents the entire group.
pub group_public: VerifyingKey<C>,
pub(crate) group_public: VerifyingKey<C>,
/// Ciphersuite ID for serialization
#[cfg_attr(
feature = "serde",
@ -506,6 +508,7 @@ pub struct KeyPackage<C: Ciphersuite> {
feature = "serde",
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
)]
#[getter(skip)]
ciphersuite: (),
}
@ -528,26 +531,6 @@ where
ciphersuite: (),
}
}
/// Gets the participant identifier associated with this [`KeyPackage`].
pub fn identifier(&self) -> &Identifier<C> {
&self.identifier
}
/// Gets the participant's [`SigningShare`] associated with this [`KeyPackage`].
pub fn secret_share(&self) -> &SigningShare<C> {
&self.secret_share
}
/// Gets the participant's [`VerifyingShare`] associated with the [`SigningShare`] in this [`KeyPackage`].
pub fn public(&self) -> &VerifyingShare<C> {
&self.public
}
/// Gets the group [`VerifyingKey`] associated with the entire group in this [`KeyPackage`].
pub fn group_public(&self) -> &VerifyingKey<C> {
&self.group_public
}
}
impl<C> TryFrom<SecretShare<C>> for KeyPackage<C>
@ -581,7 +564,7 @@ where
/// group public key.
///
/// Used for verification purposes before publishing a signature.
#[derive(PartialEq, Eq)]
#[derive(PartialEq, Eq, Getters)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct PublicKeyPackage<C: Ciphersuite> {
@ -589,9 +572,9 @@ pub struct PublicKeyPackage<C: Ciphersuite> {
/// correct view of participants' public keys to perform verification before
/// publishing a signature. `signer_pubkeys` represents all signers for a
/// signing operation.
pub signer_pubkeys: HashMap<Identifier<C>, VerifyingShare<C>>,
pub(crate) signer_pubkeys: HashMap<Identifier<C>, VerifyingShare<C>>,
/// The joint public key for the entire group.
pub group_public: VerifyingKey<C>,
pub(crate) group_public: VerifyingKey<C>,
/// Ciphersuite ID for serialization
#[cfg_attr(
feature = "serde",
@ -601,6 +584,7 @@ pub struct PublicKeyPackage<C: Ciphersuite> {
feature = "serde",
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
)]
#[getter(skip)]
ciphersuite: (),
}

View File

@ -47,20 +47,22 @@ use super::{
/// DKG Round 1 structures.
pub mod round1 {
use derive_getters::Getters;
use super::*;
/// 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, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, Getters)]
#[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>,
pub(crate) sender_identifier: Identifier<C>,
/// The public commitment from the participant (C_i)
pub commitment: VerifiableSecretSharingCommitment<C>,
pub(crate) commitment: VerifiableSecretSharingCommitment<C>,
/// The proof of knowledge of the temporary secret (σ_i = (R_i, μ_i))
pub proof_of_knowledge: Signature<C>,
pub(crate) proof_of_knowledge: Signature<C>,
/// Ciphersuite ID for serialization
#[cfg_attr(
feature = "serde",
@ -70,6 +72,7 @@ pub mod round1 {
feature = "serde",
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
)]
#[getter(skip)]
pub(super) ciphersuite: (),
}
@ -101,19 +104,21 @@ pub mod round1 {
#[derive(Clone)]
pub struct SecretPackage<C: Ciphersuite> {
/// The identifier of the participant holding the secret.
pub identifier: Identifier<C>,
pub(crate) identifier: Identifier<C>,
/// Coefficients of the temporary secret polynomial for the participant.
/// These are (a_{i0}, ..., a_{i(t1)})) which define the polynomial f_i(x)
pub coefficients: Vec<Scalar<C>>,
pub(crate) coefficients: Vec<Scalar<C>>,
/// The public commitment for the participant (C_i)
pub commitment: VerifiableSecretSharingCommitment<C>,
pub(crate) commitment: VerifiableSecretSharingCommitment<C>,
/// The total number of signers.
pub max_signers: u16,
pub(crate) max_signers: u16,
}
}
/// DKG Round 2 structures.
pub mod round2 {
use derive_getters::Getters;
use super::*;
/// A package that must be sent by each participant to some other participants
@ -123,16 +128,16 @@ pub mod round2 {
/// # Security
///
/// The package must be sent on an *confidential* and *authenticated* channel.
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, Getters)]
#[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>,
pub(crate) sender_identifier: Identifier<C>,
/// The identifier of the participant what will receive the package ().
pub receiver_identifier: Identifier<C>,
pub(crate) receiver_identifier: Identifier<C>,
/// The secret share being sent.
pub secret_share: SigningShare<C>,
pub(crate) secret_share: SigningShare<C>,
/// Ciphersuite ID for serialization
#[cfg_attr(
feature = "serde",
@ -142,6 +147,7 @@ pub mod round2 {
feature = "serde",
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
)]
#[getter(skip)]
pub(super) ciphersuite: (),
}
@ -172,13 +178,13 @@ pub mod round2 {
/// This package MUST NOT be sent to other participants!
pub struct SecretPackage<C: Ciphersuite> {
/// The identifier of the participant holding the secret.
pub identifier: Identifier<C>,
pub(crate) identifier: Identifier<C>,
/// The public commitment from the participant (C_i)
pub commitment: VerifiableSecretSharingCommitment<C>,
pub(crate) commitment: VerifiableSecretSharingCommitment<C>,
/// The participant's own secret share (f_i(i)).
pub secret_share: Scalar<C>,
pub(crate) secret_share: Scalar<C>,
/// The total number of signers.
pub max_signers: u16,
pub(crate) max_signers: u16,
}
}

View File

@ -1,7 +1,11 @@
//! FROST Round 1 functionality and types
use std::fmt::{self, Debug};
use std::{
collections::BTreeMap,
fmt::{self, Debug},
};
use derive_getters::Getters;
#[cfg(any(test, feature = "test-impl"))]
use hex::FromHex;
@ -207,9 +211,9 @@ where
#[derive(Clone, Zeroize)]
pub struct SigningNonces<C: Ciphersuite> {
/// The hiding [`Nonce`].
pub hiding: Nonce<C>,
pub(crate) hiding: Nonce<C>,
/// The binding [`Nonce`].
pub binding: Nonce<C>,
pub(crate) binding: Nonce<C>,
}
impl<C> SigningNonces<C>
@ -247,16 +251,16 @@ where
///
/// This step can be batched if desired by the implementation. Each
/// SigningCommitment can be used for exactly *one* signature.
#[derive(Copy, Clone, Eq, PartialEq)]
#[derive(Copy, Clone, Eq, PartialEq, Getters)]
#[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>,
pub(crate) identifier: Identifier<C>,
/// Commitment to the hiding [`Nonce`].
pub hiding: NonceCommitment<C>,
pub(crate) hiding: NonceCommitment<C>,
/// Commitment to the binding [`Nonce`].
pub binding: NonceCommitment<C>,
pub(crate) binding: NonceCommitment<C>,
/// Ciphersuite ID for serialization
#[cfg_attr(
feature = "serde",
@ -266,6 +270,7 @@ pub struct SigningCommitments<C: Ciphersuite> {
feature = "serde",
serde(deserialize_with = "crate::ciphersuite_deserialize::<_, C>")
)]
#[getter(skip)]
ciphersuite: (),
}
@ -297,16 +302,6 @@ where
) -> GroupCommitmentShare<C> {
GroupCommitmentShare::<C>(self.hiding.0 + (self.binding.0 * binding_factor.0))
}
/// Gets the hiding [`NonceCommitment`].
pub fn hiding(&self) -> &NonceCommitment<C> {
&self.hiding
}
/// Gets the binding [`NonceCommitment`].
pub fn binding(&self) -> &NonceCommitment<C> {
&self.binding
}
}
impl<C> From<(Identifier<C>, &SigningNonces<C>)> for SigningCommitments<C>
@ -343,17 +338,11 @@ pub struct GroupCommitmentShare<C: Ciphersuite>(pub(super) Element<C>);
///
/// [`encode_group_commitment_list()`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-list-operations
pub(super) fn encode_group_commitments<C: Ciphersuite>(
signing_commitments: Vec<SigningCommitments<C>>,
signing_commitments: &BTreeMap<Identifier<C>, SigningCommitments<C>>,
) -> Vec<u8> {
// B MUST be sorted in ascending order by signer identifier.
//
// TODO: AtLeastOne or other explicitly Sorted wrapper types?
let mut sorted_signing_commitments = signing_commitments;
sorted_signing_commitments.sort_by_key(|a| a.identifier);
let mut bytes = vec![];
for item in sorted_signing_commitments {
for item in signing_commitments.values() {
bytes.extend_from_slice(item.identifier.serialize().as_ref());
bytes.extend_from_slice(<C::Group>::serialize(&item.hiding.0).as_ref());
bytes.extend_from_slice(<C::Group>::serialize(&item.binding.0).as_ref());

View File

@ -12,13 +12,13 @@ use crate::{
use crate::ScalarSerialization;
/// A representation of a single signature share used in FROST structures and messages.
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Getters)]
#[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>,
pub(crate) z_share: Scalar<C>,
}
impl<C> SignatureResponse<C>
@ -87,14 +87,14 @@ 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)]
#[derive(Clone, Copy, Eq, PartialEq, Getters)]
#[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>,
pub(crate) identifier: Identifier<C>,
/// This participant's signature over the message.
pub signature: SignatureResponse<C>,
pub(crate) signature: SignatureResponse<C>,
/// Ciphersuite ID for serialization
#[cfg_attr(
feature = "serde",
@ -120,11 +120,6 @@ where
}
}
/// Gets the participant identifier associated with this [`SignatureShare`].
pub fn identifier(&self) -> &Identifier<C> {
&self.identifier
}
/// Tests if a signature share issued by a participant is valid before
/// aggregating it into a final joint signature to publish.
///

View File

@ -108,7 +108,7 @@ let group_signature = frost::aggregate(&signing_package, &signature_shares[..],
// key (the verification key).
# // ANCHOR: verify
let is_signature_valid = pubkey_package
.group_public
.group_public()
.verify(message, &group_signature)
.is_ok();
# // ANCHOR_END: verify

View File

@ -123,7 +123,7 @@ for participant_index in 1..=max_signers {
// gets its own specific package.
for round2_package in round2_packages {
received_round2_packages
.entry(round2_package.receiver_identifier)
.entry(*round2_package.receiver_identifier())
.or_insert_with(Vec::new)
.push(round2_package);
}

View File

@ -1,3 +1,9 @@
// Required since each integration test is compiled as a separated crate,
// and each one uses only part of the module.
#![allow(dead_code)]
pub mod samples;
use ed25519_dalek::Verifier;
use frost_ed25519::*;

View File

@ -0,0 +1,131 @@
//! Generate sample, fixed instances of structs for testing.
use std::collections::HashMap;
use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_ed25519::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment,
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare},
Field, Signature, SigningPackage, VerifyingKey,
};
type C = frost_ed25519::Ed25519Sha512;
fn element1() -> Element<C> {
<C as Ciphersuite>::Group::generator()
}
fn element2() -> Element<C> {
element1() + element1()
}
fn scalar1() -> Scalar<C> {
let one = <<C as Ciphersuite>::Group as Group>::Field::one();
let two = one + one;
// To return a fixed non-small number, get the inverse of 2
<<C as Ciphersuite>::Group as Group>::Field::invert(&two)
.expect("nonzero elements have inverses")
}
/// Generate a sample SigningCommitments.
pub fn signing_commitments() -> SigningCommitments {
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,
)
}
/// Generate a sample SigningPackage.
pub fn signing_package() -> SigningPackage {
let commitments = vec![signing_commitments()];
let message = "hello world".as_bytes();
SigningPackage::new(commitments, message)
}
/// Generate a sample SignatureShare.
pub fn signature_share() -> SignatureShare {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signature_response = SignatureResponse::from_bytes(serialized_scalar).unwrap();
SignatureShare::new(identifier, signature_response)
}
/// Generate a sample SecretShare.
pub fn secret_share() -> SecretShare {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
let vss_commitment =
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
SecretShare::new(identifier, signing_share, vss_commitment)
}
/// Generate a sample KeyPackage.
pub fn key_package() -> KeyPackage {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
KeyPackage::new(identifier, signing_share, verifying_share, verifying_key)
}
/// Generate a sample PublicKeyPackage.
pub fn public_key_package() -> PublicKeyPackage {
let identifier = 42u16.try_into().unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
let signer_pubkeys = HashMap::from([(identifier, verifying_share)]);
PublicKeyPackage::new(signer_pubkeys, verifying_key)
}
/// Generate a sample round1::Package.
pub fn round1_package() -> round1::Package {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let serialized_signature = serialized_element
.as_ref()
.iter()
.chain(serialized_scalar.as_ref().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();
round1::Package::new(identifier, vss_commitment, signature)
}
/// Generate a sample round2::Package.
pub fn round2_package() -> round2::Package {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
round2::Package::new(identifier, identifier, signing_share)
}

View File

@ -0,0 +1,131 @@
//! Test for recreating packages from their components, which shows that they
//! can be serialized and deserialized as the user wishes.
use frost_ed25519::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare,
},
round1::SigningCommitments,
round2::SignatureShare,
SigningPackage,
};
mod helpers;
use helpers::samples;
/// Check if SigningCommitments can be recreated.
#[test]
fn check_signing_commitments_recreation() {
let commitments = samples::signing_commitments();
let identifier = commitments.identifier();
let hiding = commitments.hiding();
let binding = commitments.binding();
let new_commitments = SigningCommitments::new(*identifier, *hiding, *binding);
assert!(commitments == new_commitments);
}
/// Check if SigningPackage can be recreated.
#[test]
fn check_signing_package_recreation() {
let signing_package = samples::signing_package();
let commitments = signing_package
.signing_commitments()
.values()
.cloned()
.collect();
let message = signing_package.message();
let new_signing_package = SigningPackage::new(commitments, message);
assert!(signing_package == new_signing_package);
}
/// Check if SignatureShare can be recreated.
#[test]
fn check_signature_share_recreation() {
let signature_share = samples::signature_share();
let identifier = signature_share.identifier();
let signature_response = signature_share.signature();
let new_signature_share = SignatureShare::new(*identifier, *signature_response);
assert!(signature_share == new_signature_share);
}
/// Check if SecretShare can be recreated.
#[test]
fn check_secret_share_recreation() {
let secret_share = samples::secret_share();
let identifier = secret_share.identifier();
let value = secret_share.value();
let commitment = secret_share.commitment();
let new_secret_share = SecretShare::new(*identifier, *value, commitment.clone());
assert!(secret_share == new_secret_share);
}
/// Check if KeyPackage can be recreated.
#[test]
fn check_key_package_recreation() {
let key_package = samples::key_package();
let identifier = key_package.identifier();
let signing_share = key_package.secret_share();
let verifying_share = key_package.public();
let verifying_key = key_package.group_public();
let new_key_package = KeyPackage::new(
*identifier,
*signing_share,
*verifying_share,
*verifying_key,
);
assert!(key_package == new_key_package);
}
/// Check if PublicKeyPackage can be recreated.
#[test]
fn check_public_key_package_recreation() {
let public_key_package = samples::public_key_package();
let signer_pubkeys = public_key_package.signer_pubkeys();
let verifying_key = public_key_package.group_public();
let new_public_key_package = PublicKeyPackage::new(signer_pubkeys.clone(), *verifying_key);
assert!(public_key_package == new_public_key_package);
}
/// Check if round1::Package can be recreated.
#[test]
fn check_round1_package_recreation() {
let round1_package = samples::round1_package();
let identifier = round1_package.sender_identifier();
let vss_commitment = round1_package.commitment();
let signature = round1_package.proof_of_knowledge();
let new_round1_package = round1::Package::new(*identifier, vss_commitment.clone(), *signature);
assert!(round1_package == new_round1_package);
}
/// Check if round2::Package can be recreated.
#[test]
fn check_round2_package_recreation() {
let round2_package = samples::round2_package();
let sender_identifier = round2_package.sender_identifier();
let receiver_identifier = round2_package.receiver_identifier();
let signing_share = round2_package.secret_share();
let new_round2_package =
round2::Package::new(*sender_identifier, *receiver_identifier, *signing_share);
assert!(round2_package == new_round2_package);
}

View File

@ -108,7 +108,7 @@ let group_signature = frost::aggregate(&signing_package, &signature_shares[..],
// key (the verification key).
# // ANCHOR: verify
let is_signature_valid = pubkey_package
.group_public
.group_public()
.verify(message, &group_signature)
.is_ok();
# // ANCHOR_END: verify

View File

@ -123,7 +123,7 @@ for participant_index in 1..=max_signers {
// gets its own specific package.
for round2_package in round2_packages {
received_round2_packages
.entry(round2_package.receiver_identifier)
.entry(*round2_package.receiver_identifier())
.or_insert_with(Vec::new)
.push(round2_package);
}

View File

@ -0,0 +1 @@
pub mod samples;

View File

@ -0,0 +1,131 @@
//! Generate sample, fixed instances of structs for testing.
use std::collections::HashMap;
use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_ed448::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment,
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare},
Field, Signature, SigningPackage, VerifyingKey,
};
type C = frost_ed448::Ed448Shake256;
fn element1() -> Element<C> {
<C as Ciphersuite>::Group::generator()
}
fn element2() -> Element<C> {
element1() + element1()
}
fn scalar1() -> Scalar<C> {
let one = <<C as Ciphersuite>::Group as Group>::Field::one();
let two = one + one;
// To return a fixed non-small number, get the inverse of 2
<<C as Ciphersuite>::Group as Group>::Field::invert(&two)
.expect("nonzero elements have inverses")
}
/// Generate a sample SigningCommitments.
pub fn signing_commitments() -> SigningCommitments {
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,
)
}
/// Generate a sample SigningPackage.
pub fn signing_package() -> SigningPackage {
let commitments = vec![signing_commitments()];
let message = "hello world".as_bytes();
SigningPackage::new(commitments, message)
}
/// Generate a sample SignatureShare.
pub fn signature_share() -> SignatureShare {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signature_response = SignatureResponse::from_bytes(serialized_scalar).unwrap();
SignatureShare::new(identifier, signature_response)
}
/// Generate a sample SecretShare.
pub fn secret_share() -> SecretShare {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
let vss_commitment =
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
SecretShare::new(identifier, signing_share, vss_commitment)
}
/// Generate a sample KeyPackage.
pub fn key_package() -> KeyPackage {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
KeyPackage::new(identifier, signing_share, verifying_share, verifying_key)
}
/// Generate a sample PublicKeyPackage.
pub fn public_key_package() -> PublicKeyPackage {
let identifier = 42u16.try_into().unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
let signer_pubkeys = HashMap::from([(identifier, verifying_share)]);
PublicKeyPackage::new(signer_pubkeys, verifying_key)
}
/// Generate a sample round1::Package.
pub fn round1_package() -> round1::Package {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let serialized_signature = serialized_element
.as_ref()
.iter()
.chain(serialized_scalar.as_ref().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();
round1::Package::new(identifier, vss_commitment, signature)
}
/// Generate a sample round2::Package.
pub fn round2_package() -> round2::Package {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
round2::Package::new(identifier, identifier, signing_share)
}

View File

@ -0,0 +1,131 @@
//! Test for recreating packages from their components, which shows that they
//! can be serialized and deserialized as the user wishes.
use frost_ed448::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare,
},
round1::SigningCommitments,
round2::SignatureShare,
SigningPackage,
};
mod helpers;
use helpers::samples;
/// Check if SigningCommitments can be recreated.
#[test]
fn check_signing_commitments_recreation() {
let commitments = samples::signing_commitments();
let identifier = commitments.identifier();
let hiding = commitments.hiding();
let binding = commitments.binding();
let new_commitments = SigningCommitments::new(*identifier, *hiding, *binding);
assert!(commitments == new_commitments);
}
/// Check if SigningPackage can be recreated.
#[test]
fn check_signing_package_recreation() {
let signing_package = samples::signing_package();
let commitments = signing_package
.signing_commitments()
.values()
.cloned()
.collect();
let message = signing_package.message();
let new_signing_package = SigningPackage::new(commitments, message);
assert!(signing_package == new_signing_package);
}
/// Check if SignatureShare can be recreated.
#[test]
fn check_signature_share_recreation() {
let signature_share = samples::signature_share();
let identifier = signature_share.identifier();
let signature_response = signature_share.signature();
let new_signature_share = SignatureShare::new(*identifier, *signature_response);
assert!(signature_share == new_signature_share);
}
/// Check if SecretShare can be recreated.
#[test]
fn check_secret_share_recreation() {
let secret_share = samples::secret_share();
let identifier = secret_share.identifier();
let value = secret_share.value();
let commitment = secret_share.commitment();
let new_secret_share = SecretShare::new(*identifier, *value, commitment.clone());
assert!(secret_share == new_secret_share);
}
/// Check if KeyPackage can be recreated.
#[test]
fn check_key_package_recreation() {
let key_package = samples::key_package();
let identifier = key_package.identifier();
let signing_share = key_package.secret_share();
let verifying_share = key_package.public();
let verifying_key = key_package.group_public();
let new_key_package = KeyPackage::new(
*identifier,
*signing_share,
*verifying_share,
*verifying_key,
);
assert!(key_package == new_key_package);
}
/// Check if PublicKeyPackage can be recreated.
#[test]
fn check_public_key_package_recreation() {
let public_key_package = samples::public_key_package();
let signer_pubkeys = public_key_package.signer_pubkeys();
let verifying_key = public_key_package.group_public();
let new_public_key_package = PublicKeyPackage::new(signer_pubkeys.clone(), *verifying_key);
assert!(public_key_package == new_public_key_package);
}
/// Check if round1::Package can be recreated.
#[test]
fn check_round1_package_recreation() {
let round1_package = samples::round1_package();
let identifier = round1_package.sender_identifier();
let vss_commitment = round1_package.commitment();
let signature = round1_package.proof_of_knowledge();
let new_round1_package = round1::Package::new(*identifier, vss_commitment.clone(), *signature);
assert!(round1_package == new_round1_package);
}
/// Check if round2::Package can be recreated.
#[test]
fn check_round2_package_recreation() {
let round2_package = samples::round2_package();
let sender_identifier = round2_package.sender_identifier();
let receiver_identifier = round2_package.receiver_identifier();
let signing_share = round2_package.secret_share();
let new_round2_package =
round2::Package::new(*sender_identifier, *receiver_identifier, *signing_share);
assert!(round2_package == new_round2_package);
}

View File

@ -108,7 +108,7 @@ let group_signature = frost::aggregate(&signing_package, &signature_shares[..],
// key (the verification key).
# // ANCHOR: verify
let is_signature_valid = pubkey_package
.group_public
.group_public()
.verify(message, &group_signature)
.is_ok();
# // ANCHOR_END: verify

View File

@ -123,7 +123,7 @@ for participant_index in 1..=max_signers {
// gets its own specific package.
for round2_package in round2_packages {
received_round2_packages
.entry(round2_package.receiver_identifier)
.entry(*round2_package.receiver_identifier())
.or_insert_with(Vec::new)
.push(round2_package);
}

View File

@ -0,0 +1 @@
pub mod samples;

View File

@ -0,0 +1,131 @@
//! Generate sample, fixed instances of structs for testing.
use std::collections::HashMap;
use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_p256::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment,
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare},
Field, Signature, SigningPackage, VerifyingKey,
};
type C = frost_p256::P256Sha256;
fn element1() -> Element<C> {
<C as Ciphersuite>::Group::generator()
}
fn element2() -> Element<C> {
element1() + element1()
}
fn scalar1() -> Scalar<C> {
let one = <<C as Ciphersuite>::Group as Group>::Field::one();
let two = one + one;
// To return a fixed non-small number, get the inverse of 2
<<C as Ciphersuite>::Group as Group>::Field::invert(&two)
.expect("nonzero elements have inverses")
}
/// Generate a sample SigningCommitments.
pub fn signing_commitments() -> SigningCommitments {
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,
)
}
/// Generate a sample SigningPackage.
pub fn signing_package() -> SigningPackage {
let commitments = vec![signing_commitments()];
let message = "hello world".as_bytes();
SigningPackage::new(commitments, message)
}
/// Generate a sample SignatureShare.
pub fn signature_share() -> SignatureShare {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signature_response = SignatureResponse::from_bytes(serialized_scalar).unwrap();
SignatureShare::new(identifier, signature_response)
}
/// Generate a sample SecretShare.
pub fn secret_share() -> SecretShare {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
let vss_commitment =
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
SecretShare::new(identifier, signing_share, vss_commitment)
}
/// Generate a sample KeyPackage.
pub fn key_package() -> KeyPackage {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
KeyPackage::new(identifier, signing_share, verifying_share, verifying_key)
}
/// Generate a sample PublicKeyPackage.
pub fn public_key_package() -> PublicKeyPackage {
let identifier = 42u16.try_into().unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
let signer_pubkeys = HashMap::from([(identifier, verifying_share)]);
PublicKeyPackage::new(signer_pubkeys, verifying_key)
}
/// Generate a sample round1::Package.
pub fn round1_package() -> round1::Package {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let serialized_signature = serialized_element
.as_ref()
.iter()
.chain(serialized_scalar.as_ref().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();
round1::Package::new(identifier, vss_commitment, signature)
}
/// Generate a sample round2::Package.
pub fn round2_package() -> round2::Package {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
round2::Package::new(identifier, identifier, signing_share)
}

View File

@ -0,0 +1,131 @@
//! Test for recreating packages from their components, which shows that they
//! can be serialized and deserialized as the user wishes.
use frost_p256::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare,
},
round1::SigningCommitments,
round2::SignatureShare,
SigningPackage,
};
mod helpers;
use helpers::samples;
/// Check if SigningCommitments can be recreated.
#[test]
fn check_signing_commitments_recreation() {
let commitments = samples::signing_commitments();
let identifier = commitments.identifier();
let hiding = commitments.hiding();
let binding = commitments.binding();
let new_commitments = SigningCommitments::new(*identifier, *hiding, *binding);
assert!(commitments == new_commitments);
}
/// Check if SigningPackage can be recreated.
#[test]
fn check_signing_package_recreation() {
let signing_package = samples::signing_package();
let commitments = signing_package
.signing_commitments()
.values()
.cloned()
.collect();
let message = signing_package.message();
let new_signing_package = SigningPackage::new(commitments, message);
assert!(signing_package == new_signing_package);
}
/// Check if SignatureShare can be recreated.
#[test]
fn check_signature_share_recreation() {
let signature_share = samples::signature_share();
let identifier = signature_share.identifier();
let signature_response = signature_share.signature();
let new_signature_share = SignatureShare::new(*identifier, *signature_response);
assert!(signature_share == new_signature_share);
}
/// Check if SecretShare can be recreated.
#[test]
fn check_secret_share_recreation() {
let secret_share = samples::secret_share();
let identifier = secret_share.identifier();
let value = secret_share.value();
let commitment = secret_share.commitment();
let new_secret_share = SecretShare::new(*identifier, *value, commitment.clone());
assert!(secret_share == new_secret_share);
}
/// Check if KeyPackage can be recreated.
#[test]
fn check_key_package_recreation() {
let key_package = samples::key_package();
let identifier = key_package.identifier();
let signing_share = key_package.secret_share();
let verifying_share = key_package.public();
let verifying_key = key_package.group_public();
let new_key_package = KeyPackage::new(
*identifier,
*signing_share,
*verifying_share,
*verifying_key,
);
assert!(key_package == new_key_package);
}
/// Check if PublicKeyPackage can be recreated.
#[test]
fn check_public_key_package_recreation() {
let public_key_package = samples::public_key_package();
let signer_pubkeys = public_key_package.signer_pubkeys();
let verifying_key = public_key_package.group_public();
let new_public_key_package = PublicKeyPackage::new(signer_pubkeys.clone(), *verifying_key);
assert!(public_key_package == new_public_key_package);
}
/// Check if round1::Package can be recreated.
#[test]
fn check_round1_package_recreation() {
let round1_package = samples::round1_package();
let identifier = round1_package.sender_identifier();
let vss_commitment = round1_package.commitment();
let signature = round1_package.proof_of_knowledge();
let new_round1_package = round1::Package::new(*identifier, vss_commitment.clone(), *signature);
assert!(round1_package == new_round1_package);
}
/// Check if round2::Package can be recreated.
#[test]
fn check_round2_package_recreation() {
let round2_package = samples::round2_package();
let sender_identifier = round2_package.sender_identifier();
let receiver_identifier = round2_package.receiver_identifier();
let signing_share = round2_package.secret_share();
let new_round2_package =
round2::Package::new(*sender_identifier, *receiver_identifier, *signing_share);
assert!(round2_package == new_round2_package);
}

View File

@ -34,7 +34,7 @@ pub fn sign<C: Ciphersuite>(
key_package: &frost::keys::KeyPackage<C>,
randomizer_point: &<C::Group as Group>::Element,
) -> Result<frost::round2::SignatureShare<C>, Error<C>> {
let public_key = key_package.group_public.to_element() + *randomizer_point;
let public_key = key_package.group_public().to_element() + *randomizer_point;
// Encodes the signing commitment list produced in round one as part of generating [`Rho`], the
// binding factor.
@ -43,7 +43,7 @@ pub fn sign<C: Ciphersuite>(
<C::Group as Group>::serialize(randomizer_point).as_ref(),
);
let rho: frost::BindingFactor<C> = binding_factor_list[key_package.identifier].clone();
let rho: frost::BindingFactor<C> = binding_factor_list[*key_package.identifier()].clone();
// Compute the group commitment from signing commitments produced in round one.
let group_commitment = frost::compute_group_commitment(signing_package, &binding_factor_list)?;
@ -126,7 +126,7 @@ where
let mut z = <<C::Group as Group>::Field as Field>::zero();
for signature_share in signature_shares {
z = z + signature_share.signature.z_share;
z = z + *signature_share.signature().z_share();
}
z = z + challenge.clone().to_scalar() * randomized_params.randomizer;
@ -145,19 +145,19 @@ where
// Look up the public key for this signer, where `signer_pubkey` = _G.ScalarBaseMult(s[i])_,
// and where s[i] is a secret share of the constant term of _f_, the secret polynomial.
let signer_pubkey = pubkeys
.signer_pubkeys
.get(&signature_share.identifier)
.signer_pubkeys()
.get(signature_share.identifier())
.unwrap();
// Compute Lagrange coefficient.
let lambda_i =
frost::derive_interpolating_value(&signature_share.identifier, signing_package)?;
frost::derive_interpolating_value(signature_share.identifier(), signing_package)?;
let binding_factor = binding_factor_list[signature_share.identifier].clone();
let binding_factor = binding_factor_list[*signature_share.identifier()].clone();
// Compute the commitment share.
let R_share = signing_package
.signing_commitment(&signature_share.identifier)
.signing_commitment(signature_share.identifier())
.to_group_commitment_share(&binding_factor);
// Compute relation values to verify this signature share.
@ -193,7 +193,7 @@ where
let randomizer = <<C::Group as Group>::Field as Field>::random(&mut rng);
let randomizer_point = <C::Group as Group>::generator() * randomizer;
let group_public_point = public_key_package.group_public.to_element();
let group_public_point = public_key_package.group_public().to_element();
let randomized_group_public_point = group_public_point + randomizer_point;
let randomized_group_public_key = VerifyingKey::new(randomized_group_public_point);

View File

@ -108,7 +108,7 @@ let group_signature = frost::aggregate(&signing_package, &signature_shares[..],
// key (the verification key).
# // ANCHOR: verify
let is_signature_valid = pubkey_package
.group_public
.group_public()
.verify(message, &group_signature)
.is_ok();
# // ANCHOR_END: verify

View File

@ -123,7 +123,7 @@ for participant_index in 1..=max_signers {
// gets its own specific package.
for round2_package in round2_packages {
received_round2_packages
.entry(round2_package.receiver_identifier)
.entry(*round2_package.receiver_identifier())
.or_insert_with(Vec::new)
.push(round2_package);
}

View File

@ -0,0 +1 @@
pub mod samples;

View File

@ -0,0 +1,131 @@
//! Generate sample, fixed instances of structs for testing.
use std::collections::HashMap;
use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_ristretto255::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment,
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare},
Field, Signature, SigningPackage, VerifyingKey,
};
type C = frost_ristretto255::Ristretto255Sha512;
fn element1() -> Element<C> {
<C as Ciphersuite>::Group::generator()
}
fn element2() -> Element<C> {
element1() + element1()
}
fn scalar1() -> Scalar<C> {
let one = <<C as Ciphersuite>::Group as Group>::Field::one();
let two = one + one;
// To return a fixed non-small number, get the inverse of 2
<<C as Ciphersuite>::Group as Group>::Field::invert(&two)
.expect("nonzero elements have inverses")
}
/// Generate a sample SigningCommitments.
pub fn signing_commitments() -> SigningCommitments {
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,
)
}
/// Generate a sample SigningPackage.
pub fn signing_package() -> SigningPackage {
let commitments = vec![signing_commitments()];
let message = "hello world".as_bytes();
SigningPackage::new(commitments, message)
}
/// Generate a sample SignatureShare.
pub fn signature_share() -> SignatureShare {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signature_response = SignatureResponse::from_bytes(serialized_scalar).unwrap();
SignatureShare::new(identifier, signature_response)
}
/// Generate a sample SecretShare.
pub fn secret_share() -> SecretShare {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
let vss_commitment =
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
SecretShare::new(identifier, signing_share, vss_commitment)
}
/// Generate a sample KeyPackage.
pub fn key_package() -> KeyPackage {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
KeyPackage::new(identifier, signing_share, verifying_share, verifying_key)
}
/// Generate a sample PublicKeyPackage.
pub fn public_key_package() -> PublicKeyPackage {
let identifier = 42u16.try_into().unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
let signer_pubkeys = HashMap::from([(identifier, verifying_share)]);
PublicKeyPackage::new(signer_pubkeys, verifying_key)
}
/// Generate a sample round1::Package.
pub fn round1_package() -> round1::Package {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let serialized_signature = serialized_element
.as_ref()
.iter()
.chain(serialized_scalar.as_ref().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();
round1::Package::new(identifier, vss_commitment, signature)
}
/// Generate a sample round2::Package.
pub fn round2_package() -> round2::Package {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
round2::Package::new(identifier, identifier, signing_share)
}

View File

@ -0,0 +1,131 @@
//! Test for recreating packages from their components, which shows that they
//! can be serialized and deserialized as the user wishes.
use frost_ristretto255::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare,
},
round1::SigningCommitments,
round2::SignatureShare,
SigningPackage,
};
mod helpers;
use helpers::samples;
/// Check if SigningCommitments can be recreated.
#[test]
fn check_signing_commitments_recreation() {
let commitments = samples::signing_commitments();
let identifier = commitments.identifier();
let hiding = commitments.hiding();
let binding = commitments.binding();
let new_commitments = SigningCommitments::new(*identifier, *hiding, *binding);
assert!(commitments == new_commitments);
}
/// Check if SigningPackage can be recreated.
#[test]
fn check_signing_package_recreation() {
let signing_package = samples::signing_package();
let commitments = signing_package
.signing_commitments()
.values()
.cloned()
.collect();
let message = signing_package.message();
let new_signing_package = SigningPackage::new(commitments, message);
assert!(signing_package == new_signing_package);
}
/// Check if SignatureShare can be recreated.
#[test]
fn check_signature_share_recreation() {
let signature_share = samples::signature_share();
let identifier = signature_share.identifier();
let signature_response = signature_share.signature();
let new_signature_share = SignatureShare::new(*identifier, *signature_response);
assert!(signature_share == new_signature_share);
}
/// Check if SecretShare can be recreated.
#[test]
fn check_secret_share_recreation() {
let secret_share = samples::secret_share();
let identifier = secret_share.identifier();
let value = secret_share.value();
let commitment = secret_share.commitment();
let new_secret_share = SecretShare::new(*identifier, *value, commitment.clone());
assert!(secret_share == new_secret_share);
}
/// Check if KeyPackage can be recreated.
#[test]
fn check_key_package_recreation() {
let key_package = samples::key_package();
let identifier = key_package.identifier();
let signing_share = key_package.secret_share();
let verifying_share = key_package.public();
let verifying_key = key_package.group_public();
let new_key_package = KeyPackage::new(
*identifier,
*signing_share,
*verifying_share,
*verifying_key,
);
assert!(key_package == new_key_package);
}
/// Check if PublicKeyPackage can be recreated.
#[test]
fn check_public_key_package_recreation() {
let public_key_package = samples::public_key_package();
let signer_pubkeys = public_key_package.signer_pubkeys();
let verifying_key = public_key_package.group_public();
let new_public_key_package = PublicKeyPackage::new(signer_pubkeys.clone(), *verifying_key);
assert!(public_key_package == new_public_key_package);
}
/// Check if round1::Package can be recreated.
#[test]
fn check_round1_package_recreation() {
let round1_package = samples::round1_package();
let identifier = round1_package.sender_identifier();
let vss_commitment = round1_package.commitment();
let signature = round1_package.proof_of_knowledge();
let new_round1_package = round1::Package::new(*identifier, vss_commitment.clone(), *signature);
assert!(round1_package == new_round1_package);
}
/// Check if round2::Package can be recreated.
#[test]
fn check_round2_package_recreation() {
let round2_package = samples::round2_package();
let sender_identifier = round2_package.sender_identifier();
let receiver_identifier = round2_package.receiver_identifier();
let signing_share = round2_package.secret_share();
let new_round2_package =
round2::Package::new(*sender_identifier, *receiver_identifier, *signing_share);
assert!(round2_package == new_round2_package);
}

View File

@ -108,7 +108,7 @@ let group_signature = frost::aggregate(&signing_package, &signature_shares[..],
// key (the verification key).
# // ANCHOR: verify
let is_signature_valid = pubkey_package
.group_public
.group_public()
.verify(message, &group_signature)
.is_ok();
# // ANCHOR_END: verify

View File

@ -123,7 +123,7 @@ for participant_index in 1..=max_signers {
// gets its own specific package.
for round2_package in round2_packages {
received_round2_packages
.entry(round2_package.receiver_identifier)
.entry(*round2_package.receiver_identifier())
.or_insert_with(Vec::new)
.push(round2_package);
}

View File

@ -0,0 +1 @@
pub mod samples;

View File

@ -0,0 +1,131 @@
//! Generate sample, fixed instances of structs for testing.
use std::collections::HashMap;
use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_secp256k1::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare, SigningShare, VerifiableSecretSharingCommitment,
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare},
Field, Signature, SigningPackage, VerifyingKey,
};
type C = frost_secp256k1::Secp256K1Sha256;
fn element1() -> Element<C> {
<C as Ciphersuite>::Group::generator()
}
fn element2() -> Element<C> {
element1() + element1()
}
fn scalar1() -> Scalar<C> {
let one = <<C as Ciphersuite>::Group as Group>::Field::one();
let two = one + one;
// To return a fixed non-small number, get the inverse of 2
<<C as Ciphersuite>::Group as Group>::Field::invert(&two)
.expect("nonzero elements have inverses")
}
/// Generate a sample SigningCommitments.
pub fn signing_commitments() -> SigningCommitments {
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,
)
}
/// Generate a sample SigningPackage.
pub fn signing_package() -> SigningPackage {
let commitments = vec![signing_commitments()];
let message = "hello world".as_bytes();
SigningPackage::new(commitments, message)
}
/// Generate a sample SignatureShare.
pub fn signature_share() -> SignatureShare {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signature_response = SignatureResponse::from_bytes(serialized_scalar).unwrap();
SignatureShare::new(identifier, signature_response)
}
/// Generate a sample SecretShare.
pub fn secret_share() -> SecretShare {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
let vss_commitment =
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
SecretShare::new(identifier, signing_share, vss_commitment)
}
/// Generate a sample KeyPackage.
pub fn key_package() -> KeyPackage {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
KeyPackage::new(identifier, signing_share, verifying_share, verifying_key)
}
/// Generate a sample PublicKeyPackage.
pub fn public_key_package() -> PublicKeyPackage {
let identifier = 42u16.try_into().unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_share = VerifyingShare::from_bytes(serialized_element).unwrap();
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let verifying_key = VerifyingKey::from_bytes(serialized_element).unwrap();
let signer_pubkeys = HashMap::from([(identifier, verifying_share)]);
PublicKeyPackage::new(signer_pubkeys, verifying_key)
}
/// Generate a sample round1::Package.
pub fn round1_package() -> round1::Package {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let serialized_signature = serialized_element
.as_ref()
.iter()
.chain(serialized_scalar.as_ref().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();
round1::Package::new(identifier, vss_commitment, signature)
}
/// Generate a sample round2::Package.
pub fn round2_package() -> round2::Package {
let identifier = 42u16.try_into().unwrap();
let serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signing_share = SigningShare::from_bytes(serialized_scalar).unwrap();
round2::Package::new(identifier, identifier, signing_share)
}

View File

@ -0,0 +1,131 @@
//! Test for recreating packages from their components, which shows that they
//! can be serialized and deserialized as the user wishes.
use frost_secp256k1::{
keys::{
dkg::{round1, round2},
KeyPackage, PublicKeyPackage, SecretShare,
},
round1::SigningCommitments,
round2::SignatureShare,
SigningPackage,
};
mod helpers;
use helpers::samples;
/// Check if SigningCommitments can be recreated.
#[test]
fn check_signing_commitments_recreation() {
let commitments = samples::signing_commitments();
let identifier = commitments.identifier();
let hiding = commitments.hiding();
let binding = commitments.binding();
let new_commitments = SigningCommitments::new(*identifier, *hiding, *binding);
assert!(commitments == new_commitments);
}
/// Check if SigningPackage can be recreated.
#[test]
fn check_signing_package_recreation() {
let signing_package = samples::signing_package();
let commitments = signing_package
.signing_commitments()
.values()
.cloned()
.collect();
let message = signing_package.message();
let new_signing_package = SigningPackage::new(commitments, message);
assert!(signing_package == new_signing_package);
}
/// Check if SignatureShare can be recreated.
#[test]
fn check_signature_share_recreation() {
let signature_share = samples::signature_share();
let identifier = signature_share.identifier();
let signature_response = signature_share.signature();
let new_signature_share = SignatureShare::new(*identifier, *signature_response);
assert!(signature_share == new_signature_share);
}
/// Check if SecretShare can be recreated.
#[test]
fn check_secret_share_recreation() {
let secret_share = samples::secret_share();
let identifier = secret_share.identifier();
let value = secret_share.value();
let commitment = secret_share.commitment();
let new_secret_share = SecretShare::new(*identifier, *value, commitment.clone());
assert!(secret_share == new_secret_share);
}
/// Check if KeyPackage can be recreated.
#[test]
fn check_key_package_recreation() {
let key_package = samples::key_package();
let identifier = key_package.identifier();
let signing_share = key_package.secret_share();
let verifying_share = key_package.public();
let verifying_key = key_package.group_public();
let new_key_package = KeyPackage::new(
*identifier,
*signing_share,
*verifying_share,
*verifying_key,
);
assert!(key_package == new_key_package);
}
/// Check if PublicKeyPackage can be recreated.
#[test]
fn check_public_key_package_recreation() {
let public_key_package = samples::public_key_package();
let signer_pubkeys = public_key_package.signer_pubkeys();
let verifying_key = public_key_package.group_public();
let new_public_key_package = PublicKeyPackage::new(signer_pubkeys.clone(), *verifying_key);
assert!(public_key_package == new_public_key_package);
}
/// Check if round1::Package can be recreated.
#[test]
fn check_round1_package_recreation() {
let round1_package = samples::round1_package();
let identifier = round1_package.sender_identifier();
let vss_commitment = round1_package.commitment();
let signature = round1_package.proof_of_knowledge();
let new_round1_package = round1::Package::new(*identifier, vss_commitment.clone(), *signature);
assert!(round1_package == new_round1_package);
}
/// Check if round2::Package can be recreated.
#[test]
fn check_round2_package_recreation() {
let round2_package = samples::round2_package();
let sender_identifier = round2_package.sender_identifier();
let receiver_identifier = round2_package.receiver_identifier();
let signing_share = round2_package.secret_share();
let new_round2_package =
round2::Package::new(*sender_identifier, *receiver_identifier, *signing_share);
assert!(round2_package == new_round2_package);
}

View File

@ -286,6 +286,8 @@ fn main() -> ExitCode {
"src/tests/coefficient_commitment.rs",
"src/tests/proptests.rs",
"src/tests/vss_commitment.rs",
"tests/recreation_tests.rs",
"tests/helpers/samples.rs",
] {
replaced |= copy_and_replace(
format!("{original_folder}/{filename}").as_str(),