Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/ZcashFoundation/frost into …
Browse files Browse the repository at this point in the history
…invalid-secret-share-culprit
  • Loading branch information
conradoplg committed Oct 15, 2024
2 parents e692d98 + cd352bb commit 71addd2
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 25 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ jobs:
run: cargo install cargo-llvm-cov

- name: Run tests
run: cargo llvm-cov --lcov --no-report --ignore-filename-regex '.*(tests).*|benches.rs|gencode|helpers.rs'
run: cargo llvm-cov --lcov --no-report --ignore-filename-regex '.*(tests).*|benches.rs|gencode|helpers.rs|interoperability_tests.rs'

- name: Generate coverage report
run: cargo llvm-cov report --lcov --ignore-filename-regex '.*(tests).*|benches.rs|gencode|helpers.rs' --output-path lcov.info
run: cargo llvm-cov report --lcov --ignore-filename-regex '.*(tests).*|benches.rs|gencode|helpers.rs|interoperability_tests.rs' --output-path lcov.info

- name: Upload coverage report to Codecov
uses: codecov/[email protected]
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ members = [
"frost-rerandomized",
"gencode"
]

[profile.test.package."*"]
opt-level = 3
4 changes: 4 additions & 0 deletions frost-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ Entries are listed in reverse chronological order.
implementations are probably just empty structs. The bound makes it possible
to use `frost_core::Error<C>` in `Box<dyn std::error::Error>`.
* Added getters to `round1::SecretPackage` and `round2::SecretPackage`.
* Added a `frost_core::verify_signature_share()` function which allows verifying
individual signature shares. This is not required for regular FROST usage but
might useful in certain situations where it is desired to verify each
individual signature share before aggregating the signature.
* It is now possible to identify the culprit in `frost_core::keys::dkg::part3()`
if an invalid secret share was sent by one of the participants (by calling
frost_core::Error<C>::culprit()`).
Expand Down
107 changes: 84 additions & 23 deletions frost-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ where

/// Optional cheater detection feature
/// Each share is verified to find the cheater
#[cfg(feature = "cheater-detection")]
fn detect_cheater<C: Ciphersuite>(
group_commitment: GroupCommitment<C>,
pubkeys: &keys::PublicKeyPackage<C>,
Expand All @@ -649,37 +650,97 @@ fn detect_cheater<C: Ciphersuite>(
)?;

// Verify the signature shares.
for (signature_share_identifier, signature_share) in signature_shares {
for (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
let verifying_share = pubkeys
.verifying_shares
.get(signature_share_identifier)
.get(identifier)
.ok_or(Error::UnknownIdentifier)?;

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

let binding_factor = binding_factor_list
.get(signature_share_identifier)
.ok_or(Error::UnknownIdentifier)?;

// Compute the commitment share.
let R_share = signing_package
.signing_commitment(signature_share_identifier)
.ok_or(Error::UnknownIdentifier)?
.to_group_commitment_share(binding_factor);

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

// We should never reach here; but we return an error to be safe.
Err(Error::InvalidSignature)
}

/// Verify a signature share for the given participant `identifier`,
/// `verifying_share` and `signature_share`; with the `signing_package`
/// for which the signature share was produced and with the group's
/// `verifying_key`.
///
/// This is not required for regular FROST usage but might useful in certain
/// situations where it is desired to verify each individual signature share
/// before aggregating the signature.
pub fn verify_signature_share<C: Ciphersuite>(
identifier: Identifier<C>,
verifying_share: &keys::VerifyingShare<C>,
signature_share: &round2::SignatureShare<C>,
signing_package: &SigningPackage<C>,
verifying_key: &VerifyingKey<C>,
) -> Result<(), Error<C>> {
// Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the
// binding factor.
let binding_factor_list: BindingFactorList<C> =
compute_binding_factor_list(signing_package, verifying_key, &[])?;
// Compute the group commitment from signing commitments produced in round one.
let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?;

// Compute the per-message challenge.
let challenge = crate::challenge::<C>(
&group_commitment.to_element(),
verifying_key,
signing_package.message().as_slice(),
)?;

verify_signature_share_precomputed(
identifier,
signing_package,
&binding_factor_list,
signature_share,
verifying_share,
challenge,
)
}

/// Similar to [`verify_signature_share()`] but using a precomputed
/// `binding_factor_list` and `challenge`.
#[cfg_attr(feature = "internals", visibility::make(pub))]
#[cfg_attr(docsrs, doc(cfg(feature = "internals")))]
fn verify_signature_share_precomputed<C: Ciphersuite>(
signature_share_identifier: Identifier<C>,
signing_package: &SigningPackage<C>,
binding_factor_list: &BindingFactorList<C>,
signature_share: &round2::SignatureShare<C>,
verifying_share: &keys::VerifyingShare<C>,
challenge: Challenge<C>,
) -> Result<(), Error<C>> {
let lambda_i = derive_interpolating_value(&signature_share_identifier, signing_package)?;

let binding_factor = binding_factor_list
.get(&signature_share_identifier)
.ok_or(Error::UnknownIdentifier)?;

let R_share = signing_package
.signing_commitment(&signature_share_identifier)
.ok_or(Error::UnknownIdentifier)?
.to_group_commitment_share(binding_factor);

signature_share.verify(
signature_share_identifier,
&R_share,
verifying_share,
lambda_i,
&challenge,
)?;

Ok(())
}
35 changes: 35 additions & 0 deletions frost-core/src/tests/ciphersuite_generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ pub fn check_sign<C: Ciphersuite + PartialEq, R: RngCore + CryptoRng>(
signature_shares.clone(),
);

check_verify_signature_share(&pubkey_package, &signing_package, &signature_shares);

// Aggregate (also verifies the signature shares)
let group_signature = frost::aggregate(&signing_package, &signature_shares, &pubkey_package)?;

Expand Down Expand Up @@ -933,3 +935,36 @@ fn check_verifying_shares<C: Ciphersuite>(
assert_eq!(e.culprit(), Some(id));
assert_eq!(e, Error::InvalidSignatureShare { culprit: id });
}

// Checks if `verify_signature_share()` works correctly.
fn check_verify_signature_share<C: Ciphersuite>(
pubkeys: &PublicKeyPackage<C>,
signing_package: &SigningPackage<C>,
signature_shares: &BTreeMap<Identifier<C>, SignatureShare<C>>,
) {
for (identifier, signature_share) in signature_shares {
frost::verify_signature_share(
*identifier,
pubkeys.verifying_shares().get(identifier).unwrap(),
signature_share,
signing_package,
pubkeys.verifying_key(),
)
.expect("should pass");
}

for (identifier, signature_share) in signature_shares {
let one = <<C as Ciphersuite>::Group as Group>::Field::one();
// Corrupt share
let signature_share = SignatureShare::new(signature_share.to_scalar() + one);

frost::verify_signature_share(
*identifier,
pubkeys.verifying_shares().get(identifier).unwrap(),
&signature_share,
signing_package,
pubkeys.verifying_key(),
)
.expect_err("should have failed");
}
}

0 comments on commit 71addd2

Please sign in to comment.