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 ## 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()` * rename all `to_bytes()`/`from_bytes()` to `serialize()`/`deserialize()`
## Released ## Released

View File

@ -1,6 +1,6 @@
//! Ciphersuite-generic benchmark functions. //! Ciphersuite-generic benchmark functions.
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
use criterion::{BenchmarkId, Criterion, Throughput}; use criterion::{BenchmarkId, Criterion, Throughput};
use rand_core::{CryptoRng, RngCore}; use rand_core::{CryptoRng, RngCore};
@ -126,7 +126,6 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
b.iter(|| { b.iter(|| {
let participant_identifier = 1u16.try_into().expect("should be nonzero"); let participant_identifier = 1u16.try_into().expect("should be nonzero");
frost::round1::commit( frost::round1::commit(
participant_identifier,
key_packages key_packages
.get(&participant_identifier) .get(&participant_identifier)
.unwrap() .unwrap()
@ -138,12 +137,11 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
); );
let mut nonces: HashMap<_, _> = HashMap::new(); 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 { for participant_index in 1..=min_signers {
let participant_identifier = participant_index.try_into().expect("should be nonzero"); let participant_identifier = participant_index.try_into().expect("should be nonzero");
let (nonce, commitment) = frost::round1::commit( let (nonce, commitment) = frost::round1::commit(
participant_identifier,
key_packages key_packages
.get(&participant_identifier) .get(&participant_identifier)
.unwrap() .unwrap()
@ -155,8 +153,7 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
} }
let message = "message to sign".as_bytes(); let message = "message to sign".as_bytes();
let comms = commitments.clone().into_values().collect(); let signing_package = frost::SigningPackage::new(commitments, message);
let signing_package = frost::SigningPackage::new(comms, message);
group.bench_with_input( group.bench_with_input(
BenchmarkId::new("Round 2", min_signers), 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() { for participant_identifier in nonces.keys() {
let key_package = key_packages.get(participant_identifier).unwrap(); let key_package = key_packages.get(participant_identifier).unwrap();
let nonces_to_use = &nonces.get(participant_identifier).unwrap(); let nonces_to_use = &nonces.get(participant_identifier).unwrap();
let signature_share = let signature_share =
frost::round2::sign(&signing_package, nonces_to_use, key_package).unwrap(); 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( 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), &(signing_package.clone(), signature_shares.clone(), pubkeys),
|b, (signing_package, signature_shares, pubkeys)| { |b, (signing_package, signature_shares, pubkeys)| {
b.iter(|| { 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. //! Sharing, where shares are generated using Shamir Secret Sharing.
use std::{ use std::{
collections::BTreeMap, collections::{BTreeMap, HashMap},
fmt::{self, Debug}, fmt::{self, Debug},
ops::Index, 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 // 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 // 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 in signing_package.signing_commitments().keys() {
if commitment.identifier == *signer_id { if *commitment_identifier == *signer_id {
continue; continue;
} }
num *= commitment.identifier; num *= *commitment_identifier;
den *= commitment.identifier - *signer_id; den *= *commitment_identifier - *signer_id;
} }
if den == zero { if den == zero {
@ -189,7 +189,7 @@ fn derive_interpolating_value<C: Ciphersuite>(
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct SigningPackage<C: Ciphersuite> { pub struct SigningPackage<C: Ciphersuite> {
/// The set of commitments participants published in the first round of the /// 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>>, signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
/// Message which each participant will sign. /// Message which each participant will sign.
/// ///
@ -224,14 +224,11 @@ where
/// ///
/// The `signing_commitments` are sorted by participant `identifier`. /// The `signing_commitments` are sorted by participant `identifier`.
pub fn new( pub fn new(
signing_commitments: Vec<round1::SigningCommitments<C>>, signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
message: &[u8], message: &[u8],
) -> SigningPackage<C> { ) -> SigningPackage<C> {
SigningPackage { SigningPackage {
signing_commitments: signing_commitments signing_commitments,
.into_iter()
.map(|s| (s.identifier, s))
.collect(),
message: message.to_vec(), message: message.to_vec(),
ciphersuite: (), ciphersuite: (),
} }
@ -261,13 +258,13 @@ where
binding_factor_input_prefix.extend_from_slice(additional_prefix); binding_factor_input_prefix.extend_from_slice(additional_prefix);
self.signing_commitments() self.signing_commitments()
.values() .keys()
.map(|c| { .map(|identifier| {
let mut binding_factor_input = vec![]; let mut binding_factor_input = vec![];
binding_factor_input.extend_from_slice(&binding_factor_input_prefix); binding_factor_input.extend_from_slice(&binding_factor_input_prefix);
binding_factor_input.extend_from_slice(c.identifier.serialize().as_ref()); binding_factor_input.extend_from_slice(identifier.serialize().as_ref());
(c.identifier, binding_factor_input) (*identifier, binding_factor_input)
}) })
.collect() .collect()
} }
@ -317,14 +314,14 @@ where
// Ala the sorting of B, just always sort by identifier in ascending order // 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 // 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. // The following check prevents a party from accidentally revealing their share.
// Note that the '&&' operator would be sufficient. // Note that the '&&' operator would be sufficient.
if identity == commitment.binding.0 || identity == commitment.hiding.0 { if identity == commitment.binding.0 || identity == commitment.hiding.0 {
return Err(Error::IdentityCommitment); 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 // Collect the binding commitments and their binding factors for one big
// multiscalar multiplication at the end. // multiscalar multiplication at the end.
@ -349,6 +346,12 @@ where
/// Aggregates the signature shares to produce a final signature that /// Aggregates the signature shares to produce a final signature that
/// can be verified with the group public key. /// 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 /// This operation is performed by a coordinator that can communicate with all
/// the signing participants before publishing the final signature. The /// the signing participants before publishing the final signature. The
/// coordinator can be one of the participants or a semi-trusted third party /// 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. /// service attack due to publishing an invalid signature.
pub fn aggregate<C>( pub fn aggregate<C>(
signing_package: &SigningPackage<C>, signing_package: &SigningPackage<C>,
signature_shares: &[round2::SignatureShare<C>], signature_shares: &HashMap<Identifier<C>, round2::SignatureShare<C>>,
pubkeys: &keys::PublicKeyPackage<C>, pubkeys: &keys::PublicKeyPackage<C>,
) -> Result<Signature<C>, Error<C>> ) -> Result<Signature<C>, Error<C>>
where where
@ -382,8 +385,8 @@ where
// [`aggregate`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-5.3 // [`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(); let mut z = <<C::Group as Group>::Field>::zero();
for signature_share in signature_shares { for signature_share in signature_shares.values() {
z = z + signature_share.signature.z_share; z = z + signature_share.share;
} }
let signature = Signature { let signature = Signature {
@ -408,27 +411,32 @@ where
); );
// Verify the signature shares. // 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])_, // 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. // and where s[i] is a secret share of the constant term of _f_, the secret polynomial.
let signer_pubkey = pubkeys let signer_pubkey = pubkeys
.signer_pubkeys .signer_pubkeys
.get(&signature_share.identifier) .get(signature_share_identifier)
.unwrap(); .unwrap();
// Compute Lagrange coefficient. // Compute Lagrange coefficient.
let lambda_i = let lambda_i = derive_interpolating_value(signature_share_identifier, signing_package)?;
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. // Compute the commitment share.
let R_share = signing_package let R_share = signing_package
.signing_commitment(&signature_share.identifier) .signing_commitment(signature_share_identifier)
.to_group_commitment_share(&binding_factor); .to_group_commitment_share(&binding_factor);
// Compute relation values to verify this signature share. // 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. // 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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Identifier") 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() .finish()
} }
} }

View File

@ -58,8 +58,6 @@ pub mod round1 {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct Package<C: Ciphersuite> { 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) /// The public commitment from the participant (C_i)
pub(crate) commitment: VerifiableSecretSharingCommitment<C>, pub(crate) commitment: VerifiableSecretSharingCommitment<C>,
/// The proof of knowledge of the temporary secret (σ_i = (R_i, μ_i)) /// The proof of knowledge of the temporary secret (σ_i = (R_i, μ_i))
@ -83,12 +81,10 @@ pub mod round1 {
{ {
/// Create a new [`Package`] instance. /// Create a new [`Package`] instance.
pub fn new( pub fn new(
sender_identifier: Identifier<C>,
commitment: VerifiableSecretSharingCommitment<C>, commitment: VerifiableSecretSharingCommitment<C>,
proof_of_knowledge: Signature<C>, proof_of_knowledge: Signature<C>,
) -> Self { ) -> Self {
Self { Self {
sender_identifier,
commitment, commitment,
proof_of_knowledge, proof_of_knowledge,
ciphersuite: (), ciphersuite: (),
@ -159,10 +155,6 @@ pub mod round2 {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct Package<C: Ciphersuite> { 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. /// The secret share being sent.
pub(crate) secret_share: SigningShare<C>, pub(crate) secret_share: SigningShare<C>,
/// Ciphersuite ID for serialization /// Ciphersuite ID for serialization
@ -183,14 +175,8 @@ pub mod round2 {
C: Ciphersuite, C: Ciphersuite,
{ {
/// Create a new [`Package`] instance. /// Create a new [`Package`] instance.
pub fn new( pub fn new(secret_share: SigningShare<C>) -> Self {
sender_identifier: Identifier<C>,
receiver_identifier: Identifier<C>,
secret_share: SigningShare<C>,
) -> Self {
Self { Self {
sender_identifier,
receiver_identifier,
secret_share, secret_share,
ciphersuite: (), ciphersuite: (),
} }
@ -284,7 +270,6 @@ pub fn part1<C: Ciphersuite, R: RngCore + CryptoRng>(
max_signers, max_signers,
}; };
let package = round1::Package { let package = round1::Package {
sender_identifier: identifier,
commitment, commitment,
proof_of_knowledge: Signature { R: R_i, z: mu_i }, proof_of_knowledge: Signature { R: R_i, z: mu_i },
ciphersuite: (), ciphersuite: (),
@ -315,21 +300,33 @@ where
/// for the participant holding the given [`round1::SecretPackage`], /// for the participant holding the given [`round1::SecretPackage`],
/// given the received [`round1::Package`]s received from the other participants. /// 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 /// It returns the [`round2::SecretPackage`] that must be kept in memory
/// by the participant for the final step, and the [`round2::Package`]s that /// by the participant for the final step, and the a map of [`round2::Package`]s that
/// must be sent to other participants. /// must be sent to each participant who has the given identifier in the map key.
pub fn part2<C: Ciphersuite>( pub fn part2<C: Ciphersuite>(
secret_package: round1::SecretPackage<C>, secret_package: round1::SecretPackage<C>,
round1_packages: &[round1::Package<C>], round1_packages: &HashMap<Identifier<C>, round1::Package<C>>,
) -> Result<(round2::SecretPackage<C>, Vec<round2::Package<C>>), Error<C>> { ) -> Result<
(
round2::SecretPackage<C>,
HashMap<Identifier<C>, round2::Package<C>>,
),
Error<C>,
> {
if round1_packages.len() != (secret_package.max_signers - 1) as usize { if round1_packages.len() != (secret_package.max_signers - 1) as usize {
return Err(Error::IncorrectNumberOfPackages); return Err(Error::IncorrectNumberOfPackages);
} }
let mut round2_packages = Vec::new(); let mut round2_packages = HashMap::new();
for round1_package in round1_packages { for (sender_identifier, round1_package) in round1_packages {
let ell = round1_package.sender_identifier; let ell = *sender_identifier;
// Round 1, Step 5 // Round 1, Step 5
// //
// > Upon receiving C⃗_, σ_ from participants 1 ≤ ≤ n, ≠ i, participant // > Upon receiving C⃗_, σ_ from participants 1 ≤ ≤ n, ≠ i, participant
@ -351,12 +348,13 @@ pub fn part2<C: Ciphersuite>(
// > which they keep for themselves. // > which they keep for themselves.
let value = evaluate_polynomial(ell, &secret_package.coefficients); let value = evaluate_polynomial(ell, &secret_package.coefficients);
round2_packages.push(round2::Package { round2_packages.insert(
sender_identifier: secret_package.identifier, ell,
receiver_identifier: ell, round2::Package {
secret_share: SigningShare(value), secret_share: SigningShare(value),
ciphersuite: (), ciphersuite: (),
}); },
);
} }
let fii = evaluate_polynomial(secret_package.identifier, &secret_package.coefficients); let fii = evaluate_polynomial(secret_package.identifier, &secret_package.coefficients);
Ok(( Ok((
@ -373,8 +371,7 @@ pub fn part2<C: Ciphersuite>(
/// Computes the verifying keys of the other participants for the third step /// Computes the verifying keys of the other participants for the third step
/// of the DKG protocol. /// of the DKG protocol.
fn compute_verifying_keys<C: Ciphersuite>( fn compute_verifying_keys<C: Ciphersuite>(
round2_packages: &[round2::Package<C>], round1_packages: &HashMap<Identifier<C>, round1::Package<C>>,
round1_packages_map: HashMap<Identifier<C>, &round1::Package<C>>,
round2_secret_package: &round2::SecretPackage<C>, round2_secret_package: &round2::SecretPackage<C>,
) -> Result<HashMap<Identifier<C>, VerifyingShare<C>>, Error<C>> { ) -> Result<HashMap<Identifier<C>, VerifyingShare<C>>, Error<C>> {
// Round 2, Step 4 // 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 // Note that in this loop, "i" refers to the other participant whose public verification share
// we are computing, and not the current participant. // 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(); let mut y_i = <C::Group>::identity();
// We need to iterate through all commitment vectors, including our own, // We need to iterate through all commitment vectors, including our own,
// so chain it manually // so chain it manually
for commitments in round2_packages for commitment in round1_packages
.iter() .keys()
.map(|p| { .map(|k| {
// Get the commitment vector for this participant // Get the commitment vector for this participant
Ok::<&VerifiableSecretSharingCommitment<C>, Error<C>>( Ok::<&VerifiableSecretSharingCommitment<C>, Error<C>>(
&round1_packages_map &round1_packages
.get(&p.sender_identifier) .get(k)
.ok_or(Error::PackageNotFound)? .ok_or(Error::PackageNotFound)?
.commitment, .commitment,
) )
@ -404,7 +401,7 @@ fn compute_verifying_keys<C: Ciphersuite>(
// Chain our own commitment vector // Chain our own commitment vector
.chain(iter::once(Ok(&round2_secret_package.commitment))) .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); let y_i = VerifyingShare(y_i);
others_verifying_keys.insert(i, 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 /// given the received [`round1::Package`]s and [`round2::Package`]s received from
/// the other participants. /// 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 /// It returns the [`KeyPackage`] that has the long-lived key share for the
/// participant, and the [`PublicKeyPackage`]s that has public information /// participant, and the [`PublicKeyPackage`]s that has public information
/// about all participants; both of which are required to compute FROST /// about all participants; both of which are required to compute FROST
/// signatures. /// signatures.
pub fn part3<C: Ciphersuite>( pub fn part3<C: Ciphersuite>(
round2_secret_package: &round2::SecretPackage<C>, round2_secret_package: &round2::SecretPackage<C>,
round1_packages: &[round1::Package<C>], round1_packages: &HashMap<Identifier<C>, round1::Package<C>>,
round2_packages: &[round2::Package<C>], round2_packages: &HashMap<Identifier<C>, round2::Package<C>>,
) -> Result<(KeyPackage<C>, PublicKeyPackage<C>), Error<C>> { ) -> Result<(KeyPackage<C>, PublicKeyPackage<C>), Error<C>> {
if round1_packages.len() != (round2_secret_package.max_signers - 1) as usize { if round1_packages.len() != (round2_secret_package.max_signers - 1) as usize {
return Err(Error::IncorrectNumberOfPackages); return Err(Error::IncorrectNumberOfPackages);
@ -432,30 +437,26 @@ pub fn part3<C: Ciphersuite>(
if round1_packages.len() != round2_packages.len() { if round1_packages.len() != round2_packages.len() {
return Err(Error::IncorrectNumberOfPackages); 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 signing_share = <<C::Group as Group>::Field>::zero();
let mut group_public = <C::Group>::identity(); let mut group_public = <C::Group>::identity();
let round1_packages_map: HashMap<Identifier<C>, &round1::Package<C>> = round1_packages for (sender_identifier, round2_package) in round2_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);
}
// Round 2, Step 2 // Round 2, Step 2
// //
// > Each P_i verifies their shares by calculating: // > Each P_i verifies their shares by calculating:
// > g^{f_(i)} ≟ ∏^{t1}_{k=0} φ^{i^k mod q}_{k}, aborting if the // > g^{f_(i)} ≟ ∏^{t1}_{k=0} φ^{i^k mod q}_{k}, aborting if the
// > check fails. // > check fails.
let ell = round2_package.sender_identifier; let ell = *sender_identifier;
let f_ell_i = round2_package.secret_share; let f_ell_i = round2_package.secret_share;
let commitment = &round1_packages_map let commitment = &round1_packages
.get(&ell) .get(&ell)
.ok_or(Error::PackageNotFound)? .ok_or(Error::PackageNotFound)?
.commitment; .commitment;
@ -501,8 +502,7 @@ pub fn part3<C: Ciphersuite>(
// //
// > Any participant can compute the public verification share of any other participant // > 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}. // > by calculating Y_i = ∏_{j=1}^n ∏_{k=0}^{t1} φ_{jk}^{i^k mod q}.
let mut all_verifying_keys = let mut all_verifying_keys = compute_verifying_keys(round1_packages, round2_secret_package)?;
compute_verifying_keys(round2_packages, round1_packages_map, round2_secret_package)?;
// Add the participant's own public verification share for consistency // Add the participant's own public verification share for consistency
all_verifying_keys.insert(round2_secret_package.identifier, verifying_key); 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", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct SigningCommitments<C: Ciphersuite> { pub struct SigningCommitments<C: Ciphersuite> {
/// The participant identifier.
pub(crate) identifier: Identifier<C>,
/// Commitment to the hiding [`Nonce`]. /// Commitment to the hiding [`Nonce`].
pub(crate) hiding: NonceCommitment<C>, pub(crate) hiding: NonceCommitment<C>,
/// Commitment to the binding [`Nonce`]. /// Commitment to the binding [`Nonce`].
@ -272,13 +270,8 @@ where
C: Ciphersuite, C: Ciphersuite,
{ {
/// Create new SigningCommitments /// Create new SigningCommitments
pub fn new( pub fn new(hiding: NonceCommitment<C>, binding: NonceCommitment<C>) -> Self {
identifier: Identifier<C>,
hiding: NonceCommitment<C>,
binding: NonceCommitment<C>,
) -> Self {
Self { Self {
identifier,
hiding, hiding,
binding, binding,
ciphersuite: (), 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 where
C: Ciphersuite, C: Ciphersuite,
{ {
fn from((identifier, nonces): (Identifier<C>, &SigningNonces<C>)) -> Self { fn from(nonces: &SigningNonces<C>) -> Self {
Self { Self {
identifier,
hiding: nonces.hiding.clone().into(), hiding: nonces.hiding.clone().into(),
binding: nonces.binding.clone().into(), binding: nonces.binding.clone().into(),
ciphersuite: (), ciphersuite: (),
@ -335,8 +327,8 @@ pub(super) fn encode_group_commitments<C: Ciphersuite>(
) -> Vec<u8> { ) -> Vec<u8> {
let mut bytes = vec![]; let mut bytes = vec![];
for item in signing_commitments.values() { for (item_identifier, item) in signing_commitments {
bytes.extend_from_slice(item.identifier.serialize().as_ref()); 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.hiding.0).as_ref());
bytes.extend_from_slice(<C::Group>::serialize(&item.binding.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 // https://github.com/ZcashFoundation/redjubjub/issues/111
pub fn preprocess<C, R>( pub fn preprocess<C, R>(
num_nonces: u8, num_nonces: u8,
participant_identifier: Identifier<C>,
secret: &SigningShare<C>, secret: &SigningShare<C>,
rng: &mut R, rng: &mut R,
) -> (Vec<SigningNonces<C>>, Vec<SigningCommitments<C>>) ) -> (Vec<SigningNonces<C>>, Vec<SigningCommitments<C>>)
@ -375,7 +366,7 @@ where
for _ in 0..num_nonces { for _ in 0..num_nonces {
let nonces = SigningNonces::new(secret, rng); let nonces = SigningNonces::new(secret, rng);
signing_commitments.push(SigningCommitments::from((participant_identifier, &nonces))); signing_commitments.push(SigningCommitments::from(&nonces));
signing_nonces.push(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 /// [`commit`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-round-one-commitment
pub fn commit<C, R>( pub fn commit<C, R>(
participant_identifier: Identifier<C>,
secret: &SigningShare<C>, secret: &SigningShare<C>,
rng: &mut R, rng: &mut R,
) -> (SigningNonces<C>, SigningCommitments<C>) ) -> (SigningNonces<C>, SigningCommitments<C>)
@ -399,8 +389,7 @@ where
C: Ciphersuite, C: Ciphersuite,
R: CryptoRng + RngCore, R: CryptoRng + RngCore,
{ {
let (mut vec_signing_nonces, mut vec_signing_commitments) = let (mut vec_signing_nonces, mut vec_signing_commitments) = preprocess(1, secret, rng);
preprocess(1, participant_identifier, secret, rng);
( (
vec_signing_nonces.pop().expect("must have 1 element"), vec_signing_nonces.pop().expect("must have 1 element"),
vec_signing_commitments.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")] #[cfg(feature = "serde")]
use crate::ScalarSerialization; use crate::ScalarSerialization;
/// A representation of a single signature share used in FROST structures and messages. // Used to help encoding a SignatureShare. Since it has a Scalar<C> it can't
#[derive(Clone, Copy, Getters)] // 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", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(try_from = "ScalarSerialization<C>"))] #[cfg_attr(feature = "serde", serde(try_from = "ScalarSerialization<C>"))]
#[cfg_attr(feature = "serde", serde(into = "ScalarSerialization<C>"))] #[cfg_attr(feature = "serde", serde(into = "ScalarSerialization<C>"))]
pub struct SignatureResponse<C: Ciphersuite> { struct SignatureShareHelper<C: Ciphersuite>(Scalar<C>);
/// 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)
}
}
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
impl<C> TryFrom<ScalarSerialization<C>> for SignatureResponse<C> impl<C> TryFrom<ScalarSerialization<C>> for SignatureShareHelper<C>
where where
C: Ciphersuite, C: Ciphersuite,
{ {
type Error = Error<C>; type Error = Error<C>;
fn try_from(value: ScalarSerialization<C>) -> Result<Self, Self::Error> { 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")] #[cfg(feature = "serde")]
impl<C> From<SignatureResponse<C>> for ScalarSerialization<C> impl<C> From<SignatureShareHelper<C>> for ScalarSerialization<C>
where where
C: Ciphersuite, C: Ciphersuite,
{ {
fn from(value: SignatureResponse<C>) -> Self { fn from(value: SignatureShareHelper<C>) -> Self {
Self(value.serialize()) Self(<<C::Group as Group>::Field>::serialize(&value.0))
}
}
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
} }
} }
@ -90,11 +49,63 @@ where
#[derive(Clone, Copy, Eq, PartialEq, Getters)] #[derive(Clone, Copy, Eq, PartialEq, Getters)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))] #[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> { pub struct SignatureShare<C: Ciphersuite> {
/// Represents the participant identifier.
pub(crate) identifier: Identifier<C>,
/// This participant's signature over the message. /// 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 /// Ciphersuite ID for serialization
#[cfg_attr( #[cfg_attr(
feature = "serde", feature = "serde",
@ -107,41 +118,28 @@ pub struct SignatureShare<C: Ciphersuite> {
ciphersuite: (), ciphersuite: (),
} }
impl<C> SignatureShare<C> #[cfg(feature = "serde")]
impl<C> From<SignatureShareSerialization<C>> for SignatureShare<C>
where where
C: Ciphersuite, C: Ciphersuite,
{ {
/// Create a new [`SignatureShare`]. fn from(value: SignatureShareSerialization<C>) -> Self {
pub fn new(identifier: Identifier<C>, signature: SignatureResponse<C>) -> Self {
Self { Self {
identifier, share: value.share.0,
signature,
ciphersuite: (),
} }
} }
}
/// Tests if a signature share issued by a participant is valid before #[cfg(feature = "serde")]
/// aggregating it into a final joint signature to publish. impl<C> From<SignatureShare<C>> for SignatureShareSerialization<C>
/// where
/// This is the final step of [`verify_signature_share`] from the spec. C: Ciphersuite,
/// {
/// [`verify_signature_share`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-signature-share-verificatio fn from(value: SignatureShare<C>) -> Self {
pub fn verify( Self {
&self, share: SignatureShareHelper(value.share),
group_commitment_share: &round1::GroupCommitmentShare<C>, ciphersuite: (),
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,
});
} }
Ok(())
} }
} }
@ -151,8 +149,7 @@ where
{ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("SignatureShare") f.debug_struct("SignatureShare")
.field("identifier", &self.identifier) .field("share", &hex::encode(self.serialize()))
.field("signature", &self.signature)
.finish() .finish()
} }
} }
@ -170,11 +167,7 @@ fn compute_signature_share<C: Ciphersuite>(
+ (signer_nonces.binding.0 * binding_factor.0) + (signer_nonces.binding.0 * binding_factor.0)
+ (lambda_i * key_package.secret_share.0 * challenge.0); + (lambda_i * key_package.secret_share.0 * challenge.0);
SignatureShare::<C> { SignatureShare::<C> { share: z_share }
identifier: *key_package.identifier(),
signature: SignatureResponse::<C> { z_share },
ciphersuite: (),
}
} }
/// Performed once by each participant selected for the signing operation. /// Performed once by each participant selected for the signing operation.

View File

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

View File

@ -1,5 +1,5 @@
//! Helper function for testing with test vectors. //! Helper function for testing with test vectors.
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
use debugless_unwrap::DebuglessUnwrap; use debugless_unwrap::DebuglessUnwrap;
use hex::{self, FromHex}; use hex::{self, FromHex};
@ -20,7 +20,7 @@ pub struct TestVectors<C: Ciphersuite> {
hiding_nonces_randomness: HashMap<Identifier<C>, Vec<u8>>, hiding_nonces_randomness: HashMap<Identifier<C>, Vec<u8>>,
binding_nonces_randomness: HashMap<Identifier<C>, Vec<u8>>, binding_nonces_randomness: HashMap<Identifier<C>, Vec<u8>>,
signer_nonces: HashMap<Identifier<C>, SigningNonces<C>>, 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_factor_inputs: HashMap<Identifier<C>, Vec<u8>>,
binding_factors: HashMap<Identifier<C>, BindingFactor<C>>, binding_factors: HashMap<Identifier<C>, BindingFactor<C>>,
signature_shares: HashMap<Identifier<C>, SignatureShare<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 hiding_nonces_randomness: HashMap<Identifier<C>, Vec<u8>> = HashMap::new();
let mut binding_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_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_factor_inputs: HashMap<Identifier<C>, Vec<u8>> = HashMap::new();
let mut binding_factors: HashMap<Identifier<C>, BindingFactor<C>> = 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); signer_nonces.insert(identifier, signing_nonces);
let signing_commitments = SigningCommitments::<C>::new( let signing_commitments = SigningCommitments::<C>::new(
identifier,
NonceCommitment::from_hex(signer["hiding_nonce_commitment"].as_str().unwrap()).unwrap(), NonceCommitment::from_hex(signer["hiding_nonce_commitment"].as_str().unwrap()).unwrap(),
NonceCommitment::from_hex(signer["binding_nonce_commitment"].as_str().unwrap()) NonceCommitment::from_hex(signer["binding_nonce_commitment"].as_str().unwrap())
.unwrap(), .unwrap(),
@ -136,12 +135,7 @@ pub fn parse_test_vectors<C: Ciphersuite>(json_vectors: &Value) -> TestVectors<C
) )
.debugless_unwrap(); .debugless_unwrap();
let signature_share = SignatureShare::<C>::new( let signature_share = SignatureShare::<C>::deserialize(sig_share).unwrap();
i.try_into().unwrap(),
SignatureResponse {
z_share: <<C::Group as Group>::Field>::deserialize(&sig_share).unwrap(),
},
);
signature_shares.insert(i.try_into().unwrap(), signature_share); 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 // 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, &message_bytes);
let signing_package = frost::SigningPackage::new(signer_commitments_vec, &message_bytes);
for (identifier, input) in signing_package.binding_factor_preimages(&[]).iter() { for (identifier, input) in signing_package.binding_factor_preimages(&[]).iter() {
assert_eq!(*input, binding_factor_inputs[identifier]); 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]); 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 // Each participant generates their signature share
for identifier in signer_nonces.keys() { 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. // Each participant generates their signature share.
let signature_share = frost::round2::sign(&signing_package, nonces, key_package).unwrap(); 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!(our_signature_shares, signature_shares);
assert_eq!(sig_share, signature_shares[sig_share.identifier()]);
}
let signer_pubkeys = key_packages let signer_pubkeys = key_packages
.into_iter() .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 // Aggregate the FROST signature from test vector sig shares
let group_signature_result = frost::aggregate( let group_signature_result =
&signing_package, frost::aggregate(&signing_package, &signature_shares, &pubkey_package);
&signature_shares
.values()
.cloned()
.collect::<Vec<frost::round2::SignatureShare<C>>>(),
&pubkey_package,
);
// Check that the aggregation passed signature share verification and generation // Check that the aggregation passed signature share verification and generation
assert!(group_signature_result.is_ok()); 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 # // ANCHOR: tkg_gen
use frost_ed25519 as frost; use frost_ed25519 as frost;
use rand::thread_rng; use rand::thread_rng;
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
let mut rng = thread_rng(); let mut rng = thread_rng();
let max_signers = 5; let max_signers = 5;
@ -38,7 +38,7 @@ for (identifier, secret_share) in shares {
} }
let mut nonces_map = HashMap::new(); 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 // 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_. // participant, up to _threshold_.
# // ANCHOR: round1_commit # // ANCHOR: round1_commit
let (nonces, commitments) = frost::round1::commit( let (nonces, commitments) = frost::round1::commit(
participant_identifier, key_packages[&participant_identifier].secret_share(),
key_package.secret_share(),
&mut rng, &mut rng,
); );
# // ANCHOR_END: round1_commit # // 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: // This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign // - decide what message to sign
// - take one (unused) commitment per signing participant // - take one (unused) commitment per signing participant
let mut signature_shares = Vec::new(); let mut signature_shares = HashMap::new();
let commitments_received = commitments_map.clone().into_values().collect();
# // ANCHOR: round2_package # // ANCHOR: round2_package
let message = "message to sign".as_bytes(); let message = "message to sign".as_bytes();
# // In practice, the SigningPackage must be sent to all participants # // In practice, the SigningPackage must be sent to all participants
# // involved in the current signing (at least min_signers participants), # // involved in the current signing (at least min_signers participants),
# // using an authenticate channel (and confidential if the message is secret). # // 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 # // 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 // In practice, the signature share must be sent to the Coordinator
// using an authenticated channel. // 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) // Aggregate (also verifies the signature shares)
# // ANCHOR: aggregate # // 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 # // ANCHOR_END: aggregate

View File

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

View File

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

View File

@ -2,6 +2,8 @@
#![deny(missing_docs)] #![deny(missing_docs)]
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
use std::collections::HashMap;
use curve25519_dalek::{ use curve25519_dalek::{
constants::ED25519_BASEPOINT_POINT, constants::ED25519_BASEPOINT_POINT,
edwards::{CompressedEdwardsY, EdwardsPoint}, edwards::{CompressedEdwardsY, EdwardsPoint},
@ -338,15 +340,11 @@ pub mod round1 {
/// ///
/// Generates the signing nonces and commitments to be used in the signing /// Generates the signing nonces and commitments to be used in the signing
/// operation. /// operation.
pub fn commit<RNG>( pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
participant_identifier: frost::Identifier<E>,
secret: &SigningShare,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
where where
RNG: CryptoRng + RngCore, 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. /// shares into the joint signature.
pub type SignatureShare = frost::round2::SignatureShare<E>; 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. /// 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 /// 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. /// service attack due to publishing an invalid signature.
pub fn aggregate( pub fn aggregate(
signing_package: &SigningPackage, signing_package: &SigningPackage,
signature_shares: &[round2::SignatureShare], signature_shares: &HashMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage, pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, Error> { ) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys) frost::aggregate(signing_package, signature_shares, pubkeys)

View File

@ -1,6 +1,6 @@
//! Generate sample, fixed instances of structs for testing. //! 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_core::{Ciphersuite, Element, Group, Scalar};
use frost_ed25519::{ use frost_ed25519::{
@ -10,7 +10,7 @@ use frost_ed25519::{
VerifyingShare, VerifyingShare,
}, },
round1::{NonceCommitment, SigningCommitments}, round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare}, round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey, Field, Signature, SigningPackage, VerifyingKey,
}; };
@ -38,18 +38,14 @@ pub fn signing_commitments() -> SigningCommitments {
let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2()); let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2());
let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap(); let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap();
let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap(); let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap();
let identifier = 42u16.try_into().unwrap();
SigningCommitments::new( SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment)
identifier,
hiding_nonce_commitment,
binding_nonce_commitment,
)
} }
/// Generate a sample SigningPackage. /// Generate a sample SigningPackage.
pub fn signing_package() -> 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(); let message = "hello world".as_bytes();
SigningPackage::new(commitments, message) SigningPackage::new(commitments, message)
@ -57,11 +53,9 @@ pub fn signing_package() -> SigningPackage {
/// Generate a sample SignatureShare. /// Generate a sample SignatureShare.
pub fn signature_share() -> 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 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. /// Generate a sample SecretShare.
@ -103,7 +97,6 @@ pub fn public_key_package() -> PublicKeyPackage {
/// Generate a sample round1::Package. /// Generate a sample round1::Package.
pub fn round1_package() -> 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_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()); let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let serialized_signature = serialized_element let serialized_signature = serialized_element
@ -118,14 +111,13 @@ pub fn round1_package() -> round1::Package {
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap(); VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
let signature = Signature::deserialize(serialized_signature).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. /// Generate a sample round2::Package.
pub fn round2_package() -> 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 serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signing_share = SigningShare::deserialize(serialized_scalar).unwrap(); 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] #[test]
fn check_signing_commitments_recreation() { fn check_signing_commitments_recreation() {
let commitments = samples::signing_commitments(); let commitments = samples::signing_commitments();
let identifier = commitments.identifier();
let hiding = commitments.hiding(); let hiding = commitments.hiding();
let binding = commitments.binding(); let binding = commitments.binding();
let new_commitments = SigningCommitments::new(*identifier, *hiding, *binding); let new_commitments = SigningCommitments::new(*hiding, *binding);
assert!(commitments == new_commitments); assert!(commitments == new_commitments);
} }
@ -31,14 +30,10 @@ fn check_signing_commitments_recreation() {
fn check_signing_package_recreation() { fn check_signing_package_recreation() {
let signing_package = samples::signing_package(); let signing_package = samples::signing_package();
let commitments = signing_package let commitments = signing_package.signing_commitments();
.signing_commitments()
.values()
.cloned()
.collect();
let message = signing_package.message(); 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); assert!(signing_package == new_signing_package);
} }
@ -47,10 +42,9 @@ fn check_signing_package_recreation() {
fn check_signature_share_recreation() { fn check_signature_share_recreation() {
let signature_share = samples::signature_share(); let signature_share = samples::signature_share();
let identifier = signature_share.identifier(); let encoded = signature_share.serialize();
let signature_response = signature_share.signature();
let new_signature_share = SignatureShare::new(*identifier, *signature_response); let new_signature_share = SignatureShare::deserialize(encoded).unwrap();
assert!(signature_share == new_signature_share); assert!(signature_share == new_signature_share);
} }
@ -106,11 +100,10 @@ fn check_public_key_package_recreation() {
fn check_round1_package_recreation() { fn check_round1_package_recreation() {
let round1_package = samples::round1_package(); let round1_package = samples::round1_package();
let identifier = round1_package.sender_identifier();
let vss_commitment = round1_package.commitment(); let vss_commitment = round1_package.commitment();
let signature = round1_package.proof_of_knowledge(); 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); assert!(round1_package == new_round1_package);
} }
@ -120,12 +113,9 @@ fn check_round1_package_recreation() {
fn check_round2_package_recreation() { fn check_round2_package_recreation() {
let round2_package = samples::round2_package(); 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 signing_share = round2_package.secret_share();
let new_round2_package = let new_round2_package = round2::Package::new(*signing_share);
round2::Package::new(*sender_identifier, *receiver_identifier, *signing_share);
assert!(round2_package == new_round2_package); assert!(round2_package == new_round2_package);
} }

View File

@ -25,7 +25,6 @@ fn check_signing_commitments_serialization() {
assert!(commitments == decoded_commitments); assert!(commitments == decoded_commitments);
let json = r#"{ let json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666", "hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022", "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
@ -38,7 +37,6 @@ fn check_signing_commitments_serialization() {
// Wrong ciphersuite // Wrong ciphersuite
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666", "hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022", "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Wrong, SHA-512)" "ciphersuite": "FROST(Wrong, SHA-512)"
@ -47,7 +45,6 @@ fn check_signing_commitments_serialization() {
// Invalid identifier // Invalid identifier
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666", "hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022" "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
}"#; }"#;
@ -55,8 +52,7 @@ fn check_signing_commitments_serialization() {
// Invalid field // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"foo": "0000000000000000000000000000000000000000000000000000000000000000", "foo": "5866666666666666666666666666666666666666666666666666666666666666",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022" "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022"
}"#; }"#;
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err()); assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
@ -70,7 +66,6 @@ fn check_signing_commitments_serialization() {
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666", "hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022", "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"extra": 1 "extra": 1
@ -94,7 +89,6 @@ fn check_signing_package_serialization() {
let json = r#"{ let json = r#"{
"signing_commitments": { "signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": { "2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666", "hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022", "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
@ -110,7 +104,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"0000000000000000000000000000000000000000000000000000000000000000": { "0000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666", "hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022", "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
@ -125,7 +118,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": { "2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"foo": "5866666666666666666666666666666666666666666666666666666666666666", "foo": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022", "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
@ -140,7 +132,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": { "2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022", "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
} }
@ -154,7 +145,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": { "2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "5866666666666666666666666666666666666666666666666666666666666666", "hiding": "5866666666666666666666666666666666666666666666666666666666666666",
"binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022", "binding": "c9a3f86aae465f0e56513864510f3997561fa2c9e85ea21dc2292309f3cd6022",
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
@ -179,8 +169,7 @@ fn check_signature_share_serialization() {
assert!(signature_share == decoded_signature_share); assert!(signature_share == decoded_signature_share);
let json = r#"{ let json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000", "share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"signature": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
}"#; }"#;
let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap(); let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap();
@ -189,17 +178,8 @@ fn check_signature_share_serialization() {
let invalid_json = "{}"; let invalid_json = "{}";
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err()); 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 // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"foo": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a", "foo": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
}"#; }"#;
@ -207,15 +187,13 @@ fn check_signature_share_serialization() {
// Missing field // Missing field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",,
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
}"#; }"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err()); assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000", "share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"signature": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"extra": 1, "extra": 1,
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
}"#; }"#;
@ -430,7 +408,6 @@ fn check_round1_package_serialization() {
assert!(round1_package == decoded_round1_package); assert!(round1_package == decoded_round1_package);
let json = r#"{ let json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"5866666666666666666666666666666666666666666666666666666666666666" "5866666666666666666666666666666666666666666666666666666666666666"
], ],
@ -443,20 +420,8 @@ fn check_round1_package_serialization() {
let invalid_json = "{}"; let invalid_json = "{}";
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err()); 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 // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"5866666666666666666666666666666666666666666666666666666666666666" "5866666666666666666666666666666666666666666666666666666666666666"
], ],
@ -467,7 +432,6 @@ fn check_round1_package_serialization() {
// Missing field // Missing field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"5866666666666666666666666666666666666666666666666666666666666666" "5866666666666666666666666666666666666666666666666666666666666666"
], ],
@ -477,7 +441,6 @@ fn check_round1_package_serialization() {
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"5866666666666666666666666666666666666666666666666666666666666666" "5866666666666666666666666666666666666666666666666666666666666666"
], ],
@ -499,8 +462,6 @@ fn check_round2_package_serialization() {
assert!(round2_package == decoded_round2_package); assert!(round2_package == decoded_round2_package);
let json = r#"{ let json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"receiver_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a", "secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
}"#; }"#;
@ -510,36 +471,21 @@ fn check_round2_package_serialization() {
let invalid_json = "{}"; let invalid_json = "{}";
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err()); 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 // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000", "foo": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"foo": "2a00000000000000000000000000000000000000000000000000000000000000",
"secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
}"#; }"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err()); assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Missing field // Missing field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(Ed25519, SHA-512)" "ciphersuite": "FROST(Ed25519, SHA-512)"
}"#; }"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err()); assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"receiver_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a", "secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"extra": 1, "extra": 1,
"ciphersuite": "FROST(Ed25519, SHA-512)" "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 # // ANCHOR: tkg_gen
use frost_ed448 as frost; use frost_ed448 as frost;
use rand::thread_rng; use rand::thread_rng;
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
let mut rng = thread_rng(); let mut rng = thread_rng();
let max_signers = 5; let max_signers = 5;
@ -38,7 +38,7 @@ for (identifier, secret_share) in shares {
} }
let mut nonces_map = HashMap::new(); 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 // 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_. // participant, up to _threshold_.
# // ANCHOR: round1_commit # // ANCHOR: round1_commit
let (nonces, commitments) = frost::round1::commit( let (nonces, commitments) = frost::round1::commit(
participant_identifier, key_packages[&participant_identifier].secret_share(),
key_package.secret_share(),
&mut rng, &mut rng,
); );
# // ANCHOR_END: round1_commit # // 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: // This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign // - decide what message to sign
// - take one (unused) commitment per signing participant // - take one (unused) commitment per signing participant
let mut signature_shares = Vec::new(); let mut signature_shares = HashMap::new();
let commitments_received = commitments_map.clone().into_values().collect();
# // ANCHOR: round2_package # // ANCHOR: round2_package
let message = "message to sign".as_bytes(); let message = "message to sign".as_bytes();
# // In practice, the SigningPackage must be sent to all participants # // In practice, the SigningPackage must be sent to all participants
# // involved in the current signing (at least min_signers participants), # // involved in the current signing (at least min_signers participants),
# // using an authenticate channel (and confidential if the message is secret). # // 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 # // 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 // In practice, the signature share must be sent to the Coordinator
// using an authenticated channel. // 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) // Aggregate (also verifies the signature shares)
# // ANCHOR: aggregate # // 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 # // ANCHOR_END: aggregate

View File

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

View File

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

View File

@ -2,6 +2,8 @@
#![deny(missing_docs)] #![deny(missing_docs)]
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
use std::collections::HashMap;
use ed448_goldilocks::{ use ed448_goldilocks::{
curve::{edwards::CompressedEdwardsY, ExtendedPoint}, curve::{edwards::CompressedEdwardsY, ExtendedPoint},
Scalar, Scalar,
@ -332,15 +334,11 @@ pub mod round1 {
/// ///
/// Generates the signing nonces and commitments to be used in the signing /// Generates the signing nonces and commitments to be used in the signing
/// operation. /// operation.
pub fn commit<RNG>( pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
participant_identifier: frost::Identifier<E>,
secret: &SigningShare,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
where where
RNG: CryptoRng + RngCore, 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. /// shares into the joint signature.
pub type SignatureShare = frost::round2::SignatureShare<E>; 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. /// 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 /// 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. /// service attack due to publishing an invalid signature.
pub fn aggregate( pub fn aggregate(
signing_package: &SigningPackage, signing_package: &SigningPackage,
signature_shares: &[round2::SignatureShare], signature_shares: &HashMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage, pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, Error> { ) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys) frost::aggregate(signing_package, signature_shares, pubkeys)

View File

@ -1,6 +1,6 @@
//! Generate sample, fixed instances of structs for testing. //! 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_core::{Ciphersuite, Element, Group, Scalar};
use frost_ed448::{ use frost_ed448::{
@ -10,7 +10,7 @@ use frost_ed448::{
VerifyingShare, VerifyingShare,
}, },
round1::{NonceCommitment, SigningCommitments}, round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare}, round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey, Field, Signature, SigningPackage, VerifyingKey,
}; };
@ -38,18 +38,14 @@ pub fn signing_commitments() -> SigningCommitments {
let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2()); let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2());
let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap(); let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap();
let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap(); let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap();
let identifier = 42u16.try_into().unwrap();
SigningCommitments::new( SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment)
identifier,
hiding_nonce_commitment,
binding_nonce_commitment,
)
} }
/// Generate a sample SigningPackage. /// Generate a sample SigningPackage.
pub fn signing_package() -> 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(); let message = "hello world".as_bytes();
SigningPackage::new(commitments, message) SigningPackage::new(commitments, message)
@ -57,11 +53,9 @@ pub fn signing_package() -> SigningPackage {
/// Generate a sample SignatureShare. /// Generate a sample SignatureShare.
pub fn signature_share() -> 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 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. /// Generate a sample SecretShare.
@ -103,7 +97,6 @@ pub fn public_key_package() -> PublicKeyPackage {
/// Generate a sample round1::Package. /// Generate a sample round1::Package.
pub fn round1_package() -> 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_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()); let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let serialized_signature = serialized_element let serialized_signature = serialized_element
@ -118,14 +111,13 @@ pub fn round1_package() -> round1::Package {
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap(); VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
let signature = Signature::deserialize(serialized_signature).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. /// Generate a sample round2::Package.
pub fn round2_package() -> 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 serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signing_share = SigningShare::deserialize(serialized_scalar).unwrap(); 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] #[test]
fn check_signing_commitments_recreation() { fn check_signing_commitments_recreation() {
let commitments = samples::signing_commitments(); let commitments = samples::signing_commitments();
let identifier = commitments.identifier();
let hiding = commitments.hiding(); let hiding = commitments.hiding();
let binding = commitments.binding(); let binding = commitments.binding();
let new_commitments = SigningCommitments::new(*identifier, *hiding, *binding); let new_commitments = SigningCommitments::new(*hiding, *binding);
assert!(commitments == new_commitments); assert!(commitments == new_commitments);
} }
@ -31,14 +30,10 @@ fn check_signing_commitments_recreation() {
fn check_signing_package_recreation() { fn check_signing_package_recreation() {
let signing_package = samples::signing_package(); let signing_package = samples::signing_package();
let commitments = signing_package let commitments = signing_package.signing_commitments();
.signing_commitments()
.values()
.cloned()
.collect();
let message = signing_package.message(); 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); assert!(signing_package == new_signing_package);
} }
@ -47,10 +42,9 @@ fn check_signing_package_recreation() {
fn check_signature_share_recreation() { fn check_signature_share_recreation() {
let signature_share = samples::signature_share(); let signature_share = samples::signature_share();
let identifier = signature_share.identifier(); let encoded = signature_share.serialize();
let signature_response = signature_share.signature();
let new_signature_share = SignatureShare::new(*identifier, *signature_response); let new_signature_share = SignatureShare::deserialize(encoded).unwrap();
assert!(signature_share == new_signature_share); assert!(signature_share == new_signature_share);
} }
@ -106,11 +100,10 @@ fn check_public_key_package_recreation() {
fn check_round1_package_recreation() { fn check_round1_package_recreation() {
let round1_package = samples::round1_package(); let round1_package = samples::round1_package();
let identifier = round1_package.sender_identifier();
let vss_commitment = round1_package.commitment(); let vss_commitment = round1_package.commitment();
let signature = round1_package.proof_of_knowledge(); 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); assert!(round1_package == new_round1_package);
} }
@ -120,12 +113,9 @@ fn check_round1_package_recreation() {
fn check_round2_package_recreation() { fn check_round2_package_recreation() {
let round2_package = samples::round2_package(); 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 signing_share = round2_package.secret_share();
let new_round2_package = let new_round2_package = round2::Package::new(*signing_share);
round2::Package::new(*sender_identifier, *receiver_identifier, *signing_share);
assert!(round2_package == new_round2_package); assert!(round2_package == new_round2_package);
} }

View File

@ -25,7 +25,6 @@ fn check_signing_commitments_serialization() {
assert!(commitments == decoded_commitments); assert!(commitments == decoded_commitments);
let json = r#"{ let json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900", "hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80", "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
@ -38,7 +37,6 @@ fn check_signing_commitments_serialization() {
// Wrong ciphersuite // Wrong ciphersuite
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900", "hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80", "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Wrong, SHA-512)" "ciphersuite": "FROST(Wrong, SHA-512)"
@ -47,7 +45,6 @@ fn check_signing_commitments_serialization() {
// Invalid identifier // Invalid identifier
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900", "hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80" "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
}"#; }"#;
@ -55,8 +52,7 @@ fn check_signing_commitments_serialization() {
// Invalid field // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"foo": "0000000000000000000000000000000000000000000000000000000000000000", "foo": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80" "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80"
}"#; }"#;
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err()); assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
@ -70,7 +66,6 @@ fn check_signing_commitments_serialization() {
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900", "hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80", "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"extra": 1 "extra": 1
@ -94,7 +89,6 @@ fn check_signing_package_serialization() {
let json = r#"{ let json = r#"{
"signing_commitments": { "signing_commitments": {
"2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": { "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900", "hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80", "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
@ -110,7 +104,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"0000000000000000000000000000000000000000000000000000000000000000": { "0000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900", "hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80", "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
@ -125,7 +118,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": { "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"foo": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900", "foo": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80", "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
@ -140,7 +132,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": { "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80", "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
} }
@ -154,7 +145,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": { "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900", "hiding": "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900",
"binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80", "binding": "ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae80",
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
@ -179,8 +169,7 @@ fn check_signature_share_serialization() {
assert!(signature_share == decoded_signature_share); assert!(signature_share == decoded_signature_share);
let json = r#"{ let json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"signature": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
}"#; }"#;
let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap(); let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap();
@ -189,17 +178,8 @@ fn check_signature_share_serialization() {
let invalid_json = "{}"; let invalid_json = "{}";
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err()); 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 // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"foo": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00", "foo": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
}"#; }"#;
@ -207,15 +187,13 @@ fn check_signature_share_serialization() {
// Missing field // Missing field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",,
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
}"#; }"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err()); assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"signature": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"extra": 1, "extra": 1,
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
}"#; }"#;
@ -430,7 +408,6 @@ fn check_round1_package_serialization() {
assert!(round1_package == decoded_round1_package); assert!(round1_package == decoded_round1_package);
let json = r#"{ let json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900"
], ],
@ -443,20 +420,8 @@ fn check_round1_package_serialization() {
let invalid_json = "{}"; let invalid_json = "{}";
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err()); 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 // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900"
], ],
@ -467,7 +432,6 @@ fn check_round1_package_serialization() {
// Missing field // Missing field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900"
], ],
@ -477,7 +441,6 @@ fn check_round1_package_serialization() {
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900" "14fa30f25b790898adc8d74e2c13bdfdc4397ce61cffd33ad7c2a0051e9c78874098a36c7373ea4b62c7c9563720768824bcb66e71463f6900"
], ],
@ -499,8 +462,6 @@ fn check_round2_package_serialization() {
assert!(round2_package == decoded_round2_package); assert!(round2_package == decoded_round2_package);
let json = r#"{ let json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receiver_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"secret_share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00", "secret_share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
}"#; }"#;
@ -510,36 +471,21 @@ fn check_round2_package_serialization() {
let invalid_json = "{}"; let invalid_json = "{}";
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err()); 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 // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "foo": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"foo": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"secret_share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
}"#; }"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err()); assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Missing field // Missing field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"secret_share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"ciphersuite": "FROST(Ed448, SHAKE256)" "ciphersuite": "FROST(Ed448, SHAKE256)"
}"#; }"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err()); assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receiver_identifier": "2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"secret_share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00", "secret_share": "4d83e51cb78150c2380ad9b3a18148166024e4c9db3cdf82466d3153aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2a00",
"extra": 1, "extra": 1,
"ciphersuite": "FROST(Ed448, SHAKE256)" "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 # // ANCHOR: tkg_gen
use frost_p256 as frost; use frost_p256 as frost;
use rand::thread_rng; use rand::thread_rng;
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
let mut rng = thread_rng(); let mut rng = thread_rng();
let max_signers = 5; let max_signers = 5;
@ -38,7 +38,7 @@ for (identifier, secret_share) in shares {
} }
let mut nonces_map = HashMap::new(); 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 // 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_. // participant, up to _threshold_.
# // ANCHOR: round1_commit # // ANCHOR: round1_commit
let (nonces, commitments) = frost::round1::commit( let (nonces, commitments) = frost::round1::commit(
participant_identifier, key_packages[&participant_identifier].secret_share(),
key_package.secret_share(),
&mut rng, &mut rng,
); );
# // ANCHOR_END: round1_commit # // 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: // This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign // - decide what message to sign
// - take one (unused) commitment per signing participant // - take one (unused) commitment per signing participant
let mut signature_shares = Vec::new(); let mut signature_shares = HashMap::new();
let commitments_received = commitments_map.clone().into_values().collect();
# // ANCHOR: round2_package # // ANCHOR: round2_package
let message = "message to sign".as_bytes(); let message = "message to sign".as_bytes();
# // In practice, the SigningPackage must be sent to all participants # // In practice, the SigningPackage must be sent to all participants
# // involved in the current signing (at least min_signers participants), # // involved in the current signing (at least min_signers participants),
# // using an authenticate channel (and confidential if the message is secret). # // 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 # // 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 // In practice, the signature share must be sent to the Coordinator
// using an authenticated channel. // 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) // Aggregate (also verifies the signature shares)
# // ANCHOR: aggregate # // 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 # // ANCHOR_END: aggregate

View File

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

View File

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

View File

@ -2,6 +2,8 @@
#![deny(missing_docs)] #![deny(missing_docs)]
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
use std::collections::HashMap;
use p256::{ use p256::{
elliptic_curve::{ elliptic_curve::{
hash2curve::{hash_to_field, ExpandMsgXmd}, hash2curve::{hash_to_field, ExpandMsgXmd},
@ -364,15 +366,11 @@ pub mod round1 {
/// ///
/// Generates the signing nonces and commitments to be used in the signing /// Generates the signing nonces and commitments to be used in the signing
/// operation. /// operation.
pub fn commit<RNG>( pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
participant_identifier: frost::Identifier<P>,
secret: &SigningShare,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
where where
RNG: CryptoRng + RngCore, 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. /// shares into the joint signature.
pub type SignatureShare = frost::round2::SignatureShare<P>; 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. /// 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 /// 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. /// service attack due to publishing an invalid signature.
pub fn aggregate( pub fn aggregate(
signing_package: &SigningPackage, signing_package: &SigningPackage,
signature_shares: &[round2::SignatureShare], signature_shares: &HashMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage, pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, Error> { ) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys) frost::aggregate(signing_package, signature_shares, pubkeys)

View File

@ -1,6 +1,6 @@
//! Generate sample, fixed instances of structs for testing. //! 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_core::{Ciphersuite, Element, Group, Scalar};
use frost_p256::{ use frost_p256::{
@ -10,7 +10,7 @@ use frost_p256::{
VerifyingShare, VerifyingShare,
}, },
round1::{NonceCommitment, SigningCommitments}, round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare}, round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey, Field, Signature, SigningPackage, VerifyingKey,
}; };
@ -38,18 +38,14 @@ pub fn signing_commitments() -> SigningCommitments {
let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2()); let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2());
let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap(); let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap();
let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap(); let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap();
let identifier = 42u16.try_into().unwrap();
SigningCommitments::new( SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment)
identifier,
hiding_nonce_commitment,
binding_nonce_commitment,
)
} }
/// Generate a sample SigningPackage. /// Generate a sample SigningPackage.
pub fn signing_package() -> 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(); let message = "hello world".as_bytes();
SigningPackage::new(commitments, message) SigningPackage::new(commitments, message)
@ -57,11 +53,9 @@ pub fn signing_package() -> SigningPackage {
/// Generate a sample SignatureShare. /// Generate a sample SignatureShare.
pub fn signature_share() -> 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 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. /// Generate a sample SecretShare.
@ -103,7 +97,6 @@ pub fn public_key_package() -> PublicKeyPackage {
/// Generate a sample round1::Package. /// Generate a sample round1::Package.
pub fn round1_package() -> 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_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()); let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let serialized_signature = serialized_element let serialized_signature = serialized_element
@ -118,14 +111,13 @@ pub fn round1_package() -> round1::Package {
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap(); VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
let signature = Signature::deserialize(serialized_signature).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. /// Generate a sample round2::Package.
pub fn round2_package() -> 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 serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signing_share = SigningShare::deserialize(serialized_scalar).unwrap(); 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] #[test]
fn check_signing_commitments_recreation() { fn check_signing_commitments_recreation() {
let commitments = samples::signing_commitments(); let commitments = samples::signing_commitments();
let identifier = commitments.identifier();
let hiding = commitments.hiding(); let hiding = commitments.hiding();
let binding = commitments.binding(); let binding = commitments.binding();
let new_commitments = SigningCommitments::new(*identifier, *hiding, *binding); let new_commitments = SigningCommitments::new(*hiding, *binding);
assert!(commitments == new_commitments); assert!(commitments == new_commitments);
} }
@ -31,14 +30,10 @@ fn check_signing_commitments_recreation() {
fn check_signing_package_recreation() { fn check_signing_package_recreation() {
let signing_package = samples::signing_package(); let signing_package = samples::signing_package();
let commitments = signing_package let commitments = signing_package.signing_commitments();
.signing_commitments()
.values()
.cloned()
.collect();
let message = signing_package.message(); 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); assert!(signing_package == new_signing_package);
} }
@ -47,10 +42,9 @@ fn check_signing_package_recreation() {
fn check_signature_share_recreation() { fn check_signature_share_recreation() {
let signature_share = samples::signature_share(); let signature_share = samples::signature_share();
let identifier = signature_share.identifier(); let encoded = signature_share.serialize();
let signature_response = signature_share.signature();
let new_signature_share = SignatureShare::new(*identifier, *signature_response); let new_signature_share = SignatureShare::deserialize(encoded).unwrap();
assert!(signature_share == new_signature_share); assert!(signature_share == new_signature_share);
} }
@ -106,11 +100,10 @@ fn check_public_key_package_recreation() {
fn check_round1_package_recreation() { fn check_round1_package_recreation() {
let round1_package = samples::round1_package(); let round1_package = samples::round1_package();
let identifier = round1_package.sender_identifier();
let vss_commitment = round1_package.commitment(); let vss_commitment = round1_package.commitment();
let signature = round1_package.proof_of_knowledge(); 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); assert!(round1_package == new_round1_package);
} }
@ -120,12 +113,9 @@ fn check_round1_package_recreation() {
fn check_round2_package_recreation() { fn check_round2_package_recreation() {
let round2_package = samples::round2_package(); 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 signing_share = round2_package.secret_share();
let new_round2_package = let new_round2_package = round2::Package::new(*signing_share);
round2::Package::new(*sender_identifier, *receiver_identifier, *signing_share);
assert!(round2_package == new_round2_package); assert!(round2_package == new_round2_package);
} }

View File

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

View File

@ -5,6 +5,8 @@
#[cfg(any(test, feature = "test-impl"))] #[cfg(any(test, feature = "test-impl"))]
pub mod tests; pub mod tests;
use std::collections::HashMap;
pub use frost_core; pub use frost_core;
use frost_core::{ use frost_core::{
@ -91,7 +93,7 @@ pub fn sign<C: Ciphersuite>(
/// service attack due to publishing an invalid signature. /// service attack due to publishing an invalid signature.
pub fn aggregate<C>( pub fn aggregate<C>(
signing_package: &frost::SigningPackage<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>, pubkeys: &frost::keys::PublicKeyPackage<C>,
randomized_params: &RandomizedParams<C>, randomized_params: &RandomizedParams<C>,
) -> Result<frost_core::Signature<C>, Error<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 // [`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(); let mut z = <<C::Group as Group>::Field as Field>::zero();
for signature_share in signature_shares { for signature_share in signature_shares.values() {
z = z + *signature_share.signature().z_share(); z = z + *signature_share.share();
} }
z = z + challenge.clone().to_scalar() * randomized_params.randomizer; 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 the aggregate signature is valid (which should be the common case).
if let Err(err) = verification_result { if let Err(err) = verification_result {
// Verify the signature shares. // 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])_, // 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. // and where s[i] is a secret share of the constant term of _f_, the secret polynomial.
let signer_pubkey = pubkeys let signer_pubkey = pubkeys
.signer_pubkeys() .signer_pubkeys()
.get(signature_share.identifier()) .get(signature_share_identifier)
.unwrap(); .unwrap();
// Compute Lagrange coefficient. // Compute Lagrange coefficient.
let lambda_i = 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. // Compute the commitment share.
let R_share = signing_package let R_share = signing_package
.signing_commitment(signature_share.identifier()) .signing_commitment(signature_share_identifier)
.to_group_commitment_share(&binding_factor); .to_group_commitment_share(&binding_factor);
// Compute relation values to verify this signature share. // 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. // 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. //! 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 crate::{frost_core::frost, frost_core::Ciphersuite, RandomizedParams};
use frost_core::{Field, Group, Signature, VerifyingKey}; 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 nonces: HashMap<frost::Identifier<C>, frost::round1::SigningNonces<C>> = HashMap::new();
let mut commitments: HashMap<frost::Identifier<C>, frost::round1::SigningCommitments<C>> = let mut commitments: BTreeMap<frost::Identifier<C>, frost::round1::SigningCommitments<C>> =
HashMap::new(); BTreeMap::new();
check_from_randomizer(&pubkeys, &mut rng); check_from_randomizer(&pubkeys, &mut rng);
let randomizer_params = RandomizedParams::new(&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 // Generate one (1) nonce and one SigningCommitments instance for each
// participant, up to _min_signers_. // participant, up to _min_signers_.
let (nonce, commitment) = frost::round1::commit( let (nonce, commitment) = frost::round1::commit(
participant_identifier,
key_packages key_packages
.get(&participant_identifier) .get(&participant_identifier)
.unwrap() .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: // This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign // - decide what message to sign
// - take one (unused) commitment per signing participant // - 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 message = "message to sign".as_bytes();
let comms = commitments.clone().into_values().collect(); let signing_package = frost::SigningPackage::new(commitments, message);
let signing_package = frost::SigningPackage::new(comms, message);
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Round 2: each participant generates their signature share // 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(), randomizer_params.randomizer_point(),
) )
.unwrap(); .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) // Aggregate (also verifies the signature shares)
let group_signature_res = crate::aggregate( let group_signature_res = crate::aggregate(
&signing_package, &signing_package,
&signature_shares[..], &signature_shares,
&pubkeys, &pubkeys,
&randomizer_params, &randomizer_params,
); );

View File

@ -12,7 +12,7 @@ scenario in a single thread and it abstracts away any communication between peer
# // ANCHOR: tkg_gen # // ANCHOR: tkg_gen
use frost_ristretto255 as frost; use frost_ristretto255 as frost;
use rand::thread_rng; use rand::thread_rng;
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
let mut rng = thread_rng(); let mut rng = thread_rng();
let max_signers = 5; let max_signers = 5;
@ -38,7 +38,7 @@ for (identifier, secret_share) in shares {
} }
let mut nonces_map = HashMap::new(); 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 // 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_. // participant, up to _threshold_.
# // ANCHOR: round1_commit # // ANCHOR: round1_commit
let (nonces, commitments) = frost::round1::commit( let (nonces, commitments) = frost::round1::commit(
participant_identifier, key_packages[&participant_identifier].secret_share(),
key_package.secret_share(),
&mut rng, &mut rng,
); );
# // ANCHOR_END: round1_commit # // 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: // This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign // - decide what message to sign
// - take one (unused) commitment per signing participant // - take one (unused) commitment per signing participant
let mut signature_shares = Vec::new(); let mut signature_shares = HashMap::new();
let commitments_received = commitments_map.clone().into_values().collect();
# // ANCHOR: round2_package # // ANCHOR: round2_package
let message = "message to sign".as_bytes(); let message = "message to sign".as_bytes();
# // In practice, the SigningPackage must be sent to all participants # // In practice, the SigningPackage must be sent to all participants
# // involved in the current signing (at least min_signers participants), # // involved in the current signing (at least min_signers participants),
# // using an authenticate channel (and confidential if the message is secret). # // 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 # // 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 // In practice, the signature share must be sent to the Coordinator
// using an authenticated channel. // 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) // Aggregate (also verifies the signature shares)
# // ANCHOR: aggregate # // 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 # // ANCHOR_END: aggregate

View File

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

View File

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

View File

@ -2,6 +2,8 @@
#![deny(missing_docs)] #![deny(missing_docs)]
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
use std::collections::HashMap;
use curve25519_dalek::{ use curve25519_dalek::{
constants::RISTRETTO_BASEPOINT_POINT, constants::RISTRETTO_BASEPOINT_POINT,
ristretto::{CompressedRistretto, RistrettoPoint}, ristretto::{CompressedRistretto, RistrettoPoint},
@ -326,15 +328,11 @@ pub mod round1 {
/// ///
/// Generates the signing nonces and commitments to be used in the signing /// Generates the signing nonces and commitments to be used in the signing
/// operation. /// operation.
pub fn commit<RNG>( pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
participant_identifier: frost::Identifier<R>,
secret: &SigningShare,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
where where
RNG: CryptoRng + RngCore, 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. /// shares into the joint signature.
pub type SignatureShare = frost::round2::SignatureShare<R>; 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. /// 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 /// 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. /// service attack due to publishing an invalid signature.
pub fn aggregate( pub fn aggregate(
signing_package: &SigningPackage, signing_package: &SigningPackage,
signature_shares: &[round2::SignatureShare], signature_shares: &HashMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage, pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, Error> { ) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys) frost::aggregate(signing_package, signature_shares, pubkeys)

View File

@ -1,6 +1,6 @@
//! Generate sample, fixed instances of structs for testing. //! 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_core::{Ciphersuite, Element, Group, Scalar};
use frost_ristretto255::{ use frost_ristretto255::{
@ -10,7 +10,7 @@ use frost_ristretto255::{
VerifyingShare, VerifyingShare,
}, },
round1::{NonceCommitment, SigningCommitments}, round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare}, round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey, Field, Signature, SigningPackage, VerifyingKey,
}; };
@ -38,18 +38,14 @@ pub fn signing_commitments() -> SigningCommitments {
let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2()); let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2());
let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap(); let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap();
let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap(); let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap();
let identifier = 42u16.try_into().unwrap();
SigningCommitments::new( SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment)
identifier,
hiding_nonce_commitment,
binding_nonce_commitment,
)
} }
/// Generate a sample SigningPackage. /// Generate a sample SigningPackage.
pub fn signing_package() -> 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(); let message = "hello world".as_bytes();
SigningPackage::new(commitments, message) SigningPackage::new(commitments, message)
@ -57,11 +53,9 @@ pub fn signing_package() -> SigningPackage {
/// Generate a sample SignatureShare. /// Generate a sample SignatureShare.
pub fn signature_share() -> 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 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. /// Generate a sample SecretShare.
@ -103,7 +97,6 @@ pub fn public_key_package() -> PublicKeyPackage {
/// Generate a sample round1::Package. /// Generate a sample round1::Package.
pub fn round1_package() -> 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_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()); let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let serialized_signature = serialized_element let serialized_signature = serialized_element
@ -118,14 +111,13 @@ pub fn round1_package() -> round1::Package {
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap(); VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
let signature = Signature::deserialize(serialized_signature).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. /// Generate a sample round2::Package.
pub fn round2_package() -> 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 serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signing_share = SigningShare::deserialize(serialized_scalar).unwrap(); 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] #[test]
fn check_signing_commitments_recreation() { fn check_signing_commitments_recreation() {
let commitments = samples::signing_commitments(); let commitments = samples::signing_commitments();
let identifier = commitments.identifier();
let hiding = commitments.hiding(); let hiding = commitments.hiding();
let binding = commitments.binding(); let binding = commitments.binding();
let new_commitments = SigningCommitments::new(*identifier, *hiding, *binding); let new_commitments = SigningCommitments::new(*hiding, *binding);
assert!(commitments == new_commitments); assert!(commitments == new_commitments);
} }
@ -31,14 +30,10 @@ fn check_signing_commitments_recreation() {
fn check_signing_package_recreation() { fn check_signing_package_recreation() {
let signing_package = samples::signing_package(); let signing_package = samples::signing_package();
let commitments = signing_package let commitments = signing_package.signing_commitments();
.signing_commitments()
.values()
.cloned()
.collect();
let message = signing_package.message(); 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); assert!(signing_package == new_signing_package);
} }
@ -47,10 +42,9 @@ fn check_signing_package_recreation() {
fn check_signature_share_recreation() { fn check_signature_share_recreation() {
let signature_share = samples::signature_share(); let signature_share = samples::signature_share();
let identifier = signature_share.identifier(); let encoded = signature_share.serialize();
let signature_response = signature_share.signature();
let new_signature_share = SignatureShare::new(*identifier, *signature_response); let new_signature_share = SignatureShare::deserialize(encoded).unwrap();
assert!(signature_share == new_signature_share); assert!(signature_share == new_signature_share);
} }
@ -106,11 +100,10 @@ fn check_public_key_package_recreation() {
fn check_round1_package_recreation() { fn check_round1_package_recreation() {
let round1_package = samples::round1_package(); let round1_package = samples::round1_package();
let identifier = round1_package.sender_identifier();
let vss_commitment = round1_package.commitment(); let vss_commitment = round1_package.commitment();
let signature = round1_package.proof_of_knowledge(); 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); assert!(round1_package == new_round1_package);
} }
@ -120,12 +113,9 @@ fn check_round1_package_recreation() {
fn check_round2_package_recreation() { fn check_round2_package_recreation() {
let round2_package = samples::round2_package(); 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 signing_share = round2_package.secret_share();
let new_round2_package = let new_round2_package = round2::Package::new(*signing_share);
round2::Package::new(*sender_identifier, *receiver_identifier, *signing_share);
assert!(round2_package == new_round2_package); assert!(round2_package == new_round2_package);
} }

View File

@ -25,7 +25,6 @@ fn check_signing_commitments_serialization() {
assert!(commitments == decoded_commitments); assert!(commitments == decoded_commitments);
let json = r#"{ let json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", "hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919", "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
@ -38,7 +37,6 @@ fn check_signing_commitments_serialization() {
// Wrong ciphersuite // Wrong ciphersuite
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", "hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919", "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(Wrong, SHA-512)" "ciphersuite": "FROST(Wrong, SHA-512)"
@ -47,7 +45,6 @@ fn check_signing_commitments_serialization() {
// Invalid identifier // Invalid identifier
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", "hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919" "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
}"#; }"#;
@ -55,8 +52,7 @@ fn check_signing_commitments_serialization() {
// Invalid field // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"foo": "0000000000000000000000000000000000000000000000000000000000000000", "foo": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919" "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"
}"#; }"#;
assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err()); assert!(serde_json::from_str::<SigningCommitments>(invalid_json).is_err());
@ -70,7 +66,6 @@ fn check_signing_commitments_serialization() {
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", "hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919", "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"extra": 1 "extra": 1
@ -94,7 +89,6 @@ fn check_signing_package_serialization() {
let json = r#"{ let json = r#"{
"signing_commitments": { "signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": { "2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", "hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919", "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
@ -110,7 +104,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"0000000000000000000000000000000000000000000000000000000000000000": { "0000000000000000000000000000000000000000000000000000000000000000": {
"identifier": "0000000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", "hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919", "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
@ -125,7 +118,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": { "2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"foo": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", "foo": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919", "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
@ -140,7 +132,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": { "2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919", "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
} }
@ -154,7 +145,6 @@ fn check_signing_package_serialization() {
let invalid_json = r#"{ let invalid_json = r#"{
"signing_commitments": { "signing_commitments": {
"2a00000000000000000000000000000000000000000000000000000000000000": { "2a00000000000000000000000000000000000000000000000000000000000000": {
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", "hiding": "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919", "binding": "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
@ -179,8 +169,7 @@ fn check_signature_share_serialization() {
assert!(signature_share == decoded_signature_share); assert!(signature_share == decoded_signature_share);
let json = r#"{ let json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000", "share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"signature": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
}"#; }"#;
let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap(); let decoded_commitments: SignatureShare = serde_json::from_str(json).unwrap();
@ -189,17 +178,8 @@ fn check_signature_share_serialization() {
let invalid_json = "{}"; let invalid_json = "{}";
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err()); 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 // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"foo": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a", "foo": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
}"#; }"#;
@ -207,15 +187,13 @@ fn check_signature_share_serialization() {
// Missing field // Missing field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000",,
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
}"#; }"#;
assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err()); assert!(serde_json::from_str::<SignatureShare>(invalid_json).is_err());
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"identifier": "2a00000000000000000000000000000000000000000000000000000000000000", "share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"signature": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"extra": 1, "extra": 1,
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
}"#; }"#;
@ -430,7 +408,6 @@ fn check_round1_package_serialization() {
assert!(round1_package == decoded_round1_package); assert!(round1_package == decoded_round1_package);
let json = r#"{ let json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
], ],
@ -443,20 +420,8 @@ fn check_round1_package_serialization() {
let invalid_json = "{}"; let invalid_json = "{}";
assert!(serde_json::from_str::<round1::Package>(invalid_json).is_err()); 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 // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
], ],
@ -467,7 +432,6 @@ fn check_round1_package_serialization() {
// Missing field // Missing field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
], ],
@ -477,7 +441,6 @@ fn check_round1_package_serialization() {
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"commitment": [ "commitment": [
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"
], ],
@ -499,8 +462,6 @@ fn check_round2_package_serialization() {
assert!(round2_package == decoded_round2_package); assert!(round2_package == decoded_round2_package);
let json = r#"{ let json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"receiver_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a", "secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
}"#; }"#;
@ -510,36 +471,21 @@ fn check_round2_package_serialization() {
let invalid_json = "{}"; let invalid_json = "{}";
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err()); 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 // Invalid field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000", "foo": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"foo": "2a00000000000000000000000000000000000000000000000000000000000000",
"secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
}"#; }"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err()); assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Missing field // Missing field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"ciphersuite": "FROST(ristretto255, SHA-512)" "ciphersuite": "FROST(ristretto255, SHA-512)"
}"#; }"#;
assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err()); assert!(serde_json::from_str::<round2::Package>(invalid_json).is_err());
// Extra field // Extra field
let invalid_json = r#"{ let invalid_json = r#"{
"sender_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"receiver_identifier": "2a00000000000000000000000000000000000000000000000000000000000000",
"secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a", "secret_share": "498d4e9311420c903913a56c94a694b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0a",
"extra": 1, "extra": 1,
"ciphersuite": "FROST(ristretto255, SHA-512)" "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 # // ANCHOR: tkg_gen
use frost_secp256k1 as frost; use frost_secp256k1 as frost;
use rand::thread_rng; use rand::thread_rng;
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
let mut rng = thread_rng(); let mut rng = thread_rng();
let max_signers = 5; let max_signers = 5;
@ -38,7 +38,7 @@ for (identifier, secret_share) in shares {
} }
let mut nonces_map = HashMap::new(); 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 // 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_. // participant, up to _threshold_.
# // ANCHOR: round1_commit # // ANCHOR: round1_commit
let (nonces, commitments) = frost::round1::commit( let (nonces, commitments) = frost::round1::commit(
participant_identifier, key_packages[&participant_identifier].secret_share(),
key_package.secret_share(),
&mut rng, &mut rng,
); );
# // ANCHOR_END: round1_commit # // 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: // This is what the signature aggregator / coordinator needs to do:
// - decide what message to sign // - decide what message to sign
// - take one (unused) commitment per signing participant // - take one (unused) commitment per signing participant
let mut signature_shares = Vec::new(); let mut signature_shares = HashMap::new();
let commitments_received = commitments_map.clone().into_values().collect();
# // ANCHOR: round2_package # // ANCHOR: round2_package
let message = "message to sign".as_bytes(); let message = "message to sign".as_bytes();
# // In practice, the SigningPackage must be sent to all participants # // In practice, the SigningPackage must be sent to all participants
# // involved in the current signing (at least min_signers participants), # // involved in the current signing (at least min_signers participants),
# // using an authenticate channel (and confidential if the message is secret). # // 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 # // 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 // In practice, the signature share must be sent to the Coordinator
// using an authenticated channel. // 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) // Aggregate (also verifies the signature shares)
# // ANCHOR: aggregate # // 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 # // ANCHOR_END: aggregate

View File

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

View File

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

View File

@ -2,6 +2,8 @@
#![deny(missing_docs)] #![deny(missing_docs)]
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
use std::collections::HashMap;
use k256::{ use k256::{
elliptic_curve::{ elliptic_curve::{
group::prime::PrimeCurveAffine, group::prime::PrimeCurveAffine,
@ -364,15 +366,11 @@ pub mod round1 {
/// ///
/// Generates the signing nonces and commitments to be used in the signing /// Generates the signing nonces and commitments to be used in the signing
/// operation. /// operation.
pub fn commit<RNG>( pub fn commit<RNG>(secret: &SigningShare, rng: &mut RNG) -> (SigningNonces, SigningCommitments)
participant_identifier: frost::Identifier<S>,
secret: &SigningShare,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
where where
RNG: CryptoRng + RngCore, 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. /// shares into the joint signature.
pub type SignatureShare = frost::round2::SignatureShare<S>; 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. /// 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 /// 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. /// service attack due to publishing an invalid signature.
pub fn aggregate( pub fn aggregate(
signing_package: &SigningPackage, signing_package: &SigningPackage,
signature_shares: &[round2::SignatureShare], signature_shares: &HashMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage, pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, Error> { ) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys) frost::aggregate(signing_package, signature_shares, pubkeys)

View File

@ -1,6 +1,6 @@
//! Generate sample, fixed instances of structs for testing. //! 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_core::{Ciphersuite, Element, Group, Scalar};
use frost_secp256k1::{ use frost_secp256k1::{
@ -10,7 +10,7 @@ use frost_secp256k1::{
VerifyingShare, VerifyingShare,
}, },
round1::{NonceCommitment, SigningCommitments}, round1::{NonceCommitment, SigningCommitments},
round2::{SignatureResponse, SignatureShare}, round2::SignatureShare,
Field, Signature, SigningPackage, VerifyingKey, Field, Signature, SigningPackage, VerifyingKey,
}; };
@ -38,18 +38,14 @@ pub fn signing_commitments() -> SigningCommitments {
let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2()); let serialized_element2 = <C as Ciphersuite>::Group::serialize(&element2());
let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap(); let hiding_nonce_commitment = NonceCommitment::deserialize(serialized_element1).unwrap();
let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap(); let binding_nonce_commitment = NonceCommitment::deserialize(serialized_element2).unwrap();
let identifier = 42u16.try_into().unwrap();
SigningCommitments::new( SigningCommitments::new(hiding_nonce_commitment, binding_nonce_commitment)
identifier,
hiding_nonce_commitment,
binding_nonce_commitment,
)
} }
/// Generate a sample SigningPackage. /// Generate a sample SigningPackage.
pub fn signing_package() -> 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(); let message = "hello world".as_bytes();
SigningPackage::new(commitments, message) SigningPackage::new(commitments, message)
@ -57,11 +53,9 @@ pub fn signing_package() -> SigningPackage {
/// Generate a sample SignatureShare. /// Generate a sample SignatureShare.
pub fn signature_share() -> 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 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. /// Generate a sample SecretShare.
@ -103,7 +97,6 @@ pub fn public_key_package() -> PublicKeyPackage {
/// Generate a sample round1::Package. /// Generate a sample round1::Package.
pub fn round1_package() -> 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_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let serialized_element = <C as Ciphersuite>::Group::serialize(&element1()); let serialized_element = <C as Ciphersuite>::Group::serialize(&element1());
let serialized_signature = serialized_element let serialized_signature = serialized_element
@ -118,14 +111,13 @@ pub fn round1_package() -> round1::Package {
VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap(); VerifiableSecretSharingCommitment::deserialize(vec![serialized_element]).unwrap();
let signature = Signature::deserialize(serialized_signature).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. /// Generate a sample round2::Package.
pub fn round2_package() -> 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 serialized_scalar = <<C as Ciphersuite>::Group as Group>::Field::serialize(&scalar1());
let signing_share = SigningShare::deserialize(serialized_scalar).unwrap(); 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] #[test]
fn check_signing_commitments_recreation() { fn check_signing_commitments_recreation() {
let commitments = samples::signing_commitments(); let commitments = samples::signing_commitments();
let identifier = commitments.identifier();
let hiding = commitments.hiding(); let hiding = commitments.hiding();
let binding = commitments.binding(); let binding = commitments.binding();
let new_commitments = SigningCommitments::new(*identifier, *hiding, *binding); let new_commitments = SigningCommitments::new(*hiding, *binding);
assert!(commitments == new_commitments); assert!(commitments == new_commitments);
} }
@ -31,14 +30,10 @@ fn check_signing_commitments_recreation() {
fn check_signing_package_recreation() { fn check_signing_package_recreation() {
let signing_package = samples::signing_package(); let signing_package = samples::signing_package();
let commitments = signing_package let commitments = signing_package.signing_commitments();
.signing_commitments()
.values()
.cloned()
.collect();
let message = signing_package.message(); 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); assert!(signing_package == new_signing_package);
} }
@ -47,10 +42,9 @@ fn check_signing_package_recreation() {
fn check_signature_share_recreation() { fn check_signature_share_recreation() {
let signature_share = samples::signature_share(); let signature_share = samples::signature_share();
let identifier = signature_share.identifier(); let encoded = signature_share.serialize();
let signature_response = signature_share.signature();
let new_signature_share = SignatureShare::new(*identifier, *signature_response); let new_signature_share = SignatureShare::deserialize(encoded).unwrap();
assert!(signature_share == new_signature_share); assert!(signature_share == new_signature_share);
} }
@ -106,11 +100,10 @@ fn check_public_key_package_recreation() {
fn check_round1_package_recreation() { fn check_round1_package_recreation() {
let round1_package = samples::round1_package(); let round1_package = samples::round1_package();
let identifier = round1_package.sender_identifier();
let vss_commitment = round1_package.commitment(); let vss_commitment = round1_package.commitment();
let signature = round1_package.proof_of_knowledge(); 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); assert!(round1_package == new_round1_package);
} }
@ -120,12 +113,9 @@ fn check_round1_package_recreation() {
fn check_round2_package_recreation() { fn check_round2_package_recreation() {
let round2_package = samples::round2_package(); 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 signing_share = round2_package.secret_share();
let new_round2_package = let new_round2_package = round2::Package::new(*signing_share);
round2::Package::new(*sender_identifier, *receiver_identifier, *signing_share);
assert!(round2_package == new_round2_package); assert!(round2_package == new_round2_package);
} }

View File

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