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

add a verify_signature_share() function #727

Merged
merged 2 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

## 2.0.0-rc.0

Expand Down
106 changes: 83 additions & 23 deletions frost-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,37 +649,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 @@ -263,6 +263,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 @@ -860,3 +862,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");
}
}
Loading