ZcashFoundation / frost

Rust implementation of FROST (Flexible Round-Optimised Schnorr Threshold signatures) by the Zcash Foundation
https://frost.zfnd.org
Other
145 stars 54 forks source link

Add functions to validate signature shares under "internals" feature #726

Closed StackOverflowExcept1on closed 1 month ago

StackOverflowExcept1on commented 1 month ago

I'm trying to implement ROAST: Robust Asynchronous Schnorr Threshold Signatures using frost-core and I see that it has SignatureShare::verify() API, but it seems to be not enough to implement ROAST.

The thing is that ROAST collects signature shares and validates each of them instead of collecting everything at once and then accusing some participant of not following the protocol (like frost::aggregate() does).

It would also be nice to move the function of checking one signature share out of detect_cheater and make it internal. https://github.com/ZcashFoundation/frost/blob/dcf17732f791cd5c69aeed5bb4ff60d019a57ee8/frost-core/src/lib.rs#L651-L658

StackOverflowExcept1on commented 1 month ago

Example of implementation:

//! Copy-paste of [`frost_core::aggregate()`].

use frost_secp256k1 as frost;

pub trait SignatureShareExt {
    fn verify2(
        &self,
        signature_share_identifier: &frost::Identifier,
        signing_package: &frost::SigningPackage,
        pubkeys: &frost::keys::PublicKeyPackage,
    ) -> Result<(), frost::Error>;
}

impl SignatureShareExt for frost::round2::SignatureShare {
    fn verify2(
        &self,
        signature_share_identifier: &frost::Identifier,
        signing_package: &frost::SigningPackage,
        pubkeys: &frost::keys::PublicKeyPackage,
    ) -> Result<(), frost::Error> {
        let binding_factor_list =
            frost_core::compute_binding_factor_list(signing_package, pubkeys.verifying_key(), &[])?;
        let group_commitment =
            frost_core::compute_group_commitment(signing_package, &binding_factor_list)?;

        let challenge = frost_core::challenge(
            &group_commitment.to_element(),
            pubkeys.verifying_key(),
            signing_package.message().as_slice(),
        )?;

        let signer_pubkey = pubkeys
            .verifying_shares()
            .get(signature_share_identifier)
            .ok_or(frost::Error::UnknownIdentifier)?;

        let lambda_i =
            frost_core::derive_interpolating_value(signature_share_identifier, signing_package)?;

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

        #[allow(non_snake_case)]
        let R_share = signing_package
            .signing_commitment(signature_share_identifier)
            .ok_or(frost::Error::UnknownIdentifier)?
            .to_group_commitment_share(binding_factor);

        self.verify(
            *signature_share_identifier,
            &R_share,
            signer_pubkey,
            lambda_i,
            &challenge,
        )
    }
}
conradoplg commented 1 month ago

Thanks for reporting this. Coincidentally this was just reported in #724 so I'm closing this. I've just opened a PR for it though, could you double check if that addresses your use case?