Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove identifiers from structs that are communicated #397

Merged
merged 9 commits into from
Jul 5, 2023
16 changes: 16 additions & 0 deletions frost-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ Entries are listed in reverse chronological order.

## 0.6.0

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

## Released
Expand Down
15 changes: 6 additions & 9 deletions frost-core/src/benches.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Ciphersuite-generic benchmark functions.

use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};

use criterion::{BenchmarkId, Criterion, Throughput};
use rand_core::{CryptoRng, RngCore};
Expand Down Expand Up @@ -126,7 +126,6 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
b.iter(|| {
let participant_identifier = 1u16.try_into().expect("should be nonzero");
frost::round1::commit(
participant_identifier,
key_packages
.get(&participant_identifier)
.unwrap()
Expand All @@ -138,12 +137,11 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
);

let mut nonces: HashMap<_, _> = HashMap::new();
let mut commitments: HashMap<_, _> = HashMap::new();
let mut commitments: BTreeMap<_, _> = BTreeMap::new();

for participant_index in 1..=min_signers {
let participant_identifier = participant_index.try_into().expect("should be nonzero");
let (nonce, commitment) = frost::round1::commit(
participant_identifier,
key_packages
.get(&participant_identifier)
.unwrap()
Expand All @@ -155,8 +153,7 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
}

let message = "message to sign".as_bytes();
let comms = commitments.clone().into_values().collect();
let signing_package = frost::SigningPackage::new(comms, message);
let signing_package = frost::SigningPackage::new(commitments, message);

group.bench_with_input(
BenchmarkId::new("Round 2", min_signers),
Expand All @@ -175,21 +172,21 @@ pub fn bench_sign<C: Ciphersuite, R: RngCore + CryptoRng + Clone>(
},
);

let mut signature_shares = Vec::new();
let mut signature_shares = HashMap::new();
for participant_identifier in nonces.keys() {
let key_package = key_packages.get(participant_identifier).unwrap();
let nonces_to_use = &nonces.get(participant_identifier).unwrap();
let signature_share =
frost::round2::sign(&signing_package, nonces_to_use, key_package).unwrap();
signature_shares.push(signature_share);
signature_shares.insert(*key_package.identifier(), signature_share);
}

group.bench_with_input(
BenchmarkId::new("Aggregate", min_signers),
&(signing_package.clone(), signature_shares.clone(), pubkeys),
|b, (signing_package, signature_shares, pubkeys)| {
b.iter(|| {
frost::aggregate(signing_package, &signature_shares[..], pubkeys).unwrap();
frost::aggregate(signing_package, signature_shares, pubkeys).unwrap();
})
},
);
Expand Down
62 changes: 35 additions & 27 deletions frost-core/src/frost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//! Sharing, where shares are generated using Shamir Secret Sharing.

use std::{
collections::BTreeMap,
collections::{BTreeMap, HashMap},
fmt::{self, Debug},
ops::Index,
};
Expand Down Expand Up @@ -162,14 +162,14 @@ fn derive_interpolating_value<C: Ciphersuite>(
// Ala the sorting of B, just always sort by identifier in ascending order
//
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
for commitment in signing_package.signing_commitments().values() {
if commitment.identifier == *signer_id {
for commitment_identifier in signing_package.signing_commitments().keys() {
if *commitment_identifier == *signer_id {
continue;
}

num *= commitment.identifier;
num *= *commitment_identifier;

den *= commitment.identifier - *signer_id;
den *= *commitment_identifier - *signer_id;
}

if den == zero {
Expand All @@ -189,7 +189,7 @@ fn derive_interpolating_value<C: Ciphersuite>(
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
pub struct SigningPackage<C: Ciphersuite> {
/// The set of commitments participants published in the first round of the
/// protocol, ordered by their identifiers.
/// protocol.
signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
/// Message which each participant will sign.
///
Expand Down Expand Up @@ -224,14 +224,11 @@ where
///
/// The `signing_commitments` are sorted by participant `identifier`.
pub fn new(
signing_commitments: Vec<round1::SigningCommitments<C>>,
signing_commitments: BTreeMap<Identifier<C>, round1::SigningCommitments<C>>,
message: &[u8],
) -> SigningPackage<C> {
SigningPackage {
signing_commitments: signing_commitments
.into_iter()
.map(|s| (s.identifier, s))
.collect(),
signing_commitments,
message: message.to_vec(),
ciphersuite: (),
}
Expand Down Expand Up @@ -261,13 +258,13 @@ where
binding_factor_input_prefix.extend_from_slice(additional_prefix);

self.signing_commitments()
.values()
.map(|c| {
.keys()
.map(|identifier| {
let mut binding_factor_input = vec![];

binding_factor_input.extend_from_slice(&binding_factor_input_prefix);
binding_factor_input.extend_from_slice(c.identifier.serialize().as_ref());
(c.identifier, binding_factor_input)
binding_factor_input.extend_from_slice(identifier.serialize().as_ref());
(*identifier, binding_factor_input)
})
.collect()
}
Expand Down Expand Up @@ -317,14 +314,14 @@ where
// Ala the sorting of B, just always sort by identifier in ascending order
//
// https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding
for commitment in signing_package.signing_commitments().values() {
for (commitment_identifier, commitment) in signing_package.signing_commitments() {
// The following check prevents a party from accidentally revealing their share.
// Note that the '&&' operator would be sufficient.
if identity == commitment.binding.0 || identity == commitment.hiding.0 {
return Err(Error::IdentityCommitment);
}

let binding_factor = binding_factor_list[commitment.identifier].clone();
let binding_factor = binding_factor_list[*commitment_identifier].clone();

// Collect the binding commitments and their binding factors for one big
// multiscalar multiplication at the end.
Expand All @@ -349,6 +346,12 @@ where
/// Aggregates the signature shares to produce a final signature that
/// can be verified with the group public key.
///
/// `signature_shares` maps the identifier of each participant to the
/// [`round2::SignatureShare`] they sent. These identifiers must come from whatever mapping
/// the coordinator has between communication channels and participants, i.e.
/// they must have assurance that the [`round2::SignatureShare`] came from
/// the participant with that identifier.
///
/// This operation is performed by a coordinator that can communicate with all
/// the signing participants before publishing the final signature. The
/// coordinator can be one of the participants or a semi-trusted third party
Expand All @@ -360,7 +363,7 @@ where
/// service attack due to publishing an invalid signature.
pub fn aggregate<C>(
signing_package: &SigningPackage<C>,
signature_shares: &[round2::SignatureShare<C>],
signature_shares: &HashMap<Identifier<C>, round2::SignatureShare<C>>,
pubkeys: &keys::PublicKeyPackage<C>,
) -> Result<Signature<C>, Error<C>>
where
Expand All @@ -382,8 +385,8 @@ where
// [`aggregate`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-5.3
let mut z = <<C::Group as Group>::Field>::zero();

for signature_share in signature_shares {
z = z + signature_share.signature.z_share;
for signature_share in signature_shares.values() {
z = z + signature_share.share;
}

let signature = Signature {
Expand All @@ -408,27 +411,32 @@ where
);

// Verify the signature shares.
for signature_share in signature_shares {
for (signature_share_identifier, signature_share) in signature_shares {
// Look up the public key for this signer, where `signer_pubkey` = _G.ScalarBaseMult(s[i])_,
// and where s[i] is a secret share of the constant term of _f_, the secret polynomial.
let signer_pubkey = pubkeys
.signer_pubkeys
.get(&signature_share.identifier)
.get(signature_share_identifier)
.unwrap();

// Compute Lagrange coefficient.
let lambda_i =
derive_interpolating_value(&signature_share.identifier, signing_package)?;
let lambda_i = derive_interpolating_value(signature_share_identifier, signing_package)?;

let binding_factor = binding_factor_list[signature_share.identifier].clone();
let binding_factor = binding_factor_list[*signature_share_identifier].clone();

// Compute the commitment share.
let R_share = signing_package
.signing_commitment(&signature_share.identifier)
.signing_commitment(signature_share_identifier)
.to_group_commitment_share(&binding_factor);

// Compute relation values to verify this signature share.
signature_share.verify(&R_share, signer_pubkey, lambda_i, &challenge)?;
signature_share.verify(
*signature_share_identifier,
&R_share,
signer_pubkey,
lambda_i,
&challenge,
)?;
}

// We should never reach here; but we return the verification error to be safe.
Expand Down
4 changes: 3 additions & 1 deletion frost-core/src/frost/identifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ where
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Identifier")
.field(&<<C::Group as Group>::Field>::serialize(&self.0).as_ref())
.field(&hex::encode(
<<C::Group as Group>::Field>::serialize(&self.0).as_ref(),
))
.finish()
}
}
Expand Down
Loading
Loading