Merge remote-tracking branch 'origin/main' into update-vectors-format

This commit is contained in:
Conrado Gouvea 2023-07-05 09:08:18 -03:00
commit f76ce8b7c3
46 changed files with 474 additions and 879 deletions

View File

@ -6,6 +6,22 @@ Entries are listed in reverse chronological order.
## 0.6.0
* The following structs had a `Identifier` field removed, which affects
how they are encoded and instantiated:
* `dkg::round1::Package`
* `dkg::round2::Package`
* `SigningCommitments`
* `SignatureShare`
* The following functions and methods changed parameters from `Vec` to `HashMap`
so that callers need to indicate the identifier of the source of each
value being passed:
* `aggregate()`
* `dkg::part2()`
* `dkg::part3()`
* `SigningPackage::new()`
* `commit()` and `preprocess()` no longer take an identifier as input
* `SignatureResponse` was removed. `SignatureShare` can now be encoded directly with
`from/to_bytes()`.
* rename all `to_bytes()`/`from_bytes()` to `serialize()`/`deserialize()`
## Released

View File

@ -1,6 +1,6 @@
//! Ciphersuite-generic benchmark functions.
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use criterion::{BenchmarkId, Criterion, Throughput};
use rand_core::{CryptoRng, RngCore};
@ -126,7 +126,6 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
b.iter(|| {
let participant_identifier = 1u16.try_into().expect("should be nonzero");
frost::round1::commit(
participant_identifier,
key_packages
.get(&participant_identifier)
.unwrap()
@ -138,12 +137,11 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
);
let mut nonces: HashMap<_, _> = HashMap::new();
let mut commitments: HashMap<_, _> = HashMap::new();
let mut commitments: BTreeMap<_, _> = BTreeMap::new();
for participant_index in 1..=min_signers {
let participant_identifier = participant_index.try_into().expect("should be nonzero");
let (nonce, commitment) = frost::round1::commit(
participant_identifier,
key_packages
.get(&participant_identifier)
.unwrap()
@ -155,8 +153,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);
let signing_package = frost::SigningPackage::new(commitments, message);
group.bench_with_input(
BenchmarkId::new("Round 2", min_signers),
@ -175,13 +172,13 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
},
);
let mut signature_shares = Vec::new();
let mut signature_shares = HashMap::new();
for participant_identifier in nonces.keys() {
let key_package = key_packages.get(participant_identifier).unwrap();
let nonces_to_use = &nonces.get(participant_identifier).unwrap();
let signature_share =
frost::round2::sign(&signing_package, nonces_to_use, key_package).unwrap();
signature_shares.push(signature_share);
signature_shares.insert(*key_package.identifier(), signature_share);
}
group.bench_with_input(
@ -189,7 +186,7 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
&(signing_package.clone(), signature_shares.clone(), pubkeys),
|b, (signing_package, signature_shares, pubkeys)| {
b.iter(|| {
frost::aggregate(signing_package, &signature_shares[..], pubkeys).unwrap();
frost::aggregate(signing_package, signature_shares, pubkeys).unwrap();
})
},
);

View File

@ -11,7 +11,7 @@
//! Sharing, where shares are generated using Shamir Secret Sharing.
use std::{
collections::BTreeMap,
collections::{BTreeMap, HashMap},
fmt::{self, Debug},
ops::Index,
};
@ -162,14 +162,14 @@ 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().values() {
if commitment.identifier == *signer_id {
for commitment_identifier in signing_package.signing_commitments().keys() {
if *commitment_identifier == *signer_id {
continue;
}
num *= commitment.identifier;
num *= *commitment_identifier;
den *= commitment.identifier - *signer_id;
den *= *commitment_identifier - *signer_id;
}
if den == zero {
@ -189,7 +189,7 @@ fn derive_interpolating_value<C: Ciphersuite>(
#[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, ordered by their identifiers.
/// protocol.
signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
/// Message which each participant will sign.
///
@ -224,14 +224,11 @@ where
///
/// The `signing_commitments` are sorted by participant `identifier`.
pub fn new(
signing_commitments: Vec<round1::SigningCommitments<C>>,
signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
message: &[u8],
) -> SigningPackage<C> {
SigningPackage {
signing_commitments: signing_commitments
.into_iter()
.map(|s| (s.identifier, s))
.collect(),
signing_commitments,
message: message.to_vec(),
ciphersuite: (),
}
@ -261,13 +258,13 @@ where
binding_factor_input_prefix.extend_from_slice(additional_prefix);
self.signing_commitments()
.values()
.map(|c| {
.keys()
.map(|identifier| {
let mut binding_factor_input = vec![];
binding_factor_input.extend_from_slice(&binding_factor_input_prefix);
binding_factor_input.extend_from_slice(c.identifier.serialize().as_ref());
(c.identifier, binding_factor_input)
binding_factor_input.extend_from_slice(identifier.serialize().as_ref());
(*identifier, binding_factor_input)
})
.collect()
}
@ -317,14 +314,14 @@ 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().values() {
for (commitment_identifier, commitment) in signing_package.signing_commitments() {
// 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 {
return Err(Error::IdentityCommitment);
}
let binding_factor = binding_factor_list[commitment.identifier].clone();
let binding_factor = binding_factor_list[*commitment_identifier].clone();
// Collect the binding commitments and their binding factors for one big
// multiscalar multiplication at the end.
@ -349,6 +346,12 @@ where
/// Aggregates the signature shares to produce a final signature that
/// can be verified with the group public key.
///
/// `signature_shares` maps the identifier of each participant to the
/// [`round2::SignatureShare`] they sent. These identifiers must come from whatever mapping
/// the coordinator has between communication channels and participants, i.e.
/// they must have assurance that the [`round2::SignatureShare`] came from
/// the participant with that identifier.
///
/// This operation is performed by a coordinator that can communicate with all
/// the signing participants before publishing the final signature. The
/// coordinator can be one of the participants or a semi-trusted third party
@ -360,7 +363,7 @@ where
/// service attack due to publishing an invalid signature.
pub fn aggregate<C>(
signing_package: &SigningPackage<C>,
signature_shares: &[round2::SignatureShare<C>],
signature_shares: &HashMap<Identifier<C>, round2::SignatureShare<C>>,
pubkeys: &keys::PublicKeyPackage<C>,
) -> Result<Signature<C>, Error<C>>
where
@ -382,8 +385,8 @@ where
// [`aggregate`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-5.3
let mut z = <<C::Group as Group>::Field>::zero();
for signature_share in signature_shares {
z = z + signature_share.signature.z_share;
for signature_share in signature_shares.values() {
z = z + signature_share.share;
}
let signature = Signature {
@ -408,27 +411,32 @@ where
);
// Verify the signature shares.
for signature_share in signature_shares {
for (signature_share_identifier, signature_share) in signature_shares {
// 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)
.get(signature_share_identifier)
.unwrap();
// Compute Lagrange coefficient.
let lambda_i =
derive_interpolating_value(&signature_share.identifier, signing_package)?;
let lambda_i = 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.
signature_share.verify(&R_share, signer_pubkey, lambda_i, &challenge)?;
signature_share.verify(
*signature_share_identifier,
&R_share,
signer_pubkey,
lambda_i,
&challenge,
)?;
}
// We should never reach here; but we return the verification error to be safe.

View File

@ -93,7 +93,9 @@ where
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Identifier")
.field(&<<C::Group as Group>::Field>::serialize(&self.0).as_ref())
.field(&hex::encode(
<<C::Group as Group>::Field>::serialize(&self.0).as_ref(),
))
.finish()
}
}

View File

@ -58,8 +58,6 @@ pub mod round1 {
#[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(crate) sender_identifier: Identifier<C>,
/// The public commitment from the participant (C_i)
pub(crate) commitment: VerifiableSecretSharingCommitment<C>,
/// The proof of knowledge of the temporary secret (σ_i = (R_i, μ_i))
@ -83,12 +81,10 @@ pub mod round1 {
{
/// 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: (),
@ -159,10 +155,6 @@ pub mod round2 {
#[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(crate) sender_identifier: Identifier<C>,
/// The identifier of the participant what will receive the package ().
pub(crate) receiver_identifier: Identifier<C>,
/// The secret share being sent.
pub(crate) secret_share: SigningShare<C>,
/// Ciphersuite ID for serialization
@ -183,14 +175,8 @@ pub mod round2 {
C: Ciphersuite,
{
/// Create a new [`Package`] instance.
pub fn new(
sender_identifier: Identifier<C>,
receiver_identifier: Identifier<C>,
secret_share: SigningShare<C>,
) -> Self {
pub fn new(secret_share: SigningShare<C>) -> Self {
Self {
sender_identifier,
receiver_identifier,
secret_share,
ciphersuite: (),
}
@ -284,7 +270,6 @@ pub fn part1<C: Ciphersuite, R: RngCore + CryptoRng>(
max_signers,
};
let package = round1::Package {
sender_identifier: identifier,
commitment,
proof_of_knowledge: Signature { R: R_i, z: mu_i },
ciphersuite: (),
@ -315,21 +300,33 @@ where
/// for the participant holding the given [`round1::SecretPackage`],
/// given the received [`round1::Package`]s received from the other participants.
///
/// `round1_packages` maps the identifier of each participant to the
/// [`round1::Package`] they sent. These identifiers must come from whatever mapping
/// the coordinator has between communication channels and participants, i.e.
/// they must have assurance that the [`round1::Package`] came from
/// the participant with that identifier.
///
/// It returns the [`round2::SecretPackage`] that must be kept in memory
/// by the participant for the final step, and the [`round2::Package`]s that
/// must be sent to other participants.
/// by the participant for the final step, and the a map of [`round2::Package`]s that
/// must be sent to each participant who has the given identifier in the map key.
pub fn part2<C: Ciphersuite>(
secret_package: round1::SecretPackage<C>,
round1_packages: &[round1::Package<C>],
) -> Result<(round2::SecretPackage<C>, Vec<round2::Package<C>>), Error<C>> {
round1_packages: &HashMap<Identifier<C>, round1::Package<C>>,
) -> Result<
(
round2::SecretPackage<C>,
HashMap<Identifier<C>, round2::Package<C>>,
),
Error<C>,
> {
if round1_packages.len() != (secret_package.max_signers - 1) as usize {
return Err(Error::IncorrectNumberOfPackages);
}
let mut round2_packages = Vec::new();
let mut round2_packages = HashMap::new();
for round1_package in round1_packages {
let ell = round1_package.sender_identifier;
for (sender_identifier, round1_package) in round1_packages {
let ell = *sender_identifier;
// Round 1, Step 5
//
// > Upon receiving C⃗_, σ_ from participants 1 ≤ ≤ n, ≠ i, participant
@ -351,12 +348,13 @@ pub fn part2<C: Ciphersuite>(
// > which they keep for themselves.
let value = evaluate_polynomial(ell, &secret_package.coefficients);
round2_packages.push(round2::Package {
sender_identifier: secret_package.identifier,
receiver_identifier: ell,
secret_share: SigningShare(value),
ciphersuite: (),
});
round2_packages.insert(
ell,
round2::Package {
secret_share: SigningShare(value),
ciphersuite: (),
},
);
}
let fii = evaluate_polynomial(secret_package.identifier, &secret_package.coefficients);
Ok((
@ -373,8 +371,7 @@ pub fn part2<C: Ciphersuite>(
/// Computes the verifying keys of the other participants for the third step
/// of the DKG protocol.
fn compute_verifying_keys<C: Ciphersuite>(
round2_packages: &[round2::Package<C>],
round1_packages_map: HashMap<Identifier<C>, &round1::Package<C>>,
round1_packages: &HashMap<Identifier<C>, round1::Package<C>>,
round2_secret_package: &round2::SecretPackage<C>,
) -> Result<HashMap<Identifier<C>, VerifyingShare<C>>, Error<C>> {
// Round 2, Step 4
@ -385,18 +382,18 @@ fn compute_verifying_keys<C: Ciphersuite>(
// Note that in this loop, "i" refers to the other participant whose public verification share
// we are computing, and not the current participant.
for i in round2_packages.iter().map(|p| p.sender_identifier) {
for i in round1_packages.keys().cloned() {
let mut y_i = <C::Group>::identity();
// We need to iterate through all commitment vectors, including our own,
// so chain it manually
for commitments in round2_packages
.iter()
.map(|p| {
for commitment in round1_packages
.keys()
.map(|k| {
// Get the commitment vector for this participant
Ok::<&VerifiableSecretSharingCommitment<C>, Error<C>>(
&round1_packages_map
.get(&p.sender_identifier)
&round1_packages
.get(k)
.ok_or(Error::PackageNotFound)?
.commitment,
)
@ -404,7 +401,7 @@ fn compute_verifying_keys<C: Ciphersuite>(
// Chain our own commitment vector
.chain(iter::once(Ok(&round2_secret_package.commitment)))
{
y_i = y_i + evaluate_vss(commitments?, i);
y_i = y_i + evaluate_vss(commitment?, i);
}
let y_i = VerifyingShare(y_i);
others_verifying_keys.insert(i, y_i);
@ -417,14 +414,22 @@ fn compute_verifying_keys<C: Ciphersuite>(
/// given the received [`round1::Package`]s and [`round2::Package`]s received from
/// the other participants.
///
/// `round1_packages` must be the same used in [`part2()`].
///
/// `round2_packages` maps the identifier of each participant to the
/// [`round2::Package`] they sent. These identifiers must come from whatever mapping
/// the coordinator has between communication channels and participants, i.e.
/// they must have assurance that the [`round2::Package`] came from
/// the participant with that identifier.
///
/// It returns the [`KeyPackage`] that has the long-lived key share for the
/// participant, and the [`PublicKeyPackage`]s that has public information
/// about all participants; both of which are required to compute FROST
/// signatures.
pub fn part3<C: Ciphersuite>(
round2_secret_package: &round2::SecretPackage<C>,
round1_packages: &[round1::Package<C>],
round2_packages: &[round2::Package<C>],
round1_packages: &HashMap<Identifier<C>, round1::Package<C>>,
round2_packages: &HashMap<Identifier<C>, round2::Package<C>>,
) -> Result<(KeyPackage<C>, PublicKeyPackage<C>), Error<C>> {
if round1_packages.len() != (round2_secret_package.max_signers - 1) as usize {
return Err(Error::IncorrectNumberOfPackages);
@ -432,30 +437,26 @@ pub fn part3<C: Ciphersuite>(
if round1_packages.len() != round2_packages.len() {
return Err(Error::IncorrectNumberOfPackages);
}
if round1_packages
.keys()
.any(|id| !round2_packages.contains_key(id))
{
return Err(Error::IncorrectPackage);
}
let mut signing_share = <<C::Group as Group>::Field>::zero();
let mut group_public = <C::Group>::identity();
let round1_packages_map: HashMap<Identifier<C>, &round1::Package<C>> = round1_packages
.iter()
.map(|package| (package.sender_identifier, package))
.collect();
for round2_package in round2_packages {
// Sanity check; was the package really meant to us?
if round2_package.receiver_identifier != round2_secret_package.identifier {
return Err(Error::IncorrectPackage);
}
for (sender_identifier, round2_package) in round2_packages {
// Round 2, Step 2
//
// > Each P_i verifies their shares by calculating:
// > g^{f_(i)} ≟ ∏^{t1}_{k=0} φ^{i^k mod q}_{k}, aborting if the
// > check fails.
let ell = round2_package.sender_identifier;
let ell = *sender_identifier;
let f_ell_i = round2_package.secret_share;
let commitment = &round1_packages_map
let commitment = &round1_packages
.get(&ell)
.ok_or(Error::PackageNotFound)?
.commitment;
@ -501,8 +502,7 @@ pub fn part3<C: Ciphersuite>(
//
// > Any participant can compute the public verification share of any other participant
// > by calculating Y_i = ∏_{j=1}^n ∏_{k=0}^{t1} φ_{jk}^{i^k mod q}.
let mut all_verifying_keys =
compute_verifying_keys(round2_packages, round1_packages_map, round2_secret_package)?;
let mut all_verifying_keys = compute_verifying_keys(round1_packages, round2_secret_package)?;
// Add the participant's own public verification share for consistency
all_verifying_keys.insert(round2_secret_package.identifier, verifying_key);

View File

@ -248,8 +248,6 @@ where
#[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(crate) identifier: Identifier<C>,
/// Commitment to the hiding [`Nonce`].
pub(crate) hiding: NonceCommitment<C>,
/// Commitment to the binding [`Nonce`].
@ -272,13 +270,8 @@ where
C: Ciphersuite,
{
/// Create new SigningCommitments
pub fn new(
identifier: Identifier<C>,
hiding: NonceCommitment<C>,
binding: NonceCommitment<C>,
) -> Self {
pub fn new(hiding: NonceCommitment<C>, binding: NonceCommitment<C>) -> Self {
Self {
identifier,
hiding,
binding,
ciphersuite: (),
@ -297,13 +290,12 @@ where
}
}
impl<C> From<(Identifier<C>, &SigningNonces<C>)> for SigningCommitments<C>
impl<C> From<&SigningNonces<C>> for SigningCommitments<C>
where
C: Ciphersuite,
{
fn from((identifier, nonces): (Identifier<C>, &SigningNonces<C>)) -> Self {
fn from(nonces: &SigningNonces<C>) -> Self {
Self {
identifier,
hiding: nonces.hiding.clone().into(),
binding: nonces.binding.clone().into(),
ciphersuite: (),
@ -335,8 +327,8 @@ pub(super) fn encode_group_commitments<C: Ciphersuite>(
) -> Vec<u8> {
let mut bytes = vec![];
for item in signing_commitments.values() {
bytes.extend_from_slice(item.identifier.serialize().as_ref());
for (item_identifier, item) in signing_commitments {
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());
}
@ -361,7 +353,6 @@ pub(super) fn encode_group_commitments<C: Ciphersuite>(
// https://github.com/ZcashFoundation/redjubjub/issues/111
pub fn preprocess<C, R>(
num_nonces: u8,
participant_identifier: Identifier<C>,
secret: &SigningShare<C>,
rng: &mut R,
) -> (Vec<SigningNonces<C>>, Vec<SigningCommitments<C>>)
@ -375,7 +366,7 @@ where
for _ in 0..num_nonces {
let nonces = SigningNonces::new(secret, rng);
signing_commitments.push(SigningCommitments::from((participant_identifier, &nonces)));
signing_commitments.push(SigningCommitments::from(&nonces));
signing_nonces.push(nonces);
}
@ -391,7 +382,6 @@ where
///
/// [`commit`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-round-one-commitment
pub fn commit<C, R>(
participant_identifier: Identifier<C>,
secret: &SigningShare<C>,
rng: &mut R,
) -> (SigningNonces<C>, SigningCommitments<C>)
@ -399,8 +389,7 @@ where
C: Ciphersuite,
R: CryptoRng + RngCore,
{
let (mut vec_signing_nonces, mut vec_signing_commitments) =
preprocess(1, participant_identifier, secret, rng);
let (mut vec_signing_nonces, mut vec_signing_commitments) = preprocess(1, secret, rng);
(
vec_signing_nonces.pop().expect("must have 1 element"),
vec_signing_commitments.pop().expect("must have 1 element"),

View File

@ -11,77 +11,36 @@ use crate::{
#[cfg(feature = "serde")]
use crate::ScalarSerialization;
/// A representation of a single signature share used in FROST structures and messages.
#[derive(Clone, Copy, Getters)]
// Used to help encoding a SignatureShare. Since it has a Scalar<C> it can't
// be directly encoded with serde, so we use this struct to wrap the scalar.
#[cfg(feature = "serde")]
#[derive(Clone)]
#[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(crate) z_share: Scalar<C>,
}
impl<C> SignatureResponse<C>
where
C: Ciphersuite,
{
/// Deserialize [`SignatureResponse`] from bytes
pub fn deserialize(
bytes: <<C::Group as Group>::Field as Field>::Serialization,
) -> Result<Self, Error<C>> {
<<C::Group as Group>::Field>::deserialize(&bytes)
.map(|scalar| Self { z_share: scalar })
.map_err(|e| e.into())
}
/// Serialize [`SignatureResponse`] to bytes
pub fn serialize(&self) -> <<C::Group as Group>::Field as Field>::Serialization {
<<C::Group as Group>::Field>::serialize(&self.z_share)
}
}
struct SignatureShareHelper<C: Ciphersuite>(Scalar<C>);
#[cfg(feature = "serde")]
impl<C> TryFrom<ScalarSerialization<C>> for SignatureResponse<C>
impl<C> TryFrom<ScalarSerialization<C>> for SignatureShareHelper<C>
where
C: Ciphersuite,
{
type Error = Error<C>;
fn try_from(value: ScalarSerialization<C>) -> Result<Self, Self::Error> {
Self::deserialize(value.0)
<<C::Group as Group>::Field>::deserialize(&value.0)
.map(|scalar| Self(scalar))
.map_err(|e| e.into())
}
}
#[cfg(feature = "serde")]
impl<C> From<SignatureResponse<C>> for ScalarSerialization<C>
impl<C> From<SignatureShareHelper<C>> for ScalarSerialization<C>
where
C: Ciphersuite,
{
fn from(value: SignatureResponse<C>) -> Self {
Self(value.serialize())
}
}
impl<C> Debug for SignatureResponse<C>
where
C: Ciphersuite,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("SignatureResponse")
.field("z_share", &hex::encode(self.serialize()))
.finish()
}
}
impl<C> Eq for SignatureResponse<C> where C: Ciphersuite {}
impl<C> PartialEq for SignatureResponse<C>
where
C: Ciphersuite,
{
// TODO: should this have any constant-time guarantees? I think signature shares are public.
fn eq(&self, other: &Self) -> bool {
self.z_share == other.z_share
fn from(value: SignatureShareHelper<C>) -> Self {
Self(<<C::Group as Group>::Field>::serialize(&value.0))
}
}
@ -90,11 +49,63 @@ where
#[derive(Clone, Copy, Eq, PartialEq, Getters)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "serde", serde(try_from = "SignatureShareSerialization<C>"))]
#[cfg_attr(feature = "serde", serde(into = "SignatureShareSerialization<C>"))]
pub struct SignatureShare<C: Ciphersuite> {
/// Represents the participant identifier.
pub(crate) identifier: Identifier<C>,
/// This participant's signature over the message.
pub(crate) signature: SignatureResponse<C>,
pub(crate) share: Scalar<C>,
}
impl<C> SignatureShare<C>
where
C: Ciphersuite,
{
/// Deserialize [`SignatureShare`] from bytes
pub fn deserialize(
bytes: <<C::Group as Group>::Field as Field>::Serialization,
) -> Result<Self, Error<C>> {
<<C::Group as Group>::Field>::deserialize(&bytes)
.map(|scalar| Self { share: scalar })
.map_err(|e| e.into())
}
/// Serialize [`SignatureShare`] to bytes
pub fn serialize(&self) -> <<C::Group as Group>::Field as Field>::Serialization {
<<C::Group as Group>::Field>::serialize(&self.share)
}
/// Tests if a signature share issued by a participant is valid before
/// aggregating it into a final joint signature to publish.
///
/// This is the final step of [`verify_signature_share`] from the spec.
///
/// [`verify_signature_share`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-signature-share-verificatio
#[cfg_attr(feature = "internals", visibility::make(pub))]
pub(crate) fn verify(
&self,
identifier: Identifier<C>,
group_commitment_share: &round1::GroupCommitmentShare<C>,
public_key: &frost::keys::VerifyingShare<C>,
lambda_i: Scalar<C>,
challenge: &Challenge<C>,
) -> Result<(), Error<C>> {
if (<C::Group>::generator() * self.share)
!= (group_commitment_share.0 + (public_key.0 * challenge.0 * lambda_i))
{
return Err(Error::InvalidSignatureShare {
culprit: identifier,
});
}
Ok(())
}
}
#[cfg(feature = "serde")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
struct SignatureShareSerialization<C: Ciphersuite> {
share: SignatureShareHelper<C>,
/// Ciphersuite ID for serialization
#[cfg_attr(
feature = "serde",
@ -107,41 +118,28 @@ pub struct SignatureShare<C: Ciphersuite> {
ciphersuite: (),
}
impl<C> SignatureShare<C>
#[cfg(feature = "serde")]
impl<C> From<SignatureShareSerialization<C>> for SignatureShare<C>
where
C: Ciphersuite,
{
/// Create a new [`SignatureShare`].
pub fn new(identifier: Identifier<C>, signature: SignatureResponse<C>) -> Self {
fn from(value: SignatureShareSerialization<C>) -> Self {
Self {
identifier,
signature,
ciphersuite: (),
share: value.share.0,
}
}
}
/// Tests if a signature share issued by a participant is valid before
/// aggregating it into a final joint signature to publish.
///
/// This is the final step of [`verify_signature_share`] from the spec.
///
/// [`verify_signature_share`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-signature-share-verificatio
pub fn verify(
&self,
group_commitment_share: &round1::GroupCommitmentShare<C>,
public_key: &frost::keys::VerifyingShare<C>,
lambda_i: Scalar<C>,
challenge: &Challenge<C>,
) -> Result<(), Error<C>> {
if (<C::Group>::generator() * self.signature.z_share)
!= (group_commitment_share.0 + (public_key.0 * challenge.0 * lambda_i))
{
return Err(Error::InvalidSignatureShare {
culprit: self.identifier,
});
#[cfg(feature = "serde")]
impl<C> From<SignatureShare<C>> for SignatureShareSerialization<C>
where
C: Ciphersuite,
{
fn from(value: SignatureShare<C>) -> Self {
Self {
share: SignatureShareHelper(value.share),
ciphersuite: (),
}
Ok(())
}
}
@ -151,8 +149,7 @@ where
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("SignatureShare")
.field("identifier", &self.identifier)
.field("signature", &self.signature)
.field("share", &hex::encode(self.serialize()))
.finish()
}
}
@ -170,11 +167,7 @@ fn compute_signature_share<C: Ciphersuite>(
+ (signer_nonces.binding.0 * binding_factor.0)
+ (lambda_i * key_package.secret_share.0 * challenge.0);
SignatureShare::<C> {
identifier: *key_package.identifier(),
signature: SignatureResponse::<C> { z_share },
ciphersuite: (),
}
SignatureShare::<C> { share: z_share }
}
/// Performed once by each participant selected for the signing operation.

View File

@ -1,5 +1,8 @@
//! Ciphersuite-generic test functions.
use std::{collections::HashMap, convert::TryFrom};
use std::{
collections::{BTreeMap, HashMap},
convert::TryFrom,
};
use crate::{
frost::{self, Identifier},
@ -94,8 +97,8 @@ fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
) -> (Vec<u8>, Signature<C>, VerifyingKey<C>) {
let mut nonces_map: HashMap<frost::Identifier<C>, frost::round1::SigningNonces<C>> =
HashMap::new();
let mut commitments_map: HashMap<frost::Identifier<C>, frost::round1::SigningCommitments<C>> =
HashMap::new();
let mut commitments_map: BTreeMap<frost::Identifier<C>, frost::round1::SigningCommitments<C>> =
BTreeMap::new();
////////////////////////////////////////////////////////////////////////////
// Round 1: generating nonces and signing commitments for each participant
@ -105,7 +108,6 @@ fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
// Generate one (1) nonce and one SigningCommitments instance for each
// participant, up to _min_signers_.
let (nonces, commitments) = frost::round1::commit(
participant_identifier,
key_packages
.get(&participant_identifier)
.unwrap()
@ -119,10 +121,9 @@ fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
// This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign
// - take one (unused) commitment per signing participant
let mut signature_shares = Vec::new();
let mut signature_shares = HashMap::new();
let message = "message to sign".as_bytes();
let comms = commitments_map.clone().into_values().collect();
let signing_package = frost::SigningPackage::new(comms, message);
let signing_package = frost::SigningPackage::new(commitments_map, message);
////////////////////////////////////////////////////////////////////////////
// Round 2: each participant generates their signature share
@ -136,7 +137,7 @@ fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
// Each participant generates their signature share.
let signature_share =
frost::round2::sign(&signing_package, nonces_to_use, key_package).unwrap();
signature_shares.push(signature_share);
signature_shares.insert(*participant_identifier, signature_share);
}
////////////////////////////////////////////////////////////////////////////
@ -152,7 +153,7 @@ fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
// Aggregate (also verifies the signature shares)
let group_signature =
frost::aggregate(&signing_package, &signature_shares[..], &pubkey_package).unwrap();
frost::aggregate(&signing_package, &signature_shares, &pubkey_package).unwrap();
// Check that the threshold signature can be verified by the group public
// key (the verification key).
@ -182,20 +183,16 @@ fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
fn check_aggregate_error<C: Ciphersuite + PartialEq>(
signing_package: frost::SigningPackage<C>,
mut signature_shares: Vec<frost::round2::SignatureShare<C>>,
mut signature_shares: HashMap<frost::Identifier<C>, frost::round2::SignatureShare<C>>,
pubkey_package: frost::keys::PublicKeyPackage<C>,
) {
let one = <<C as Ciphersuite>::Group as Group>::Field::one();
// Corrupt a share
signature_shares[0].signature.z_share = signature_shares[0].signature.z_share + one;
let e = frost::aggregate(&signing_package, &signature_shares[..], &pubkey_package).unwrap_err();
assert_eq!(e.culprit(), Some(*signature_shares[0].identifier()));
assert_eq!(
e,
Error::InvalidSignatureShare {
culprit: *signature_shares[0].identifier()
}
);
let id = *signature_shares.keys().next().unwrap();
signature_shares.get_mut(&id).unwrap().share = signature_shares[&id].share + one;
let e = frost::aggregate(&signing_package, &signature_shares, &pubkey_package).unwrap_err();
assert_eq!(e.culprit(), Some(id));
assert_eq!(e, Error::InvalidSignatureShare { culprit: id });
}
/// Test FROST signing with trusted dealer with a Ciphersuite.
@ -225,7 +222,7 @@ where
// will be sent through some communication channel.
let mut received_round1_packages: HashMap<
frost::Identifier<C>,
Vec<frost::keys::dkg::round1::Package<C>>,
HashMap<frost::Identifier<C>, frost::keys::dkg::round1::Package<C>>,
> = HashMap::new();
// For each participant, perform the first part of the DKG protocol.
@ -252,8 +249,8 @@ where
.expect("should be nonzero");
received_round1_packages
.entry(receiver_participant_identifier)
.or_insert_with(Vec::new)
.push(round1_package.clone());
.or_insert_with(HashMap::new)
.insert(participant_identifier, round1_package.clone());
}
}
@ -292,11 +289,11 @@ where
// sent through some communication channel.
// Note that, in contrast to the previous part, here each other participant
// gets its own specific package.
for round2_package in round2_packages {
for (receiver_identifier, round2_package) in round2_packages {
received_round2_packages
.entry(round2_package.receiver_identifier)
.or_insert_with(Vec::new)
.push(round2_package);
.entry(receiver_identifier)
.or_insert_with(HashMap::new)
.insert(participant_identifier, round2_package);
}
}
@ -412,19 +409,16 @@ pub fn check_sign_with_dealer_and_identifiers<C: Ciphersuite, R: RngCore + Crypt
fn check_part2_error<C: Ciphersuite>(
round1_secret_package: frost::keys::dkg::round1::SecretPackage<C>,
mut round1_packages: Vec<frost::keys::dkg::round1::Package<C>>,
mut round1_packages: HashMap<frost::Identifier<C>, frost::keys::dkg::round1::Package<C>>,
) {
let one = <<C as Ciphersuite>::Group as Group>::Field::one();
// Corrupt a PoK
round1_packages[0].proof_of_knowledge.z = round1_packages[0].proof_of_knowledge.z + one;
let id = *round1_packages.keys().next().unwrap();
round1_packages.get_mut(&id).unwrap().proof_of_knowledge.z =
round1_packages[&id].proof_of_knowledge.z + one;
let e = frost::keys::dkg::part2(round1_secret_package, &round1_packages).unwrap_err();
assert_eq!(e.culprit(), Some(*round1_packages[0].sender_identifier()));
assert_eq!(
e,
Error::InvalidProofOfKnowledge {
culprit: *round1_packages[0].sender_identifier()
}
);
assert_eq!(e.culprit(), Some(id));
assert_eq!(e, Error::InvalidProofOfKnowledge { culprit: id });
}
/// Test Error culprit method.

View File

@ -1,5 +1,5 @@
//! Helper function for testing with test vectors.
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use debugless_unwrap::DebuglessUnwrap;
use hex::{self, FromHex};
@ -20,7 +20,7 @@ pub struct TestVectors<C: Ciphersuite> {
hiding_nonces_randomness: HashMap<Identifier<C>, Vec<u8>>,
binding_nonces_randomness: HashMap<Identifier<C>, Vec<u8>>,
signer_nonces: HashMap<Identifier<C>, SigningNonces<C>>,
signer_commitments: HashMap<Identifier<C>, SigningCommitments<C>>,
signer_commitments: BTreeMap<Identifier<C>, SigningCommitments<C>>,
binding_factor_inputs: HashMap<Identifier<C>, Vec<u8>>,
binding_factors: HashMap<Identifier<C>, BindingFactor<C>>,
signature_shares: HashMap<Identifier<C>, SignatureShare<C>>,
@ -80,7 +80,7 @@ pub fn parse_test_vectors<C: Ciphersuite>(json_vectors: &Value) -> TestVectors<C
let mut hiding_nonces_randomness: HashMap<Identifier<C>, Vec<u8>> = HashMap::new();
let mut binding_nonces_randomness: HashMap<Identifier<C>, Vec<u8>> = HashMap::new();
let mut signer_nonces: HashMap<Identifier<C>, SigningNonces<C>> = HashMap::new();
let mut signer_commitments: HashMap<Identifier<C>, SigningCommitments<C>> = HashMap::new();
let mut signer_commitments: BTreeMap<Identifier<C>, SigningCommitments<C>> = BTreeMap::new();
let mut binding_factor_inputs: HashMap<Identifier<C>, Vec<u8>> = HashMap::new();
let mut binding_factors: HashMap<Identifier<C>, BindingFactor<C>> = HashMap::new();
@ -104,7 +104,6 @@ pub fn parse_test_vectors<C: Ciphersuite>(json_vectors: &Value) -> TestVectors<C
signer_nonces.insert(identifier, signing_nonces);
let signing_commitments = SigningCommitments::<C>::new(
identifier,
NonceCommitment::from_hex(signer["hiding_nonce_commitment"].as_str().unwrap()).unwrap(),
NonceCommitment::from_hex(signer["binding_nonce_commitment"].as_str().unwrap())
.unwrap(),
@ -136,12 +135,7 @@ pub fn parse_test_vectors<C: Ciphersuite>(json_vectors: &Value) -> TestVectors<C
)
.debugless_unwrap();
let signature_share = SignatureShare::<C>::new(
i.try_into().unwrap(),
SignatureResponse {
z_share: <<C::Group as Group>::Field>::deserialize(&sig_share).unwrap(),
},
);
let signature_share = SignatureShare::<C>::deserialize(sig_share).unwrap();
signature_shares.insert(i.try_into().unwrap(), signature_share);
}
@ -259,9 +253,7 @@ pub fn check_sign_with_test_vectors<C: Ciphersuite>(json_vectors: &Value) {
// Round 2: each participant generates their signature share
/////////////////////////////////////////////////////////////////////////////
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, &message_bytes);
for (identifier, input) in signing_package.binding_factor_preimages(&[]).iter() {
assert_eq!(*input, binding_factor_inputs[identifier]);
@ -274,7 +266,7 @@ pub fn check_sign_with_test_vectors<C: Ciphersuite>(json_vectors: &Value) {
assert_eq!(*binding_factor, binding_factors[identifier]);
}
let mut our_signature_shares: Vec<frost::round2::SignatureShare<C>> = Vec::new();
let mut our_signature_shares = HashMap::new();
// Each participant generates their signature share
for identifier in signer_nonces.keys() {
@ -284,12 +276,10 @@ pub fn check_sign_with_test_vectors<C: Ciphersuite>(json_vectors: &Value) {
// Each participant generates their signature share.
let signature_share = frost::round2::sign(&signing_package, nonces, key_package).unwrap();
our_signature_shares.push(signature_share);
our_signature_shares.insert(*identifier, signature_share);
}
for sig_share in our_signature_shares.clone() {
assert_eq!(sig_share, signature_shares[sig_share.identifier()]);
}
assert_eq!(our_signature_shares, signature_shares);
let signer_pubkeys = key_packages
.into_iter()
@ -304,14 +294,8 @@ pub fn check_sign_with_test_vectors<C: Ciphersuite>(json_vectors: &Value) {
////////////////////////////////////////////////////////////////////////////
// Aggregate the FROST signature from test vector sig shares
let group_signature_result = frost::aggregate(
&signing_package,
&signature_shares
.values()
.cloned()
.collect::<Vec<frost::round2::SignatureShare<C>>>(),
&pubkey_package,
);
let group_signature_result =
frost::aggregate(&signing_package, &signature_shares, &pubkey_package);
// Check that the aggregation passed signature share verification and generation
assert!(group_signature_result.is_ok());

View File

@ -12,7 +12,7 @@ scenario in a single thread and it abstracts away any communication between peer
# // ANCHOR: tkg_gen
use frost_ed25519 as frost;
use rand::thread_rng;
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
let mut rng = thread_rng();
let max_signers = 5;
@ -38,7 +38,7 @@ for (identifier, secret_share) in shares {
}
let mut nonces_map = HashMap::new();
let mut commitments_map = HashMap::new();
let mut commitments_map = BTreeMap::new();
////////////////////////////////////////////////////////////////////////////
// Round 1: generating nonces and signing commitments for each participant
@ -52,8 +52,7 @@ for participant_index in 1..(min_signers as u16 + 1) {
// participant, up to _threshold_.
# // ANCHOR: round1_commit
let (nonces, commitments) = frost::round1::commit(
participant_identifier,
key_package.secret_share(),
key_packages[&participant_identifier].secret_share(),
&mut rng,
);
# // ANCHOR_END: round1_commit
@ -68,14 +67,13 @@ for participant_index in 1..(min_signers as u16 + 1) {
// This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign
// - take one (unused) commitment per signing participant
let mut signature_shares = Vec::new();
let commitments_received = commitments_map.clone().into_values().collect();
let mut signature_shares = HashMap::new();
# // ANCHOR: round2_package
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);
let signing_package = frost::SigningPackage::new(commitments_map, message);
# // ANCHOR_END: round2_package
////////////////////////////////////////////////////////////////////////////
@ -95,7 +93,7 @@ for participant_identifier in nonces_map.keys() {
// In practice, the signature share must be sent to the Coordinator
// using an authenticated channel.
signature_shares.push(signature_share);
signature_shares.insert(*participant_identifier, signature_share);
}
////////////////////////////////////////////////////////////////////////////
@ -105,7 +103,7 @@ for participant_identifier in nonces_map.keys() {
// Aggregate (also verifies the signature shares)
# // ANCHOR: aggregate
let group_signature = frost::aggregate(&signing_package, &signature_shares[..], &pubkey_package)?;
let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?;
# // ANCHOR_END: aggregate

View File

@ -80,8 +80,8 @@ for participant_index in 1..=max_signers {
.expect("should be nonzero");
received_round1_packages
.entry(receiver_participant_identifier)
.or_insert_with(Vec::new)
.push(round1_package.clone());
.or_insert_with(HashMap::new)
.insert(participant_identifier, round1_package.clone());
}
}
@ -121,11 +121,11 @@ for participant_index in 1..=max_signers {
// sent through some communication channel.
// Note that, in contrast to the previous part, here each other participant
// gets its own specific package.
for round2_package in round2_packages {
for (receiver_identifier, round2_package) in round2_packages {
received_round2_packages
.entry(*round2_package.receiver_identifier())
.or_insert_with(Vec::new)
.push(round2_package);
.entry(receiver_identifier)
.or_insert_with(HashMap::new)
.insert(participant_identifier, round2_package);
}
}

View File

@ -64,8 +64,8 @@ pub fn part1<R: RngCore + CryptoRng>(
/// must be sent to other participants.
pub fn part2(
secret_package: round1::SecretPackage,
round1_packages: &[round1::Package],
) -> Result<(round2::SecretPackage, Vec<round2::Package>), Error> {
round1_packages: &HashMap<Identifier, round1::Package>,
) -> Result<(round2::SecretPackage, HashMap<Identifier, round2::Package>), Error> {
frost::keys::dkg::part2(secret_package, round1_packages)
}
@ -80,8 +80,8 @@ pub fn part2(
/// signatures.
pub fn part3(
round2_secret_package: &round2::SecretPackage,
round1_packages: &[round1::Package],
round2_packages: &[round2::Package],
round1_packages: &HashMap<Identifier, round1::Package>,
round2_packages: &HashMap<Identifier, round2::Package>,
) -> Result<(KeyPackage, PublicKeyPackage), Error> {
frost::keys::dkg::part3(round2_secret_package, round1_packages, round2_packages)
}

View File

@ -2,6 +2,8 @@
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
use std::collections::HashMap;
use curve25519_dalek::{
constants::ED25519_BASEPOINT_POINT,
edwards::{CompressedEdwardsY, EdwardsPoint},
@ -338,15 +340,11 @@ pub mod round1 {
///
/// Generates the signing nonces and commitments to be used in the signing
/// operation.
pub fn commit<RNG>(
participant_identifier: frost::Identifier<E>,
secret: &SigningShare,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
where
RNG: CryptoRng + RngCore,
{
frost::round1::commit::<E, RNG>(participant_identifier, secret, rng)
frost::round1::commit::<E, RNG>(secret, rng)
}
}
@ -362,9 +360,6 @@ pub mod round2 {
/// shares into the joint signature.
pub type SignatureShare = frost::round2::SignatureShare<E>;
/// A representation of a single signature share used in FROST structures and messages.
pub type SignatureResponse = frost::round2::SignatureResponse<E>;
/// Performed once by each participant selected for the signing operation.
///
/// Receives the message to be signed and a set of signing commitments and a set
@ -402,7 +397,7 @@ pub type Signature = frost_core::Signature<E>;
/// service attack due to publishing an invalid signature.
pub fn aggregate(
signing_package: &SigningPackage,
signature_shares: &[round2::SignatureShare],
signature_shares: &HashMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys)

View File

@ -1,6 +1,6 @@
//! Generate sample, fixed instances of structs for testing.
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_ed25519::{
@ -10,7 +10,7 @@ use frost_ed25519::{
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare},
round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey,
};
@ -38,18 +38,14 @@ pub fn signing_commitments() -> SigningCommitments {
let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2());
let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap();
let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap();
let identifier = 42u16.try_into().unwrap();
SigningCommitments::new(
identifier,
hiding_nonce_commitment,
binding_nonce_commitment,
)
SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment)
}
/// Generate a sample SigningPackage.
pub fn signing_package() -> SigningPackage {
let commitments = vec![signing_commitments()];
let identifier = 42u16.try_into().unwrap();
let commitments = BTreeMap::from([(identifier, signing_commitments())]);
let message = "hello world".as_bytes();
SigningPackage::new(commitments, message)
@ -57,11 +53,9 @@ pub fn signing_package() -> SigningPackage {
/// 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::deserialize(serialized_scalar).unwrap();
SignatureShare::new(identifier, signature_response)
SignatureShare::deserialize(serialized_scalar).unwrap()
}
/// Generate a sample SecretShare.
@ -103,7 +97,6 @@ pub fn public_key_package() -> PublicKeyPackage {
/// 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
@ -118,14 +111,13 @@ pub fn round1_package() -> round1::Package {
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
let signature = Signature::deserialize(serialized_signature).unwrap();
round1::Package::new(identifier, vss_commitment, signature)
round1::Package::new(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::deserialize(serialized_scalar).unwrap();
round2::Package::new(identifier, identifier, signing_share)
round2::Package::new(signing_share)
}

View File

@ -19,10 +19,9 @@ use helpers::samples;
#[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);
let new_commitments = SigningCommitments::new(*hiding, *binding);
assert!(commitments == new_commitments);
}
@ -31,14 +30,10 @@ fn check_signing_commitments_recreation() {
fn check_signing_package_recreation() {
let signing_package = samples::signing_package();
let commitments = signing_package
.signing_commitments()
.values()
.cloned()
.collect();
let commitments = signing_package.signing_commitments();
let message = signing_package.message();
let new_signing_package = SigningPackage::new(commitments, message);
let new_signing_package = SigningPackage::new(commitments.clone(), message);
assert!(signing_package == new_signing_package);
}
@ -47,10 +42,9 @@ fn check_signing_package_recreation() {
fn check_signature_share_recreation() {
let signature_share = samples::signature_share();
let identifier = signature_share.identifier();
let signature_response = signature_share.signature();
let encoded = signature_share.serialize();
let new_signature_share = SignatureShare::new(*identifier, *signature_response);
let new_signature_share = SignatureShare::deserialize(encoded).unwrap();
assert!(signature_share == new_signature_share);
}
@ -106,11 +100,10 @@ fn check_public_key_package_recreation() {
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);
let new_round1_package = round1::Package::new(vss_commitment.clone(), *signature);
assert!(round1_package == new_round1_package);
}
@ -120,12 +113,9 @@ fn check_round1_package_recreation() {
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);
let new_round2_package = round2::Package::new(*signing_share);
assert!(round2_package == new_round2_package);
}

View File

@ -25,7 +25,6 @@ fn check_signing_commitments_serialization() {
assert!(commitments == decoded_commitments);
let json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)"
@ -38,7 +37,6 @@ fn check_signing_commitments_serialization() {
// Wrong ciphersuite
let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Wrong, SHA-512)"
@ -47,7 +45,6 @@ fn check_signing_commitments_serialization() {
// Invalid identifier
let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
}"#;
@ -55,8 +52,7 @@ fn check_signing_commitments_serialization() {
// Invalid field
let invalid_json = r#"{
"foo": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"foo": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
}"#;
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
@ -70,7 +66,6 @@ fn check_signing_commitments_serialization() {
// Extra field
let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"extra": 1
@ -94,7 +89,6 @@ fn check_signing_package_serialization() {
let json = r#"{
"signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)"
@ -110,7 +104,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"0000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)"
@ -125,7 +118,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"foo": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)"
@ -140,7 +132,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)"
}
@ -154,7 +145,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)"
@ -179,8 +169,7 @@ fn check_signature_share_serialization() {
assert!(signature_share == decoded_signature_share);
let json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"signature": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, SHA-512)"
}"#;
let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap();
@ -189,17 +178,8 @@ fn check_signature_share_serialization() {
let invalid_json = "{}";
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Invalid identifier
let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"signature": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, SHA-512)"
}"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"foo": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, SHA-512)"
}"#;
@ -207,15 +187,13 @@ fn check_signature_share_serialization() {
// Missing field
let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",,
"ciphersuite": "FROST(Ed25519, SHA-512)"
}"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Extra field
let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"signature": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"extra": 1,
"ciphersuite": "FROST(Ed25519, SHA-512)"
}"#;
@ -430,7 +408,6 @@ fn check_round1_package_serialization() {
assert!(round1_package == decoded_round1_package);
let json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"5866666666666666666666666666666666666666666666666666666666666666"
],
@ -443,20 +420,8 @@ fn check_round1_package_serialization() {
let invalid_json = "{}";
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
// Invalid identifier
let invalid_json = r#"{
"sender_identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"5866666666666666666666666666666666666666666666666666666666666666"
],
"proof_of_knowledge": "5866666666666666666666666666666666666666666666666666666666666666498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, SHA-512)"
}"#;
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"5866666666666666666666666666666666666666666666666666666666666666"
],
@ -467,7 +432,6 @@ fn check_round1_package_serialization() {
// Missing field
let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"5866666666666666666666666666666666666666666666666666666666666666"
],
@ -477,7 +441,6 @@ fn check_round1_package_serialization() {
// Extra field
let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"5866666666666666666666666666666666666666666666666666666666666666"
],
@ -499,8 +462,6 @@ fn check_round2_package_serialization() {
assert!(round2_package == decoded_round2_package);
let json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"receiver_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, SHA-512)"
}"#;
@ -510,36 +471,21 @@ fn check_round2_package_serialization() {
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": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, 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": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"foo": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, SHA-512)"
}"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Missing field
let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, 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": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"extra": 1,
"ciphersuite": "FROST(Ed25519, SHA-512)"

View File

@ -12,7 +12,7 @@ scenario in a single thread and it abstracts away any communication between peer
# // ANCHOR: tkg_gen
use frost_ed448 as frost;
use rand::thread_rng;
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
let mut rng = thread_rng();
let max_signers = 5;
@ -38,7 +38,7 @@ for (identifier, secret_share) in shares {
}
let mut nonces_map = HashMap::new();
let mut commitments_map = HashMap::new();
let mut commitments_map = BTreeMap::new();
////////////////////////////////////////////////////////////////////////////
// Round 1: generating nonces and signing commitments for each participant
@ -52,8 +52,7 @@ for participant_index in 1..(min_signers as u16 + 1) {
// participant, up to _threshold_.
# // ANCHOR: round1_commit
let (nonces, commitments) = frost::round1::commit(
participant_identifier,
key_package.secret_share(),
key_packages[&participant_identifier].secret_share(),
&mut rng,
);
# // ANCHOR_END: round1_commit
@ -68,14 +67,13 @@ for participant_index in 1..(min_signers as u16 + 1) {
// This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign
// - take one (unused) commitment per signing participant
let mut signature_shares = Vec::new();
let commitments_received = commitments_map.clone().into_values().collect();
let mut signature_shares = HashMap::new();
# // ANCHOR: round2_package
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);
let signing_package = frost::SigningPackage::new(commitments_map, message);
# // ANCHOR_END: round2_package
////////////////////////////////////////////////////////////////////////////
@ -95,7 +93,7 @@ for participant_identifier in nonces_map.keys() {
// In practice, the signature share must be sent to the Coordinator
// using an authenticated channel.
signature_shares.push(signature_share);
signature_shares.insert(*participant_identifier, signature_share);
}
////////////////////////////////////////////////////////////////////////////
@ -105,7 +103,7 @@ for participant_identifier in nonces_map.keys() {
// Aggregate (also verifies the signature shares)
# // ANCHOR: aggregate
let group_signature = frost::aggregate(&signing_package, &signature_shares[..], &pubkey_package)?;
let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?;
# // ANCHOR_END: aggregate

View File

@ -80,8 +80,8 @@ for participant_index in 1..=max_signers {
.expect("should be nonzero");
received_round1_packages
.entry(receiver_participant_identifier)
.or_insert_with(Vec::new)
.push(round1_package.clone());
.or_insert_with(HashMap::new)
.insert(participant_identifier, round1_package.clone());
}
}
@ -121,11 +121,11 @@ for participant_index in 1..=max_signers {
// sent through some communication channel.
// Note that, in contrast to the previous part, here each other participant
// gets its own specific package.
for round2_package in round2_packages {
for (receiver_identifier, round2_package) in round2_packages {
received_round2_packages
.entry(*round2_package.receiver_identifier())
.or_insert_with(Vec::new)
.push(round2_package);
.entry(receiver_identifier)
.or_insert_with(HashMap::new)
.insert(participant_identifier, round2_package);
}
}

View File

@ -64,8 +64,8 @@ pub fn part1<R: RngCore + CryptoRng>(
/// must be sent to other participants.
pub fn part2(
secret_package: round1::SecretPackage,
round1_packages: &[round1::Package],
) -> Result<(round2::SecretPackage, Vec<round2::Package>), Error> {
round1_packages: &HashMap<Identifier, round1::Package>,
) -> Result<(round2::SecretPackage, HashMap<Identifier, round2::Package>), Error> {
frost::keys::dkg::part2(secret_package, round1_packages)
}
@ -80,8 +80,8 @@ pub fn part2(
/// signatures.
pub fn part3(
round2_secret_package: &round2::SecretPackage,
round1_packages: &[round1::Package],
round2_packages: &[round2::Package],
round1_packages: &HashMap<Identifier, round1::Package>,
round2_packages: &HashMap<Identifier, round2::Package>,
) -> Result<(KeyPackage, PublicKeyPackage), Error> {
frost::keys::dkg::part3(round2_secret_package, round1_packages, round2_packages)
}

View File

@ -2,6 +2,8 @@
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
use std::collections::HashMap;
use ed448_goldilocks::{
curve::{edwards::CompressedEdwardsY, ExtendedPoint},
Scalar,
@ -332,15 +334,11 @@ pub mod round1 {
///
/// Generates the signing nonces and commitments to be used in the signing
/// operation.
pub fn commit<RNG>(
participant_identifier: frost::Identifier<E>,
secret: &SigningShare,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
where
RNG: CryptoRng + RngCore,
{
frost::round1::commit::<E, RNG>(participant_identifier, secret, rng)
frost::round1::commit::<E, RNG>(secret, rng)
}
}
@ -356,9 +354,6 @@ pub mod round2 {
/// shares into the joint signature.
pub type SignatureShare = frost::round2::SignatureShare<E>;
/// A representation of a single signature share used in FROST structures and messages.
pub type SignatureResponse = frost::round2::SignatureResponse<E>;
/// Performed once by each participant selected for the signing operation.
///
/// Receives the message to be signed and a set of signing commitments and a set
@ -396,7 +391,7 @@ pub type Signature = frost_core::Signature<E>;
/// service attack due to publishing an invalid signature.
pub fn aggregate(
signing_package: &SigningPackage,
signature_shares: &[round2::SignatureShare],
signature_shares: &HashMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys)

View File

@ -1,6 +1,6 @@
//! Generate sample, fixed instances of structs for testing.
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_ed448::{
@ -10,7 +10,7 @@ use frost_ed448::{
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare},
round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey,
};
@ -38,18 +38,14 @@ pub fn signing_commitments() -> SigningCommitments {
let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2());
let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap();
let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap();
let identifier = 42u16.try_into().unwrap();
SigningCommitments::new(
identifier,
hiding_nonce_commitment,
binding_nonce_commitment,
)
SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment)
}
/// Generate a sample SigningPackage.
pub fn signing_package() -> SigningPackage {
let commitments = vec![signing_commitments()];
let identifier = 42u16.try_into().unwrap();
let commitments = BTreeMap::from([(identifier, signing_commitments())]);
let message = "hello world".as_bytes();
SigningPackage::new(commitments, message)
@ -57,11 +53,9 @@ pub fn signing_package() -> SigningPackage {
/// 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::deserialize(serialized_scalar).unwrap();
SignatureShare::new(identifier, signature_response)
SignatureShare::deserialize(serialized_scalar).unwrap()
}
/// Generate a sample SecretShare.
@ -103,7 +97,6 @@ pub fn public_key_package() -> PublicKeyPackage {
/// 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
@ -118,14 +111,13 @@ pub fn round1_package() -> round1::Package {
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
let signature = Signature::deserialize(serialized_signature).unwrap();
round1::Package::new(identifier, vss_commitment, signature)
round1::Package::new(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::deserialize(serialized_scalar).unwrap();
round2::Package::new(identifier, identifier, signing_share)
round2::Package::new(signing_share)
}

View File

@ -19,10 +19,9 @@ use helpers::samples;
#[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);
let new_commitments = SigningCommitments::new(*hiding, *binding);
assert!(commitments == new_commitments);
}
@ -31,14 +30,10 @@ fn check_signing_commitments_recreation() {
fn check_signing_package_recreation() {
let signing_package = samples::signing_package();
let commitments = signing_package
.signing_commitments()
.values()
.cloned()
.collect();
let commitments = signing_package.signing_commitments();
let message = signing_package.message();
let new_signing_package = SigningPackage::new(commitments, message);
let new_signing_package = SigningPackage::new(commitments.clone(), message);
assert!(signing_package == new_signing_package);
}
@ -47,10 +42,9 @@ fn check_signing_package_recreation() {
fn check_signature_share_recreation() {
let signature_share = samples::signature_share();
let identifier = signature_share.identifier();
let signature_response = signature_share.signature();
let encoded = signature_share.serialize();
let new_signature_share = SignatureShare::new(*identifier, *signature_response);
let new_signature_share = SignatureShare::deserialize(encoded).unwrap();
assert!(signature_share == new_signature_share);
}
@ -106,11 +100,10 @@ fn check_public_key_package_recreation() {
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);
let new_round1_package = round1::Package::new(vss_commitment.clone(), *signature);
assert!(round1_package == new_round1_package);
}
@ -120,12 +113,9 @@ fn check_round1_package_recreation() {
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);
let new_round2_package = round2::Package::new(*signing_share);
assert!(round2_package == new_round2_package);
}

View File

@ -25,7 +25,6 @@ fn check_signing_commitments_serialization() {
assert!(commitments == decoded_commitments);
let json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)"
@ -38,7 +37,6 @@ fn check_signing_commitments_serialization() {
// Wrong ciphersuite
let invalid_json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Wrong, SHA-512)"
@ -47,7 +45,6 @@ fn check_signing_commitments_serialization() {
// Invalid identifier
let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
}"#;
@ -55,8 +52,7 @@ fn check_signing_commitments_serialization() {
// Invalid field
let invalid_json = r#"{
"foo": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"foo": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
}"#;
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
@ -70,7 +66,6 @@ fn check_signing_commitments_serialization() {
// Extra field
let invalid_json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"extra": 1
@ -94,7 +89,6 @@ fn check_signing_package_serialization() {
let json = r#"{
"signing_commitments": {
"2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)"
@ -110,7 +104,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"0000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)"
@ -125,7 +118,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"foo": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)"
@ -140,7 +132,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)"
}
@ -154,7 +145,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)"
@ -179,8 +169,7 @@ fn check_signature_share_serialization() {
assert!(signature_share == decoded_signature_share);
let json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"signature": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)"
}"#;
let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap();
@ -189,17 +178,8 @@ fn check_signature_share_serialization() {
let invalid_json = "{}";
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Invalid identifier
let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"signature": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)"
}"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"foo": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)"
}"#;
@ -207,15 +187,13 @@ fn check_signature_share_serialization() {
// Missing field
let invalid_json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",,
"ciphersuite": "FROST(Ed448, SHAKE256)"
}"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Extra field
let invalid_json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"signature": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"extra": 1,
"ciphersuite": "FROST(Ed448, SHAKE256)"
}"#;
@ -430,7 +408,6 @@ fn check_round1_package_serialization() {
assert!(round1_package == decoded_round1_package);
let json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900"
],
@ -443,20 +420,8 @@ fn check_round1_package_serialization() {
let invalid_json = "{}";
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
// Invalid identifier
let invalid_json = r#"{
"sender_identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900"
],
"proof_of_knowledge": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f69004d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)"
}"#;
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900"
],
@ -467,7 +432,6 @@ fn check_round1_package_serialization() {
// Missing field
let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900"
],
@ -477,7 +441,6 @@ fn check_round1_package_serialization() {
// Extra field
let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900"
],
@ -499,8 +462,6 @@ fn check_round2_package_serialization() {
assert!(round2_package == decoded_round2_package);
let json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receiver_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"secret_share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)"
}"#;
@ -510,36 +471,21 @@ fn check_round2_package_serialization() {
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": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"secret_share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)"
}"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"foo": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"secret_share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"foo": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)"
}"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Missing field
let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"secret_share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)"
}"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Extra field
let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receiver_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"secret_share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"extra": 1,
"ciphersuite": "FROST(Ed448, SHAKE256)"

View File

@ -12,7 +12,7 @@ scenario in a single thread and it abstracts away any communication between peer
# // ANCHOR: tkg_gen
use frost_p256 as frost;
use rand::thread_rng;
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
let mut rng = thread_rng();
let max_signers = 5;
@ -38,7 +38,7 @@ for (identifier, secret_share) in shares {
}
let mut nonces_map = HashMap::new();
let mut commitments_map = HashMap::new();
let mut commitments_map = BTreeMap::new();
////////////////////////////////////////////////////////////////////////////
// Round 1: generating nonces and signing commitments for each participant
@ -52,8 +52,7 @@ for participant_index in 1..(min_signers as u16 + 1) {
// participant, up to _threshold_.
# // ANCHOR: round1_commit
let (nonces, commitments) = frost::round1::commit(
participant_identifier,
key_package.secret_share(),
key_packages[&participant_identifier].secret_share(),
&mut rng,
);
# // ANCHOR_END: round1_commit
@ -68,14 +67,13 @@ for participant_index in 1..(min_signers as u16 + 1) {
// This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign
// - take one (unused) commitment per signing participant
let mut signature_shares = Vec::new();
let commitments_received = commitments_map.clone().into_values().collect();
let mut signature_shares = HashMap::new();
# // ANCHOR: round2_package
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);
let signing_package = frost::SigningPackage::new(commitments_map, message);
# // ANCHOR_END: round2_package
////////////////////////////////////////////////////////////////////////////
@ -95,7 +93,7 @@ for participant_identifier in nonces_map.keys() {
// In practice, the signature share must be sent to the Coordinator
// using an authenticated channel.
signature_shares.push(signature_share);
signature_shares.insert(*participant_identifier, signature_share);
}
////////////////////////////////////////////////////////////////////////////
@ -105,7 +103,7 @@ for participant_identifier in nonces_map.keys() {
// Aggregate (also verifies the signature shares)
# // ANCHOR: aggregate
let group_signature = frost::aggregate(&signing_package, &signature_shares[..], &pubkey_package)?;
let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?;
# // ANCHOR_END: aggregate

View File

@ -80,8 +80,8 @@ for participant_index in 1..=max_signers {
.expect("should be nonzero");
received_round1_packages
.entry(receiver_participant_identifier)
.or_insert_with(Vec::new)
.push(round1_package.clone());
.or_insert_with(HashMap::new)
.insert(participant_identifier, round1_package.clone());
}
}
@ -121,11 +121,11 @@ for participant_index in 1..=max_signers {
// sent through some communication channel.
// Note that, in contrast to the previous part, here each other participant
// gets its own specific package.
for round2_package in round2_packages {
for (receiver_identifier, round2_package) in round2_packages {
received_round2_packages
.entry(*round2_package.receiver_identifier())
.or_insert_with(Vec::new)
.push(round2_package);
.entry(receiver_identifier)
.or_insert_with(HashMap::new)
.insert(participant_identifier, round2_package);
}
}

View File

@ -64,8 +64,8 @@ pub fn part1<R: RngCore + CryptoRng>(
/// must be sent to other participants.
pub fn part2(
secret_package: round1::SecretPackage,
round1_packages: &[round1::Package],
) -> Result<(round2::SecretPackage, Vec<round2::Package>), Error> {
round1_packages: &HashMap<Identifier, round1::Package>,
) -> Result<(round2::SecretPackage, HashMap<Identifier, round2::Package>), Error> {
frost::keys::dkg::part2(secret_package, round1_packages)
}
@ -80,8 +80,8 @@ pub fn part2(
/// signatures.
pub fn part3(
round2_secret_package: &round2::SecretPackage,
round1_packages: &[round1::Package],
round2_packages: &[round2::Package],
round1_packages: &HashMap<Identifier, round1::Package>,
round2_packages: &HashMap<Identifier, round2::Package>,
) -> Result<(KeyPackage, PublicKeyPackage), Error> {
frost::keys::dkg::part3(round2_secret_package, round1_packages, round2_packages)
}

View File

@ -2,6 +2,8 @@
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
use std::collections::HashMap;
use p256::{
elliptic_curve::{
hash2curve::{hash_to_field, ExpandMsgXmd},
@ -364,15 +366,11 @@ pub mod round1 {
///
/// Generates the signing nonces and commitments to be used in the signing
/// operation.
pub fn commit<RNG>(
participant_identifier: frost::Identifier<P>,
secret: &SigningShare,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
where
RNG: CryptoRng + RngCore,
{
frost::round1::commit::<P, RNG>(participant_identifier, secret, rng)
frost::round1::commit::<P, RNG>(secret, rng)
}
}
@ -388,9 +386,6 @@ pub mod round2 {
/// shares into the joint signature.
pub type SignatureShare = frost::round2::SignatureShare<P>;
/// A representation of a single signature share used in FROST structures and messages.
pub type SignatureResponse = frost::round2::SignatureResponse<P>;
/// Performed once by each participant selected for the signing operation.
///
/// Receives the message to be signed and a set of signing commitments and a set
@ -428,7 +423,7 @@ pub type Signature = frost_core::Signature<P>;
/// service attack due to publishing an invalid signature.
pub fn aggregate(
signing_package: &SigningPackage,
signature_shares: &[round2::SignatureShare],
signature_shares: &HashMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys)

View File

@ -1,6 +1,6 @@
//! Generate sample, fixed instances of structs for testing.
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_p256::{
@ -10,7 +10,7 @@ use frost_p256::{
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare},
round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey,
};
@ -38,18 +38,14 @@ pub fn signing_commitments() -> SigningCommitments {
let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2());
let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap();
let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap();
let identifier = 42u16.try_into().unwrap();
SigningCommitments::new(
identifier,
hiding_nonce_commitment,
binding_nonce_commitment,
)
SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment)
}
/// Generate a sample SigningPackage.
pub fn signing_package() -> SigningPackage {
let commitments = vec![signing_commitments()];
let identifier = 42u16.try_into().unwrap();
let commitments = BTreeMap::from([(identifier, signing_commitments())]);
let message = "hello world".as_bytes();
SigningPackage::new(commitments, message)
@ -57,11 +53,9 @@ pub fn signing_package() -> SigningPackage {
/// 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::deserialize(serialized_scalar).unwrap();
SignatureShare::new(identifier, signature_response)
SignatureShare::deserialize(serialized_scalar).unwrap()
}
/// Generate a sample SecretShare.
@ -103,7 +97,6 @@ pub fn public_key_package() -> PublicKeyPackage {
/// 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
@ -118,14 +111,13 @@ pub fn round1_package() -> round1::Package {
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
let signature = Signature::deserialize(serialized_signature).unwrap();
round1::Package::new(identifier, vss_commitment, signature)
round1::Package::new(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::deserialize(serialized_scalar).unwrap();
round2::Package::new(identifier, identifier, signing_share)
round2::Package::new(signing_share)
}

View File

@ -19,10 +19,9 @@ use helpers::samples;
#[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);
let new_commitments = SigningCommitments::new(*hiding, *binding);
assert!(commitments == new_commitments);
}
@ -31,14 +30,10 @@ fn check_signing_commitments_recreation() {
fn check_signing_package_recreation() {
let signing_package = samples::signing_package();
let commitments = signing_package
.signing_commitments()
.values()
.cloned()
.collect();
let commitments = signing_package.signing_commitments();
let message = signing_package.message();
let new_signing_package = SigningPackage::new(commitments, message);
let new_signing_package = SigningPackage::new(commitments.clone(), message);
assert!(signing_package == new_signing_package);
}
@ -47,10 +42,9 @@ fn check_signing_package_recreation() {
fn check_signature_share_recreation() {
let signature_share = samples::signature_share();
let identifier = signature_share.identifier();
let signature_response = signature_share.signature();
let encoded = signature_share.serialize();
let new_signature_share = SignatureShare::new(*identifier, *signature_response);
let new_signature_share = SignatureShare::deserialize(encoded).unwrap();
assert!(signature_share == new_signature_share);
}
@ -106,11 +100,10 @@ fn check_public_key_package_recreation() {
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);
let new_round1_package = round1::Package::new(vss_commitment.clone(), *signature);
assert!(round1_package == new_round1_package);
}
@ -120,12 +113,9 @@ fn check_round1_package_recreation() {
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);
let new_round2_package = round2::Package::new(*signing_share);
assert!(round2_package == new_round2_package);
}

View File

@ -25,7 +25,6 @@ fn check_signing_commitments_serialization() {
assert!(commitments == decoded_commitments);
let json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"hiding": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978",
"ciphersuite": "FROST(P-256, SHA-256)"
@ -38,7 +37,6 @@ fn check_signing_commitments_serialization() {
// Wrong ciphersuite
let invalid_json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"hiding": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978",
"ciphersuite": "FROST(Wrong, SHA-512)"
@ -47,7 +45,6 @@ fn check_signing_commitments_serialization() {
// Invalid identifier
let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
}"#;
@ -55,8 +52,7 @@ fn check_signing_commitments_serialization() {
// Invalid field
let invalid_json = r#"{
"foo": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"foo": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978"
}"#;
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
@ -70,7 +66,6 @@ fn check_signing_commitments_serialization() {
// Extra field
let invalid_json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"hiding": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978",
"extra": 1
@ -94,7 +89,6 @@ fn check_signing_package_serialization() {
let json = r#"{
"signing_commitments": {
"000000000000000000000000000000000000000000000000000000000000002a": {
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"hiding": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978",
"ciphersuite": "FROST(P-256, SHA-256)"
@ -110,7 +104,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"0000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978",
"ciphersuite": "FROST(P-256, SHA-256)"
@ -125,7 +118,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"000000000000000000000000000000000000000000000000000000000000002a": {
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"foo": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978",
"ciphersuite": "FROST(P-256, SHA-256)"
@ -140,7 +132,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"000000000000000000000000000000000000000000000000000000000000002a": {
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978",
"ciphersuite": "FROST(P-256, SHA-256)"
}
@ -154,7 +145,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"000000000000000000000000000000000000000000000000000000000000002a": {
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"hiding": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"binding": "037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978",
"ciphersuite": "FROST(P-256, SHA-256)"
@ -179,8 +169,7 @@ fn check_signature_share_serialization() {
assert!(signature_share == decoded_signature_share);
let json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"signature": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"share": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"ciphersuite": "FROST(P-256, SHA-256)"
}"#;
let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap();
@ -189,17 +178,8 @@ fn check_signature_share_serialization() {
let invalid_json = "{}";
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Invalid identifier
let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"signature": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"ciphersuite": "FROST(P-256, SHA-256)"
}"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"foo": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"ciphersuite": "FROST(P-256, SHA-256)"
}"#;
@ -207,15 +187,13 @@ fn check_signature_share_serialization() {
// Missing field
let invalid_json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",,
"ciphersuite": "FROST(P-256, SHA-256)"
}"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Extra field
let invalid_json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"signature": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"share": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"extra": 1,
"ciphersuite": "FROST(P-256, SHA-256)"
}"#;
@ -430,7 +408,6 @@ fn check_round1_package_serialization() {
assert!(round1_package == decoded_round1_package);
let json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"commitment": [
"036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296"
],
@ -443,20 +420,8 @@ fn check_round1_package_serialization() {
let invalid_json = "{}";
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
// Invalid identifier
let invalid_json = r#"{
"sender_identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296"
],
"proof_of_knowledge": "036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"ciphersuite": "FROST(P-256, SHA-256)"
}"#;
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"commitment": [
"036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296"
],
@ -467,7 +432,6 @@ fn check_round1_package_serialization() {
// Missing field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"commitment": [
"036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296"
],
@ -477,7 +441,6 @@ fn check_round1_package_serialization() {
// Extra field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"commitment": [
"036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296"
],
@ -499,8 +462,6 @@ fn check_round2_package_serialization() {
assert!(round2_package == decoded_round2_package);
let json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"receiver_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"secret_share": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"ciphersuite": "FROST(P-256, SHA-256)"
}"#;
@ -510,36 +471,21 @@ fn check_round2_package_serialization() {
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": "000000000000000000000000000000000000000000000000000000000000002a",
"secret_share": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"ciphersuite": "FROST(P-256, SHA-256)"
}"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"foo": "000000000000000000000000000000000000000000000000000000000000002a",
"secret_share": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"foo": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"ciphersuite": "FROST(P-256, SHA-256)"
}"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Missing field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"secret_share": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"ciphersuite": "FROST(P-256, SHA-256)"
}"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Extra field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"receiver_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"secret_share": "aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1",
"extra": 1,
"ciphersuite": "FROST(P-256, SHA-256)"

View File

@ -5,6 +5,8 @@
#[cfg(any(test, feature = "test-impl"))]
pub mod tests;
use std::collections::HashMap;
pub use frost_core;
use frost_core::{
@ -91,7 +93,7 @@ pub fn sign<C: Ciphersuite>(
/// service attack due to publishing an invalid signature.
pub fn aggregate<C>(
signing_package: &frost::SigningPackage<C>,
signature_shares: &[frost::round2::SignatureShare<C>],
signature_shares: &HashMap<frost::Identifier<C>, frost::round2::SignatureShare<C>>,
pubkeys: &frost::keys::PublicKeyPackage<C>,
randomized_params: &RandomizedParams<C>,
) -> Result<frost_core::Signature<C>, Error<C>>
@ -125,8 +127,8 @@ where
// [`aggregate`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-12.html#section-5.3
let mut z = <<C::Group as Group>::Field as Field>::zero();
for signature_share in signature_shares {
z = z + *signature_share.signature().z_share();
for signature_share in signature_shares.values() {
z = z + *signature_share.share();
}
z = z + challenge.clone().to_scalar() * randomized_params.randomizer;
@ -141,27 +143,33 @@ where
// if the aggregate signature is valid (which should be the common case).
if let Err(err) = verification_result {
// Verify the signature shares.
for signature_share in signature_shares {
for (signature_share_identifier, signature_share) in signature_shares {
// 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())
.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.
signature_share.verify(&R_share, signer_pubkey, lambda_i, &challenge)?;
signature_share.verify(
*signature_share_identifier,
&R_share,
signer_pubkey,
lambda_i,
&challenge,
)?;
}
// We should never reach here; but we return the verification error to be safe.

View File

@ -1,6 +1,6 @@
//! Ciphersuite-generic test functions for re-randomized FROST.
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use crate::{frost_core::frost, frost_core::Ciphersuite, RandomizedParams};
use frost_core::{Field, Group, Signature, VerifyingKey};
@ -35,8 +35,8 @@ pub fn check_randomized_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>
}
let mut nonces: HashMap<frost::Identifier<C>, frost::round1::SigningNonces<C>> = HashMap::new();
let mut commitments: HashMap<frost::Identifier<C>, frost::round1::SigningCommitments<C>> =
HashMap::new();
let mut commitments: BTreeMap<frost::Identifier<C>, frost::round1::SigningCommitments<C>> =
BTreeMap::new();
check_from_randomizer(&pubkeys, &mut rng);
let randomizer_params = RandomizedParams::new(&pubkeys, &mut rng);
@ -50,7 +50,6 @@ pub fn check_randomized_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>
// Generate one (1) nonce and one SigningCommitments instance for each
// participant, up to _min_signers_.
let (nonce, commitment) = frost::round1::commit(
participant_identifier,
key_packages
.get(&participant_identifier)
.unwrap()
@ -64,10 +63,10 @@ pub fn check_randomized_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>
// This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign
// - take one (unused) commitment per signing participant
let mut signature_shares: Vec<frost::round2::SignatureShare<_>> = Vec::new();
let mut signature_shares: HashMap<frost::Identifier<_>, frost::round2::SignatureShare<_>> =
HashMap::new();
let message = "message to sign".as_bytes();
let comms = commitments.clone().into_values().collect();
let signing_package = frost::SigningPackage::new(comms, message);
let signing_package = frost::SigningPackage::new(commitments, message);
////////////////////////////////////////////////////////////////////////////
// Round 2: each participant generates their signature share
@ -86,7 +85,7 @@ pub fn check_randomized_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>
randomizer_params.randomizer_point(),
)
.unwrap();
signature_shares.push(signature_share);
signature_shares.insert(*participant_identifier, signature_share);
}
////////////////////////////////////////////////////////////////////////////
@ -97,7 +96,7 @@ pub fn check_randomized_sign_with_dealer<C: Ciphersuite, R: RngCore + CryptoRng>
// Aggregate (also verifies the signature shares)
let group_signature_res = crate::aggregate(
&signing_package,
&signature_shares[..],
&signature_shares,
&pubkeys,
&randomizer_params,
);

View File

@ -12,7 +12,7 @@ scenario in a single thread and it abstracts away any communication between peer
# // ANCHOR: tkg_gen
use frost_ristretto255 as frost;
use rand::thread_rng;
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
let mut rng = thread_rng();
let max_signers = 5;
@ -38,7 +38,7 @@ for (identifier, secret_share) in shares {
}
let mut nonces_map = HashMap::new();
let mut commitments_map = HashMap::new();
let mut commitments_map = BTreeMap::new();
////////////////////////////////////////////////////////////////////////////
// Round 1: generating nonces and signing commitments for each participant
@ -52,8 +52,7 @@ for participant_index in 1..(min_signers as u16 + 1) {
// participant, up to _threshold_.
# // ANCHOR: round1_commit
let (nonces, commitments) = frost::round1::commit(
participant_identifier,
key_package.secret_share(),
key_packages[&participant_identifier].secret_share(),
&mut rng,
);
# // ANCHOR_END: round1_commit
@ -68,14 +67,13 @@ for participant_index in 1..(min_signers as u16 + 1) {
// This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign
// - take one (unused) commitment per signing participant
let mut signature_shares = Vec::new();
let commitments_received = commitments_map.clone().into_values().collect();
let mut signature_shares = HashMap::new();
# // ANCHOR: round2_package
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);
let signing_package = frost::SigningPackage::new(commitments_map, message);
# // ANCHOR_END: round2_package
////////////////////////////////////////////////////////////////////////////
@ -95,7 +93,7 @@ for participant_identifier in nonces_map.keys() {
// In practice, the signature share must be sent to the Coordinator
// using an authenticated channel.
signature_shares.push(signature_share);
signature_shares.insert(*participant_identifier, signature_share);
}
////////////////////////////////////////////////////////////////////////////
@ -105,7 +103,7 @@ for participant_identifier in nonces_map.keys() {
// Aggregate (also verifies the signature shares)
# // ANCHOR: aggregate
let group_signature = frost::aggregate(&signing_package, &signature_shares[..], &pubkey_package)?;
let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?;
# // ANCHOR_END: aggregate

View File

@ -80,8 +80,8 @@ for participant_index in 1..=max_signers {
.expect("should be nonzero");
received_round1_packages
.entry(receiver_participant_identifier)
.or_insert_with(Vec::new)
.push(round1_package.clone());
.or_insert_with(HashMap::new)
.insert(participant_identifier, round1_package.clone());
}
}
@ -121,11 +121,11 @@ for participant_index in 1..=max_signers {
// sent through some communication channel.
// Note that, in contrast to the previous part, here each other participant
// gets its own specific package.
for round2_package in round2_packages {
for (receiver_identifier, round2_package) in round2_packages {
received_round2_packages
.entry(*round2_package.receiver_identifier())
.or_insert_with(Vec::new)
.push(round2_package);
.entry(receiver_identifier)
.or_insert_with(HashMap::new)
.insert(participant_identifier, round2_package);
}
}

View File

@ -64,8 +64,8 @@ pub fn part1<R: RngCore + CryptoRng>(
/// must be sent to other participants.
pub fn part2(
secret_package: round1::SecretPackage,
round1_packages: &[round1::Package],
) -> Result<(round2::SecretPackage, Vec<round2::Package>), Error> {
round1_packages: &HashMap<Identifier, round1::Package>,
) -> Result<(round2::SecretPackage, HashMap<Identifier, round2::Package>), Error> {
frost::keys::dkg::part2(secret_package, round1_packages)
}
@ -80,8 +80,8 @@ pub fn part2(
/// signatures.
pub fn part3(
round2_secret_package: &round2::SecretPackage,
round1_packages: &[round1::Package],
round2_packages: &[round2::Package],
round1_packages: &HashMap<Identifier, round1::Package>,
round2_packages: &HashMap<Identifier, round2::Package>,
) -> Result<(KeyPackage, PublicKeyPackage), Error> {
frost::keys::dkg::part3(round2_secret_package, round1_packages, round2_packages)
}

View File

@ -2,6 +2,8 @@
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
use std::collections::HashMap;
use curve25519_dalek::{
constants::RISTRETTO_BASEPOINT_POINT,
ristretto::{CompressedRistretto, RistrettoPoint},
@ -326,15 +328,11 @@ pub mod round1 {
///
/// Generates the signing nonces and commitments to be used in the signing
/// operation.
pub fn commit<RNG>(
participant_identifier: frost::Identifier<R>,
secret: &SigningShare,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
where
RNG: CryptoRng + RngCore,
{
frost::round1::commit::<R, RNG>(participant_identifier, secret, rng)
frost::round1::commit::<R, RNG>(secret, rng)
}
}
@ -350,9 +348,6 @@ pub mod round2 {
/// shares into the joint signature.
pub type SignatureShare = frost::round2::SignatureShare<R>;
/// A representation of a single signature share used in FROST structures and messages.
pub type SignatureResponse = frost::round2::SignatureResponse<R>;
/// Performed once by each participant selected for the signing operation.
///
/// Receives the message to be signed and a set of signing commitments and a set
@ -390,7 +385,7 @@ pub type Signature = frost_core::Signature<R>;
/// service attack due to publishing an invalid signature.
pub fn aggregate(
signing_package: &SigningPackage,
signature_shares: &[round2::SignatureShare],
signature_shares: &HashMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys)

View File

@ -1,6 +1,6 @@
//! Generate sample, fixed instances of structs for testing.
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_ristretto255::{
@ -10,7 +10,7 @@ use frost_ristretto255::{
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare},
round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey,
};
@ -38,18 +38,14 @@ pub fn signing_commitments() -> SigningCommitments {
let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2());
let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap();
let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap();
let identifier = 42u16.try_into().unwrap();
SigningCommitments::new(
identifier,
hiding_nonce_commitment,
binding_nonce_commitment,
)
SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment)
}
/// Generate a sample SigningPackage.
pub fn signing_package() -> SigningPackage {
let commitments = vec![signing_commitments()];
let identifier = 42u16.try_into().unwrap();
let commitments = BTreeMap::from([(identifier, signing_commitments())]);
let message = "hello world".as_bytes();
SigningPackage::new(commitments, message)
@ -57,11 +53,9 @@ pub fn signing_package() -> SigningPackage {
/// 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::deserialize(serialized_scalar).unwrap();
SignatureShare::new(identifier, signature_response)
SignatureShare::deserialize(serialized_scalar).unwrap()
}
/// Generate a sample SecretShare.
@ -103,7 +97,6 @@ pub fn public_key_package() -> PublicKeyPackage {
/// 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
@ -118,14 +111,13 @@ pub fn round1_package() -> round1::Package {
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
let signature = Signature::deserialize(serialized_signature).unwrap();
round1::Package::new(identifier, vss_commitment, signature)
round1::Package::new(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::deserialize(serialized_scalar).unwrap();
round2::Package::new(identifier, identifier, signing_share)
round2::Package::new(signing_share)
}

View File

@ -19,10 +19,9 @@ use helpers::samples;
#[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);
let new_commitments = SigningCommitments::new(*hiding, *binding);
assert!(commitments == new_commitments);
}
@ -31,14 +30,10 @@ fn check_signing_commitments_recreation() {
fn check_signing_package_recreation() {
let signing_package = samples::signing_package();
let commitments = signing_package
.signing_commitments()
.values()
.cloned()
.collect();
let commitments = signing_package.signing_commitments();
let message = signing_package.message();
let new_signing_package = SigningPackage::new(commitments, message);
let new_signing_package = SigningPackage::new(commitments.clone(), message);
assert!(signing_package == new_signing_package);
}
@ -47,10 +42,9 @@ fn check_signing_package_recreation() {
fn check_signature_share_recreation() {
let signature_share = samples::signature_share();
let identifier = signature_share.identifier();
let signature_response = signature_share.signature();
let encoded = signature_share.serialize();
let new_signature_share = SignatureShare::new(*identifier, *signature_response);
let new_signature_share = SignatureShare::deserialize(encoded).unwrap();
assert!(signature_share == new_signature_share);
}
@ -106,11 +100,10 @@ fn check_public_key_package_recreation() {
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);
let new_round1_package = round1::Package::new(vss_commitment.clone(), *signature);
assert!(round1_package == new_round1_package);
}
@ -120,12 +113,9 @@ fn check_round1_package_recreation() {
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);
let new_round2_package = round2::Package::new(*signing_share);
assert!(round2_package == new_round2_package);
}

View File

@ -25,7 +25,6 @@ fn check_signing_commitments_serialization() {
assert!(commitments == decoded_commitments);
let json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)"
@ -38,7 +37,6 @@ fn check_signing_commitments_serialization() {
// Wrong ciphersuite
let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(Wrong, SHA-512)"
@ -47,7 +45,6 @@ fn check_signing_commitments_serialization() {
// Invalid identifier
let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
}"#;
@ -55,8 +52,7 @@ fn check_signing_commitments_serialization() {
// Invalid field
let invalid_json = r#"{
"foo": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"foo": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
}"#;
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
@ -70,7 +66,6 @@ fn check_signing_commitments_serialization() {
// Extra field
let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"extra": 1
@ -94,7 +89,6 @@ fn check_signing_package_serialization() {
let json = r#"{
"signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)"
@ -110,7 +104,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"0000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)"
@ -125,7 +118,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"foo": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)"
@ -140,7 +132,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)"
}
@ -154,7 +145,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)"
@ -179,8 +169,7 @@ fn check_signature_share_serialization() {
assert!(signature_share == decoded_signature_share);
let json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"signature": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(ristretto255, SHA-512)"
}"#;
let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap();
@ -189,17 +178,8 @@ fn check_signature_share_serialization() {
let invalid_json = "{}";
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Invalid identifier
let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"signature": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(ristretto255, SHA-512)"
}"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"foo": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(ristretto255, SHA-512)"
}"#;
@ -207,15 +187,13 @@ fn check_signature_share_serialization() {
// 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": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"extra": 1,
"ciphersuite": "FROST(ristretto255, SHA-512)"
}"#;
@ -430,7 +408,6 @@ fn check_round1_package_serialization() {
assert!(round1_package == decoded_round1_package);
let json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
],
@ -443,20 +420,8 @@ fn check_round1_package_serialization() {
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": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"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"
],
@ -467,7 +432,6 @@ fn check_round1_package_serialization() {
// Missing field
let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
],
@ -477,7 +441,6 @@ fn check_round1_package_serialization() {
// Extra field
let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
],
@ -499,8 +462,6 @@ fn check_round2_package_serialization() {
assert!(round2_package == decoded_round2_package);
let json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"receiver_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(ristretto255, SHA-512)"
}"#;
@ -510,36 +471,21 @@ fn check_round2_package_serialization() {
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": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"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": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"foo": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"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": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"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": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"extra": 1,
"ciphersuite": "FROST(ristretto255, SHA-512)"

View File

@ -12,7 +12,7 @@ scenario in a single thread and it abstracts away any communication between peer
# // ANCHOR: tkg_gen
use frost_secp256k1 as frost;
use rand::thread_rng;
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
let mut rng = thread_rng();
let max_signers = 5;
@ -38,7 +38,7 @@ for (identifier, secret_share) in shares {
}
let mut nonces_map = HashMap::new();
let mut commitments_map = HashMap::new();
let mut commitments_map = BTreeMap::new();
////////////////////////////////////////////////////////////////////////////
// Round 1: generating nonces and signing commitments for each participant
@ -52,8 +52,7 @@ for participant_index in 1..(min_signers as u16 + 1) {
// participant, up to _threshold_.
# // ANCHOR: round1_commit
let (nonces, commitments) = frost::round1::commit(
participant_identifier,
key_package.secret_share(),
key_packages[&participant_identifier].secret_share(),
&mut rng,
);
# // ANCHOR_END: round1_commit
@ -68,14 +67,13 @@ for participant_index in 1..(min_signers as u16 + 1) {
// This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign
// - take one (unused) commitment per signing participant
let mut signature_shares = Vec::new();
let commitments_received = commitments_map.clone().into_values().collect();
let mut signature_shares = HashMap::new();
# // ANCHOR: round2_package
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);
let signing_package = frost::SigningPackage::new(commitments_map, message);
# // ANCHOR_END: round2_package
////////////////////////////////////////////////////////////////////////////
@ -95,7 +93,7 @@ for participant_identifier in nonces_map.keys() {
// In practice, the signature share must be sent to the Coordinator
// using an authenticated channel.
signature_shares.push(signature_share);
signature_shares.insert(*participant_identifier, signature_share);
}
////////////////////////////////////////////////////////////////////////////
@ -105,7 +103,7 @@ for participant_identifier in nonces_map.keys() {
// Aggregate (also verifies the signature shares)
# // ANCHOR: aggregate
let group_signature = frost::aggregate(&signing_package, &signature_shares[..], &pubkey_package)?;
let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?;
# // ANCHOR_END: aggregate

View File

@ -80,8 +80,8 @@ for participant_index in 1..=max_signers {
.expect("should be nonzero");
received_round1_packages
.entry(receiver_participant_identifier)
.or_insert_with(Vec::new)
.push(round1_package.clone());
.or_insert_with(HashMap::new)
.insert(participant_identifier, round1_package.clone());
}
}
@ -121,11 +121,11 @@ for participant_index in 1..=max_signers {
// sent through some communication channel.
// Note that, in contrast to the previous part, here each other participant
// gets its own specific package.
for round2_package in round2_packages {
for (receiver_identifier, round2_package) in round2_packages {
received_round2_packages
.entry(*round2_package.receiver_identifier())
.or_insert_with(Vec::new)
.push(round2_package);
.entry(receiver_identifier)
.or_insert_with(HashMap::new)
.insert(participant_identifier, round2_package);
}
}

View File

@ -64,8 +64,8 @@ pub fn part1<R: RngCore + CryptoRng>(
/// must be sent to other participants.
pub fn part2(
secret_package: round1::SecretPackage,
round1_packages: &[round1::Package],
) -> Result<(round2::SecretPackage, Vec<round2::Package>), Error> {
round1_packages: &HashMap<Identifier, round1::Package>,
) -> Result<(round2::SecretPackage, HashMap<Identifier, round2::Package>), Error> {
frost::keys::dkg::part2(secret_package, round1_packages)
}
@ -80,8 +80,8 @@ pub fn part2(
/// signatures.
pub fn part3(
round2_secret_package: &round2::SecretPackage,
round1_packages: &[round1::Package],
round2_packages: &[round2::Package],
round1_packages: &HashMap<Identifier, round1::Package>,
round2_packages: &HashMap<Identifier, round2::Package>,
) -> Result<(KeyPackage, PublicKeyPackage), Error> {
frost::keys::dkg::part3(round2_secret_package, round1_packages, round2_packages)
}

View File

@ -2,6 +2,8 @@
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
use std::collections::HashMap;
use k256::{
elliptic_curve::{
group::prime::PrimeCurveAffine,
@ -364,15 +366,11 @@ pub mod round1 {
///
/// Generates the signing nonces and commitments to be used in the signing
/// operation.
pub fn commit<RNG>(
participant_identifier: frost::Identifier<S>,
secret: &SigningShare,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
where
RNG: CryptoRng + RngCore,
{
frost::round1::commit::<S, RNG>(participant_identifier, secret, rng)
frost::round1::commit::<S, RNG>(secret, rng)
}
}
@ -388,9 +386,6 @@ pub mod round2 {
/// shares into the joint signature.
pub type SignatureShare = frost::round2::SignatureShare<S>;
/// A representation of a single signature share used in FROST structures and messages.
pub type SignatureResponse = frost::round2::SignatureResponse<S>;
/// Performed once by each participant selected for the signing operation.
///
/// Receives the message to be signed and a set of signing commitments and a set
@ -428,7 +423,7 @@ pub type Signature = frost_core::Signature<S>;
/// service attack due to publishing an invalid signature.
pub fn aggregate(
signing_package: &SigningPackage,
signature_shares: &[round2::SignatureShare],
signature_shares: &HashMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys)

View File

@ -1,6 +1,6 @@
//! Generate sample, fixed instances of structs for testing.
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use frost_core::{Ciphersuite, Element, Group, Scalar};
use frost_secp256k1::{
@ -10,7 +10,7 @@ use frost_secp256k1::{
VerifyingShare,
},
round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare},
round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey,
};
@ -38,18 +38,14 @@ pub fn signing_commitments() -> SigningCommitments {
let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2());
let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap();
let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap();
let identifier = 42u16.try_into().unwrap();
SigningCommitments::new(
identifier,
hiding_nonce_commitment,
binding_nonce_commitment,
)
SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment)
}
/// Generate a sample SigningPackage.
pub fn signing_package() -> SigningPackage {
let commitments = vec![signing_commitments()];
let identifier = 42u16.try_into().unwrap();
let commitments = BTreeMap::from([(identifier, signing_commitments())]);
let message = "hello world".as_bytes();
SigningPackage::new(commitments, message)
@ -57,11 +53,9 @@ pub fn signing_package() -> SigningPackage {
/// 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::deserialize(serialized_scalar).unwrap();
SignatureShare::new(identifier, signature_response)
SignatureShare::deserialize(serialized_scalar).unwrap()
}
/// Generate a sample SecretShare.
@ -103,7 +97,6 @@ pub fn public_key_package() -> PublicKeyPackage {
/// 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
@ -118,14 +111,13 @@ pub fn round1_package() -> round1::Package {
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
let signature = Signature::deserialize(serialized_signature).unwrap();
round1::Package::new(identifier, vss_commitment, signature)
round1::Package::new(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::deserialize(serialized_scalar).unwrap();
round2::Package::new(identifier, identifier, signing_share)
round2::Package::new(signing_share)
}

View File

@ -19,10 +19,9 @@ use helpers::samples;
#[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);
let new_commitments = SigningCommitments::new(*hiding, *binding);
assert!(commitments == new_commitments);
}
@ -31,14 +30,10 @@ fn check_signing_commitments_recreation() {
fn check_signing_package_recreation() {
let signing_package = samples::signing_package();
let commitments = signing_package
.signing_commitments()
.values()
.cloned()
.collect();
let commitments = signing_package.signing_commitments();
let message = signing_package.message();
let new_signing_package = SigningPackage::new(commitments, message);
let new_signing_package = SigningPackage::new(commitments.clone(), message);
assert!(signing_package == new_signing_package);
}
@ -47,10 +42,9 @@ fn check_signing_package_recreation() {
fn check_signature_share_recreation() {
let signature_share = samples::signature_share();
let identifier = signature_share.identifier();
let signature_response = signature_share.signature();
let encoded = signature_share.serialize();
let new_signature_share = SignatureShare::new(*identifier, *signature_response);
let new_signature_share = SignatureShare::deserialize(encoded).unwrap();
assert!(signature_share == new_signature_share);
}
@ -106,11 +100,10 @@ fn check_public_key_package_recreation() {
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);
let new_round1_package = round1::Package::new(vss_commitment.clone(), *signature);
assert!(round1_package == new_round1_package);
}
@ -120,12 +113,9 @@ fn check_round1_package_recreation() {
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);
let new_round2_package = round2::Package::new(*signing_share);
assert!(round2_package == new_round2_package);
}

View File

@ -25,7 +25,6 @@ fn check_signing_commitments_serialization() {
assert!(commitments == decoded_commitments);
let json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
"ciphersuite": "FROST(secp256k1, SHA-256)"
@ -38,7 +37,6 @@ fn check_signing_commitments_serialization() {
// Wrong ciphersuite
let invalid_json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
"ciphersuite": "FROST(Wrong, SHA-512)"
@ -47,7 +45,6 @@ fn check_signing_commitments_serialization() {
// Invalid identifier
let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
}"#;
@ -55,8 +52,7 @@ fn check_signing_commitments_serialization() {
// Invalid field
let invalid_json = r#"{
"foo": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"foo": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
}"#;
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
@ -70,7 +66,6 @@ fn check_signing_commitments_serialization() {
// Extra field
let invalid_json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
"extra": 1
@ -94,7 +89,6 @@ fn check_signing_package_serialization() {
let json = r#"{
"signing_commitments": {
"000000000000000000000000000000000000000000000000000000000000002a": {
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
"ciphersuite": "FROST(secp256k1, SHA-256)"
@ -110,7 +104,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"0000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
"ciphersuite": "FROST(secp256k1, SHA-256)"
@ -125,7 +118,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"000000000000000000000000000000000000000000000000000000000000002a": {
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"foo": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
"ciphersuite": "FROST(secp256k1, SHA-256)"
@ -140,7 +132,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"000000000000000000000000000000000000000000000000000000000000002a": {
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
"ciphersuite": "FROST(secp256k1, SHA-256)"
}
@ -154,7 +145,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{
"signing_commitments": {
"000000000000000000000000000000000000000000000000000000000000002a": {
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"hiding": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"binding": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
"ciphersuite": "FROST(secp256k1, SHA-256)"
@ -179,8 +169,7 @@ fn check_signature_share_serialization() {
assert!(signature_share == decoded_signature_share);
let json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"signature": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"ciphersuite": "FROST(secp256k1, SHA-256)"
}"#;
let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap();
@ -189,17 +178,8 @@ fn check_signature_share_serialization() {
let invalid_json = "{}";
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Invalid identifier
let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"signature": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"ciphersuite": "FROST(secp256k1, SHA-256)"
}"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"ciphersuite": "FROST(secp256k1, SHA-256)"
}"#;
@ -207,15 +187,13 @@ fn check_signature_share_serialization() {
// Missing field
let invalid_json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",,
"ciphersuite": "FROST(secp256k1, SHA-256)"
}"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Extra field
let invalid_json = r#"{
"identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"signature": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"extra": 1,
"ciphersuite": "FROST(secp256k1, SHA-256)"
}"#;
@ -430,7 +408,6 @@ fn check_round1_package_serialization() {
assert!(round1_package == decoded_round1_package);
let json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"commitment": [
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
],
@ -443,20 +420,8 @@ fn check_round1_package_serialization() {
let invalid_json = "{}";
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
// Invalid identifier
let invalid_json = r#"{
"sender_identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"commitment": [
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
],
"proof_of_knowledge": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"ciphersuite": "FROST(secp256k1, SHA-256)"
}"#;
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"commitment": [
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
],
@ -467,7 +432,6 @@ fn check_round1_package_serialization() {
// Missing field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"commitment": [
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
],
@ -477,7 +441,6 @@ fn check_round1_package_serialization() {
// Extra field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"commitment": [
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
],
@ -499,8 +462,6 @@ fn check_round2_package_serialization() {
assert!(round2_package == decoded_round2_package);
let json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"receiver_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"secret_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"ciphersuite": "FROST(secp256k1, SHA-256)"
}"#;
@ -510,36 +471,21 @@ fn check_round2_package_serialization() {
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": "000000000000000000000000000000000000000000000000000000000000002a",
"secret_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"ciphersuite": "FROST(secp256k1, SHA-256)"
}"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Invalid field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"foo": "000000000000000000000000000000000000000000000000000000000000002a",
"secret_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"foo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"ciphersuite": "FROST(secp256k1, SHA-256)"
}"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Missing field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"secret_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"ciphersuite": "FROST(secp256k1, SHA-256)"
}"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Extra field
let invalid_json = r#"{
"sender_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"receiver_identifier": "000000000000000000000000000000000000000000000000000000000000002a",
"secret_share": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b81",
"extra": 1,
"ciphersuite": "FROST(secp256k1, SHA-256)"