Skip to content

Commit

Permalink
add a verify_signature_share() function
Browse files Browse the repository at this point in the history
  • Loading branch information
conradoplg committed Sep 11, 2024
1 parent dcf1773 commit d67fa08
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 23 deletions.
4 changes: 4 additions & 0 deletions frost-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Entries are listed in reverse chronological order.
but it's likely to not require any code changes since most ciphersuite
implementations are probably just empty structs. The bound makes it possible
to use `frost_core::Error<C>` in `Box<dyn std::error::Error>`.
* 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(),

Check failure on line 875 in frost-core/src/tests/ciphersuite_generic.rs

View workflow job for this annotation

GitHub Actions / Clippy (stable) Results

this expression creates a reference which is immediately dereferenced by the compiler

error: this expression creates a reference which is immediately dereferenced by the compiler --> frost-core/src/tests/ciphersuite_generic.rs:875:44 | 875 | pubkeys.verifying_shares().get(&identifier).unwrap(), | ^^^^^^^^^^^ help: change this to: `identifier` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow = note: `-D clippy::needless-borrow` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]`

Check failure on line 875 in frost-core/src/tests/ciphersuite_generic.rs

View workflow job for this annotation

GitHub Actions / Clippy (stable) Results

this expression creates a reference which is immediately dereferenced by the compiler

error: this expression creates a reference which is immediately dereferenced by the compiler --> frost-core/src/tests/ciphersuite_generic.rs:875:44 | 875 | pubkeys.verifying_shares().get(&identifier).unwrap(), | ^^^^^^^^^^^ help: change this to: `identifier` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow = note: `-D clippy::needless-borrow` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]`
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(),

Check failure on line 890 in frost-core/src/tests/ciphersuite_generic.rs

View workflow job for this annotation

GitHub Actions / Clippy (stable) Results

this expression creates a reference which is immediately dereferenced by the compiler

error: this expression creates a reference which is immediately dereferenced by the compiler --> frost-core/src/tests/ciphersuite_generic.rs:890:44 | 890 | pubkeys.verifying_shares().get(&identifier).unwrap(), | ^^^^^^^^^^^ help: change this to: `identifier` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow

Check failure on line 890 in frost-core/src/tests/ciphersuite_generic.rs

View workflow job for this annotation

GitHub Actions / Clippy (stable) Results

this expression creates a reference which is immediately dereferenced by the compiler

error: this expression creates a reference which is immediately dereferenced by the compiler --> frost-core/src/tests/ciphersuite_generic.rs:890:44 | 890 | pubkeys.verifying_shares().get(&identifier).unwrap(), | ^^^^^^^^^^^ help: change this to: `identifier` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
&signature_share,
signing_package,
pubkeys.verifying_key(),
)
.expect_err("should have failed");
}
}

0 comments on commit d67fa08

Please sign in to comment.