diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index e1e07b7..1c2a0f0 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -697,3 +697,58 @@ where Ok(signature) } + +/// signature aggreation +pub fn aggregate_spark( + signing_package: &SigningPackage, + signature_shares: &BTreeMap, round2::SignatureShare>, + verifying_key: &VerifyingKey, +) -> Result, Error> +where + C: Ciphersuite, +{ + // Check if signing_package.signing_commitments and signature_shares have + // the same set of identifiers, and if they are all in pubkeys.verifying_shares. + if signing_package.signing_commitments().len() != signature_shares.len() { + return Err(Error::UnknownIdentifier); + } + + // if !signing_package.signing_commitments().keys().all(|id| { + // #[cfg(feature = "cheater-detection")] + // return signature_shares.contains_key(id) && pubkeys.verifying_shares().contains_key(id); + // #[cfg(not(feature = "cheater-detection"))] + // return signature_shares.contains_key(id); + // }) { + // return Err(Error::UnknownIdentifier); + // } + + // Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the + // binding factor. + let binding_factor_list: BindingFactorList = + 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)?; + + // The aggregation of the signature shares by summing them up, resulting in + // a plain Schnorr signature. + // + // Implements [`aggregate`] from the spec. + // + // [`aggregate`]: https://datatracker.ietf.org/doc/html/rfc9591#name-signature-share-aggregation + let mut z = <::Field>::zero(); + + for signature_share in signature_shares.values() { + z = z + signature_share.share; + } + + let R = ::effective_nonce_element(group_commitment.0); + + let signature: Signature = + ::aggregate_sig_finalize(z, R, &verifying_key, &signing_package.sig_target); + + // Verify the aggregate signature + let verification_result = verifying_key.verify(signing_package.sig_target.clone(), &signature); + verification_result?; + + Ok(signature) +} diff --git a/frost-core/src/round2.rs b/frost-core/src/round2.rs index dd9b069..b72607b 100644 --- a/frost-core/src/round2.rs +++ b/frost-core/src/round2.rs @@ -233,6 +233,80 @@ pub fn sign( group_commitment, lambda_i, key_package, + key_package.verifying_key(), + challenge, + &signing_package.sig_target.sig_params, + ); + + Ok(signature_share) +} + +/// Performed once by each participant selected for the signing operation. +pub fn sign_spark( + signing_package: &SigningPackage, + signer_nonces: &round1::SigningNonces, + key_package: &frost::keys::KeyPackage, + inner_coef_set_x: &BTreeSet>, + outer_coef_set_x: Option<&BTreeSet>>, + outer_signer_id: Option<&Identifier>, + verifying_key: &VerifyingKey, +) -> Result, Error> { + if signing_package.signing_commitments().len() < key_package.min_signers as usize { + return Err(Error::IncorrectNumberOfCommitments); + } + + // Validate the signer's commitment is present in the signing package + let commitment = signing_package + .signing_commitments + .get(&key_package.identifier) + .ok_or(Error::MissingCommitment)?; + + // Validate if the signer's commitment exists + if &signer_nonces.commitments != commitment { + return Err(Error::IncorrectCommitment); + } + + // Encodes the signing commitment list produced in round one as part of generating [`BindingFactor`], the + // binding factor. + let binding_factor_list: BindingFactorList = + compute_binding_factor_list(signing_package, &verifying_key, &[]); + let binding_factor: frost::BindingFactor = binding_factor_list + .get(&key_package.identifier) + .ok_or(Error::UnknownIdentifier)? + .clone(); + + // Compute the group commitment from signing commitments produced in round one. + let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?; + + // Compute Lagrange coefficient. + let inner_lambda_i = + frost::compute_lagrange_coefficient(inner_coef_set_x, None, *key_package.identifier())?; + + let outer_lambda_i = match (outer_coef_set_x, outer_signer_id) { + (Some(outer_coef_set_x), Some(outer_signer_id)) => { + // If the user's key is a SSS shard, we need to compute the outer lambda_i + frost::compute_lagrange_coefficient(outer_coef_set_x, None, *outer_signer_id)? + } + // Otherwise, we use additive approach for user's key and SO's keys. + _ => <::Field>::one(), + }; + + let lambda_i = inner_lambda_i * outer_lambda_i; + // Compute the per-message challenge. + let challenge = ::challenge( + &group_commitment.0, + &verifying_key, + &signing_package.sig_target, + ); + + // Compute the Schnorr signature share. + let signature_share = ::compute_signature_share( + signer_nonces, + binding_factor, + group_commitment, + lambda_i, + key_package, + verifying_key, challenge, &signing_package.sig_target.sig_params, ); diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs index 14620cc..11be22e 100644 --- a/frost-core/src/traits.rs +++ b/frost-core/src/traits.rs @@ -375,6 +375,7 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug { _group_commitment: GroupCommitment, lambda_i: <::Field as Field>::Scalar, key_package: &KeyPackage, + _verifying_key: &VerifyingKey, challenge: Challenge, _sig_params: &Self::SigningParameters, ) -> round2::SignatureShare { diff --git a/frost-secp256k1-tr/src/lib.rs b/frost-secp256k1-tr/src/lib.rs index b9f4962..e12958c 100644 --- a/frost-secp256k1-tr/src/lib.rs +++ b/frost-secp256k1-tr/src/lib.rs @@ -429,6 +429,7 @@ impl Ciphersuite for Secp256K1Sha256 { group_commitment: GroupCommitment, lambda_i: <::Field as Field>::Scalar, key_package: &frost::keys::KeyPackage, + verifying_key: &VerifyingKey, challenge: Challenge, sig_params: &SigningParameters, ) -> round2::SignatureShare { @@ -438,7 +439,7 @@ impl Ciphersuite for Secp256K1Sha256 { } let mut kp = key_package.clone(); - let public_key = key_package.verifying_key(); + let public_key = verifying_key; let pubkey_is_odd: bool = public_key.y_is_odd(); let tweaked_pubkey_is_odd: bool = tweaked_public_key(public_key, sig_params.tapscript_merkle_root.as_ref()) @@ -720,6 +721,27 @@ pub mod round2 { ) -> Result { frost::round2::sign(signing_package, signer_nonces, key_package) } + + /// Spark custom signing operation. + pub fn sign_spark( + signing_package: &SigningPackage, + signer_nonces: &round1::SigningNonces, + key_package: &keys::KeyPackage, + inner_coef_set_x: &std::collections::BTreeSet, + outer_coef_set_x: Option<&std::collections::BTreeSet>, + outer_signer_id: Option<&Identifier>, + verifiying_key: &VerifyingKey, + ) -> Result { + frost::round2::sign_spark( + signing_package, + signer_nonces, + key_package, + inner_coef_set_x, + outer_coef_set_x, + outer_signer_id, + verifiying_key, + ) + } } /// A Schnorr signature on FROST(secp256k1, SHA-256). @@ -748,6 +770,15 @@ pub fn aggregate( frost::aggregate(signing_package, signature_shares, pubkeys) } +/// Spark +pub fn aggregate_spark( + signing_package: &SigningPackage, + signature_shares: &BTreeMap, + verifying_key: &VerifyingKey, +) -> Result { + frost::aggregate_spark(signing_package, signature_shares, verifying_key) +} + /// A signing key for a Schnorr signature on FROST(secp256k1, SHA-256). pub type SigningKey = frost_core::SigningKey; diff --git a/frost-secp256k1/src/lib.rs b/frost-secp256k1/src/lib.rs index a67634c..625b4c0 100644 --- a/frost-secp256k1/src/lib.rs +++ b/frost-secp256k1/src/lib.rs @@ -418,6 +418,27 @@ pub mod round2 { ) -> Result { frost::round2::sign(signing_package, signer_nonces, key_package) } + + /// Spark custom signing operation. + pub fn sign_spark( + signing_package: &SigningPackage, + signer_nonces: &round1::SigningNonces, + key_package: &keys::KeyPackage, + inner_coef_set_x: &std::collections::BTreeSet, + outer_coef_set_x: Option<&std::collections::BTreeSet>, + outer_signer_id: Option<&Identifier>, + verifiying_key: &VerifyingKey, + ) -> Result { + frost::round2::sign_spark( + signing_package, + signer_nonces, + key_package, + inner_coef_set_x, + outer_coef_set_x, + outer_signer_id, + verifiying_key, + ) + } } /// A Schnorr signature on FROST(secp256k1, SHA-256). @@ -446,6 +467,15 @@ pub fn aggregate( frost::aggregate(signing_package, signature_shares, pubkeys) } +/// Spark +pub fn aggregate_spark( + signing_package: &SigningPackage, + signature_shares: &BTreeMap, + verifying_key: &VerifyingKey, +) -> Result { + frost::aggregate_spark(signing_package, signature_shares, verifying_key) +} + /// A signing key for a Schnorr signature on FROST(secp256k1, SHA-256). pub type SigningKey = frost_core::SigningKey;