From 9b91df2a7ef0ebaf0dd1106ba324966a4539d629 Mon Sep 17 00:00:00 2001 From: jfdreis Date: Tue, 18 Feb 2025 17:47:20 +0000 Subject: [PATCH] f Co-authored-by: Manuel Santos --- Cargo.lock | 82 ++----- libs/client-core/Cargo.toml | 2 +- libs/execution-engine/mpc-vm/Cargo.toml | 2 +- libs/nada-value/Cargo.toml | 2 +- libs/protocols/Cargo.toml | 8 +- libs/protocols/src/lib.rs | 1 + .../src/threshold_ecdsa/signing/test.rs | 6 +- libs/protocols/src/threshold_eddsa/README.md | 4 + libs/protocols/src/threshold_eddsa/mod.rs | 12 + libs/protocols/src/threshold_eddsa/output.rs | 28 +++ libs/protocols/src/threshold_eddsa/state.rs | 210 ++++++++++++++++++ libs/protocols/src/threshold_eddsa/test.rs | 124 +++++++++++ libs/threshold-keypair/Cargo.toml | 6 +- tests/functional/Cargo.toml | 2 +- 14 files changed, 411 insertions(+), 78 deletions(-) create mode 100644 libs/protocols/src/threshold_eddsa/README.md create mode 100644 libs/protocols/src/threshold_eddsa/mod.rs create mode 100644 libs/protocols/src/threshold_eddsa/output.rs create mode 100644 libs/protocols/src/threshold_eddsa/state.rs create mode 100644 libs/protocols/src/threshold_eddsa/test.rs diff --git a/Cargo.lock b/Cargo.lock index 112ad2e..c4166ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -712,42 +712,22 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "cggmp21" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7371c7cb9c13f63c49d8637cdb47b22819e586ac2780f01151f2b8ea591ec7ce" +checksum = "251e13fefa29935b4d502580e340b79fbdbe2fb3189e7b16054fe2252895a4e0" dependencies = [ - "cggmp21-keygen 0.4.0", + "cggmp21-keygen", "digest 0.10.7", "futures", "generic-ec", "generic-ec-zkp", + "hd-wallet", "hex", - "key-share 0.5.2", + "key-share", "paillier-zk", "rand_core", "rand_hash", - "round-based 0.3.2", - "serde", - "serde_with 2.3.3", - "sha2 0.10.8", - "thiserror 1.0.65", - "udigest", -] - -[[package]] -name = "cggmp21-keygen" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035c720da632d7303d2286f9ba4923dfba646f99465695ef21fc72b9e330476f" -dependencies = [ - "digest 0.10.7", - "displaydoc", - "generic-ec", - "generic-ec-zkp", - "hex", - "key-share 0.5.2", - "rand_core", - "round-based 0.3.2", + "round-based", "serde", "serde_with 2.3.3", "sha2 0.10.8", @@ -766,9 +746,9 @@ dependencies = [ "generic-ec", "generic-ec-zkp", "hex", - "key-share 0.6.0", + "key-share", "rand_core", - "round-based 0.4.1", + "round-based", "serde", "serde_with 2.3.3", "sha2 0.10.8", @@ -2192,13 +2172,14 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af482004b1f70b5b3b584c3fc3733dd24c71f8c0081a6087f49e09746746788a" dependencies = [ - "cggmp21-keygen 0.5.0", + "cggmp21-keygen", "digest 0.10.7", "generic-ec", "hd-wallet", "k256", - "key-share 0.6.0", + "key-share", "rand_core", + "round-based", "serde", "sha2 0.10.8", "static_assertions", @@ -3064,22 +3045,6 @@ dependencies = [ "signature", ] -[[package]] -name = "key-share" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10fce16fa05b2c8e6e36096506ef4a98d410159991ccfeb38a7eeb5f296588f6" -dependencies = [ - "displaydoc", - "generic-ec", - "generic-ec-zkp", - "hex", - "rand_core", - "serde", - "serde_with 2.3.3", - "thiserror 1.0.65", -] - [[package]] name = "key-share" version = "0.6.0" @@ -3502,7 +3467,7 @@ dependencies = [ "generic-ec", "givre", "indexmap 2.6.0", - "key-share 0.5.2", + "key-share", "math_lib", "nada-type", "node-api", @@ -3632,7 +3597,7 @@ version = "0.1.0" dependencies = [ "basic-types", "generic-ec", - "key-share 0.5.2", + "key-share", "math_lib", "mpc-vm", "nada-value", @@ -4677,16 +4642,17 @@ dependencies = [ "basic-types", "cggmp21", "criterion", + "givre", "gmp-mpfr-sys", "itertools 0.13.0", - "key-share 0.5.2", + "key-share", "math_lib", "num-bigint", "once_cell", "rand", "rand_chacha", "rayon", - "round-based 0.3.2", + "round-based", "rstest", "serde", "serde_json", @@ -5147,20 +5113,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "round-based" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81564866f5617d497753563151d8beb80d61e925e904d94b7e8a202b721e931e" -dependencies = [ - "displaydoc", - "futures-util", - "phantom-type 0.3.1", - "round-based-derive", - "thiserror 1.0.65", - "tracing", -] - [[package]] name = "round-based" version = "0.4.1" @@ -6607,7 +6559,7 @@ dependencies = [ "cggmp21", "generic-ec", "givre", - "key-share 0.5.2", + "key-share", "rand", "rstest", "serde", diff --git a/libs/client-core/Cargo.toml b/libs/client-core/Cargo.toml index 072cea5..c5fc726 100644 --- a/libs/client-core/Cargo.toml +++ b/libs/client-core/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -key-share = { version = "0.5.0", default-features = false, features = ["spof"] } +key-share = { version = "0.6.0", default-features = false, features = ["spof"] } basic-types = { path = "../basic-types" } threshold-keypair = { path = "../../libs/threshold-keypair", default-features = false } diff --git a/libs/execution-engine/mpc-vm/Cargo.toml b/libs/execution-engine/mpc-vm/Cargo.toml index 1997397..3f06b40 100644 --- a/libs/execution-engine/mpc-vm/Cargo.toml +++ b/libs/execution-engine/mpc-vm/Cargo.toml @@ -31,7 +31,7 @@ rstest = "0.21.0" serde = { version = "1", features = ["derive"] } test-programs = { path = "../../../nada-lang/test-programs" } mpc-vm = { path = ".", features = ["simulator"] } -cggmp21 = { version = "0.5.0", features = ["curve-secp256k1"] } +cggmp21 = { version = "0.6.0", features = ["curve-secp256k1"] } [features] default = [] diff --git a/libs/nada-value/Cargo.toml b/libs/nada-value/Cargo.toml index 59a8f55..9ef716f 100644 --- a/libs/nada-value/Cargo.toml +++ b/libs/nada-value/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" anyhow = "1.0.86" enum-as-inner = "0.6.1" indexmap = { version = "2.6.0" } -key-share = { version = "0.5.0", default-features = false, features = ["spof"] } +key-share = { version = "0.6.0", default-features = false, features = ["spof"] } generic-ec = { version = "0.4.2", default-features = false, features = ["alloc", "curve-secp256k1"] } givre = { version = "0.2.0", features = ["ciphersuite-secp256k1", "ciphersuite-ed25519", "spof", "cggmp21-keygen"] } num-bigint = "0.4.6" diff --git a/libs/protocols/Cargo.toml b/libs/protocols/Cargo.toml index f5953b4..3459e58 100644 --- a/libs/protocols/Cargo.toml +++ b/libs/protocols/Cargo.toml @@ -6,12 +6,12 @@ edition = "2021" [dependencies] anyhow = "1" itertools = "0.13" -key-share = { version = "0.5.0", default-features = false, features = ["spof"] } +key-share = { version = "0.6.0", default-features = false, features = ["spof"] } num-bigint = "0.4" rand = "0.8" rand_chacha = "0.3.1" rayon = { version = "1.10", optional = true } -round-based = "0.3" +round-based = "0.4" serde = { version = "1", features = ["derive"] } thiserror = "1" uuid = { version = "1.10", features = ["v4"], optional = true } @@ -24,12 +24,14 @@ state-machine-derive = { path = "../state-machine-derive" } state-machine-macros = { path = "../state-machine-macros" } threshold-keypair = { path = "../threshold-keypair" } -cggmp21 = { version = "0.5.0", features = ["curve-secp256k1", "state-machine"] } +cggmp21 = { version = "0.6.0", features = ["curve-secp256k1", "state-machine"] } gmp-mpfr-sys = { version = "1.6.4", features = ["force-cross"] } sha2 = "0.10" serde_json = "1" once_cell = "1.20.1" +givre = { version = "0.2.0", features = ["ciphersuite-ed25519", "full-signing", "serde" ] } + [dev-dependencies] criterion = "0.5.1" num-bigint = "0.4.6" diff --git a/libs/protocols/src/lib.rs b/libs/protocols/src/lib.rs index d819410..cdd5ee9 100644 --- a/libs/protocols/src/lib.rs +++ b/libs/protocols/src/lib.rs @@ -22,6 +22,7 @@ pub mod multiplication; pub mod random; pub mod reveal; pub mod threshold_ecdsa; +pub mod threshold_eddsa; #[cfg(any(test, feature = "validation"))] pub mod simulator; diff --git a/libs/protocols/src/threshold_ecdsa/signing/test.rs b/libs/protocols/src/threshold_ecdsa/signing/test.rs index f36b76b..ff39e8b 100644 --- a/libs/protocols/src/threshold_ecdsa/signing/test.rs +++ b/libs/protocols/src/threshold_ecdsa/signing/test.rs @@ -59,7 +59,7 @@ impl EcdsaSignProtocol { impl Protocol for EcdsaSignProtocol { type State = EcdsaSignState; - type PrepareOutput = EcdsSignConfig; + type PrepareOutput = EcdsaSignConfig; fn prepare(&self, parties: &[PartyId]) -> Result { let sorted_parties = SortedParties::new(parties.to_vec()); @@ -76,7 +76,7 @@ impl Protocol for EcdsaSignProtocol { private_key_shares.insert(party_id.clone(), pk_share.clone()); } - Ok(EcdsSignConfig { + Ok(EcdsaSignConfig { eid: self.eid.clone(), parties: parties.to_vec(), private_key_shares, @@ -112,7 +112,7 @@ impl Protocol for EcdsaSignProtocol { } /// The internal configuration of a EcdsSignConfig protocol. -struct EcdsSignConfig { +struct EcdsaSignConfig { eid: Vec, parties: Vec, private_key_shares: PartyShares>, diff --git a/libs/protocols/src/threshold_eddsa/README.md b/libs/protocols/src/threshold_eddsa/README.md new file mode 100644 index 0000000..3261c8b --- /dev/null +++ b/libs/protocols/src/threshold_eddsa/README.md @@ -0,0 +1,4 @@ +# Signing protocol + +- This protocol is the main singing protocol of the FROST EdDSA Signing protocol from the [givre](https://docs.rs/givre/latest/givre/index.html) library. +- It generates a signature that is sent to the client. diff --git a/libs/protocols/src/threshold_eddsa/mod.rs b/libs/protocols/src/threshold_eddsa/mod.rs new file mode 100644 index 0000000..c06b3e7 --- /dev/null +++ b/libs/protocols/src/threshold_eddsa/mod.rs @@ -0,0 +1,12 @@ +//! Threshold EdDSA protocol +pub mod output; +pub mod state; + +pub use state::*; +#[cfg(test)] +pub mod test; + +use state_machine::StateMachine; + +/// The Eddsa Signing state machine. +pub type EddsaSignStateMachine = StateMachine; diff --git a/libs/protocols/src/threshold_eddsa/output.rs b/libs/protocols/src/threshold_eddsa/output.rs new file mode 100644 index 0000000..821b0c9 --- /dev/null +++ b/libs/protocols/src/threshold_eddsa/output.rs @@ -0,0 +1,28 @@ +//! Outputs for the EdDSA signing protocol. +use givre::signing::round2::SigningError; +use std::fmt::Display; +use threshold_keypair::signature::EddsaSignature; + +/// The EdDSA signing output. +pub enum EddsaSignatureOutput { + /// The protocol was successful. + Success { + /// The output elements. + element: EddsaSignature, + }, + + /// This or a subprotocol aborted by chance. + Abort { + /// The reason why it aborted + reason: SigningError, + }, +} + +impl Display for EddsaSignatureOutput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Success { .. } => write!(f, "Success"), + Self::Abort { .. } => write!(f, "Abort"), + } + } +} diff --git a/libs/protocols/src/threshold_eddsa/state.rs b/libs/protocols/src/threshold_eddsa/state.rs new file mode 100644 index 0000000..65603c4 --- /dev/null +++ b/libs/protocols/src/threshold_eddsa/state.rs @@ -0,0 +1,210 @@ +//! The EdDSA-SIGNING protocol state machine. +//! +//! This state machine generates the EdDSA signature. It uses the FROST signing protocol. + +use crate::{threshold_ecdsa::util::SortedParties, threshold_eddsa::output::EddsaSignatureOutput}; +use threshold_keypair::{privatekey::ThresholdPrivateKeyShare, signature::EddsaSignature}; + +use anyhow::{anyhow, Context}; +use basic_types::{jar::PartyJar, PartyMessage}; +use cggmp21::generic_ec::curves::Ed25519; + +use givre::{ + ciphersuite, + signing::{aggregate::aggregate, full_signing::Msg, round1::commit, round2::sign}, +}; +use key_share::Validate; + +use rand::rngs::OsRng; +use state_machine::{ + state::{Recipient, RecipientMessage, StateMachineMessage}, + StateMachineStateExt, StateMachineStateOutput, StateMachineStateResult, +}; +use state_machine_derive::StateMachineState; + +use shamir_sharing::party::PartyId; + +/// The Threshold EdDSA protocol state definitions. +pub mod states { + use basic_types::jar::PartyJar; + use cggmp21::generic_ec::curves::Ed25519; + use givre::signing::{ + round1::{PublicCommitments, SecretNonces}, + round2::SigShare, + }; + use key_share::{CoreKeyShare, KeyInfo}; + + use crate::threshold_ecdsa::util::SortedParties; + /// The protocol is waiting for the pre processing phase to finish + pub struct WaitingPublicCommits { + /// Key Share + pub(crate) key_share: CoreKeyShare, + /// Nonces from pre processing phase + pub(crate) secret_nonces: SecretNonces, + /// Vector of Public Commitments from pre processing phase + pub(crate) pcommits: PartyJar>, + /// Message to sign + pub(crate) msg: Vec, + /// Sorted Parties + pub(crate) sorted_parties: SortedParties, + } + /// The protocol is waiting for the signatures shares + /// from each signer + pub struct WaitingSigShares { + /// Public Key Info + pub(crate) key_info: KeyInfo, + /// Vector of Public Commitments from pre processing phase + pub(crate) pcommits: PartyJar>, + /// Vector with the Signatures Shares from each signer + pub(crate) sig_shares: PartyJar>, + /// Message to sign + pub(crate) msg: Vec, + /// Sorted Parties + pub(crate) sorted_parties: SortedParties, + } +} + +/// The state machine for the EdDSA protocol. +#[derive(StateMachineState)] +#[state_machine( + recipient_id = "PartyId", + input_message = "PartyMessage>", + output_message = "Msg", + final_result = "EddsaSignatureOutput", + handle_message_fn = "Self::handle_message" +)] +pub enum EddsaSignState { + /// We are waiting for the Public Commits + #[state_machine(completed = "state.pcommits.is_full()", transition_fn = "Self::transition_waiting_public_commits")] + WaitingPublicCommits(states::WaitingPublicCommits), + /// We are waiting for the SigShares + #[state_machine(completed = "state.sig_shares.is_full()", transition_fn = "Self::transition_waiting_sigshares")] + WaitingSigShares(states::WaitingSigShares), +} + +use EddsaSignState::*; +/// Implementations on EddsaSignState Machine +impl EddsaSignState { + /// Construct a new EddsaSignState + pub fn new( + parties: Vec, + msg: Vec, + incomplete_key_share: ThresholdPrivateKeyShare, + ) -> Result<(Self, Vec>), EddsaSignError> { + // Compute input elements required for givre's functions + let sorted_parties = SortedParties::new(parties); + let parties_len = sorted_parties.len(); + let key_share = incomplete_key_share.into_inner(); + + // Round 1 of FROST protocol + let mut csprng = OsRng; + let (secret_nonces, commits) = commit::(&mut csprng, &key_share); + let pcommits = PartyJar::new(parties_len as usize); + + let next_state = states::WaitingPublicCommits { + key_share, + secret_nonces, + pcommits, + msg, + sorted_parties: sorted_parties.clone(), + }; + + let messages = vec![RecipientMessage::new(Recipient::Multiple(sorted_parties.parties()), Msg::Round1(commits))]; + Ok((WaitingPublicCommits(next_state), messages)) + } + #[allow(clippy::indexing_slicing)] + fn transition_waiting_public_commits(state: states::WaitingPublicCommits) -> StateMachineStateResult { + // Steps 1 to 5 of Figure 3 of FROST protocol + + // Build signers_pcommits = [(SignerIndex, PublicCommits)] + let signers_pcommits: Vec<_> = state + .pcommits + .elements() + .map(|(party_id, pcommit)| { + state + .sorted_parties + .index(party_id.clone()) + .map(|party_index| (party_index, *pcommit)) + .map_err(|e| anyhow!("Error converting PartyId to u16: {e}")) + }) + .collect::>() + .map_err(|e| anyhow!("Error converting PartyId to u16: {e}"))?; + + //Obtain Signature Shares + let sigshare = + sign::(&state.key_share, state.secret_nonces, &state.msg, &signers_pcommits) + .map_err(|e| anyhow!("Signing Error: {e}"))?; + + // Build next state + let parties_len = state.sorted_parties.len(); + let sig_shares = PartyJar::new(parties_len as usize); + let key_info = + state.key_share.into_inner().key_info.validate().map_err(|e| anyhow!("Error in Validate: {e}"))?; + let sorted_parties = state.sorted_parties.clone(); + let next_state = + states::WaitingSigShares { key_info, pcommits: state.pcommits, sig_shares, msg: state.msg, sorted_parties }; + + //Build Message + let message = RecipientMessage::new(Recipient::Multiple(state.sorted_parties.parties()), Msg::Round2(sigshare)); + let messages = vec![message]; + Ok(StateMachineStateOutput::Messages(WaitingSigShares(next_state), messages)) + } + + #[allow(clippy::indexing_slicing)] + fn transition_waiting_sigshares(state: states::WaitingSigShares) -> StateMachineStateResult { + // Build signers: [(SignerIndex, PublicCommits, SigShare)], + let signers: Vec<_> = state + .pcommits + .elements() + .zip(state.sig_shares.elements()) + .map(|((party_id1, commit), (party_id2, sigshare))| { + if party_id1 != party_id2 { + return Err(anyhow!("Error in Validate")); + } + state + .sorted_parties + .index(party_id1.clone()) + .map(|party| (party, *commit, *sigshare)) + .map_err(|e| anyhow!("Error in Validate: {e}")) + }) + .collect::>() + .map_err(|e| anyhow!("Signing Error: {e}"))?; + + // Step 7 of FROST protocol + let sig = aggregate(&state.key_info, &signers, &state.msg) + .map_err(|e| anyhow!("Error in the aggregation of SigShares: {e}"))?; + + Ok(StateMachineStateOutput::Final(EddsaSignatureOutput::Success { element: EddsaSignature { signature: sig } })) + } + + fn handle_message(mut state: Self, message: PartyMessage>) -> StateMachineStateResult { + let (party_id, message) = message.into_parts(); + match (message, &mut state) { + (Msg::Round1(message), WaitingPublicCommits(inner)) => { + inner + .pcommits + .add_element(party_id.clone(), message) + .context("adding public commits") + .map_err(|e| anyhow!("Error in the adding public commits: {e}"))?; + state.advance_if_completed() + } + (Msg::Round2(message), WaitingSigShares(inner)) => { + inner + .sig_shares + .add_element(party_id, message) + .context("adding signature shares") + .map_err(|e| anyhow!("Error in the adding signature shares: {e}"))?; + state.advance_if_completed() + } + (message, _) => Ok(StateMachineStateOutput::OutOfOrder(state, PartyMessage::new(party_id, message))), + } + } +} + +/// An error during the EdDSA-SIGNING state creation. +#[derive(Debug, thiserror::Error)] +pub enum EddsaSignError { + /// Unexpected error + #[error("unexpected error: {0}")] + Unexpected(anyhow::Error), +} diff --git a/libs/protocols/src/threshold_eddsa/test.rs b/libs/protocols/src/threshold_eddsa/test.rs new file mode 100644 index 0000000..a1f3e67 --- /dev/null +++ b/libs/protocols/src/threshold_eddsa/test.rs @@ -0,0 +1,124 @@ +#![allow(clippy::arithmetic_side_effects, clippy::panic, clippy::indexing_slicing)] + +use super::output::EddsaSignatureOutput; +use crate::{ + simulator::symmetric::{InitializedProtocol, Protocol, SymmetricProtocolSimulator}, + threshold_eddsa::state::EddsaSignState, +}; +use anyhow::{anyhow, Error, Result}; +use basic_types::PartyId; +use rstest::rstest; +use threshold_keypair::{ + privatekey::{ThresholdPrivateKey, ThresholdPrivateKeyShare}, + publickey::ThresholdPublicKey, + signature::EddsaSignature, +}; + +use crate::threshold_ecdsa::util::SortedParties; +use cggmp21::generic_ec::{curves::Ed25519, NonZero, SecretScalar}; +use givre::{signing::aggregate::Signature, Ciphersuite}; + +use rand_chacha::rand_core::OsRng; +use shamir_sharing::secret_sharer::PartyShares; + +struct EddsaSignProtocol { + private_key: ThresholdPrivateKey, + message: Vec, +} + +impl EddsaSignProtocol { + fn new(private_key: ThresholdPrivateKey, message: Vec) -> Self { + Self { private_key, message } + } +} + +impl Protocol for EddsaSignProtocol { + type State = EddsaSignState; + type PrepareOutput = EddsaSignConfig; + + fn prepare(&self, parties: &[PartyId]) -> Result { + let sorted_parties = SortedParties::new(parties.to_vec()); + let n: u16 = parties + .len() + .try_into() + .map_err(|_| anyhow!("Failed to convert the length of parties (which is {}) to a u16", parties.len()))?; + + let pk_shares = self.private_key.generate_shares(n).map_err(|e| anyhow!("generation shares failed: {e}"))?; + let mut private_key_shares: PartyShares> = PartyShares::default(); + for (party_id, pk_share) in sorted_parties.parties().iter().zip(pk_shares.iter()) { + private_key_shares.insert(party_id.clone(), pk_share.clone()); + } + + Ok(EddsaSignConfig { parties: parties.to_vec(), private_key_shares, message: self.message.clone() }) + } + + fn initialize( + &self, + party_id: PartyId, + config: &Self::PrepareOutput, + ) -> Result, anyhow::Error> { + let key_share = config + .private_key_shares + .get(&party_id) + .cloned() + .ok_or_else(|| anyhow!("shares for party {party_id:?}"))?; + + let (state, messages) = EddsaSignState::new(config.parties.clone(), config.message.clone(), key_share)?; + Ok(InitializedProtocol::new(state, messages)) + } +} + +/// The internal configuration of a EcdsSignConfig protocol. +struct EddsaSignConfig { + parties: Vec, + private_key_shares: PartyShares>, + message: Vec, +} + +fn verify(pk: ThresholdPublicKey, signature: EddsaSignature, message: Vec) -> bool { + let givre_sig = Signature { r: signature.signature.r, z: signature.signature.z }; + let pk_point = NonZero::from_point(*pk.as_point()).expect("Public key should not be zero!"); + + // Normalize the point using givre's normalize_point + let pk_normalized = Ciphersuite::normalize_point(pk_point); // NormalizedPoint<_, Point> + + // Call verify with the normalized and non-zero point + givre_sig.verify(&pk_normalized, &message).is_ok() +} + +#[rstest] +fn end_to_end() { + //0. Network configuration + let max_rounds = 100; + let network_size = 3; + // 1. Message generation + let message_to_sign = b"Transaction with plenty of bitcoin".to_vec(); + // 2. Secret key generation + let mut csprng = OsRng; + let sk_val = SecretScalar::::random(&mut csprng); + let sk: ThresholdPrivateKey = ThresholdPrivateKey::::from_scalar(sk_val).unwrap(); + // 3. Run protocol + let protocol = EddsaSignProtocol::new(sk.clone(), message_to_sign.clone()); + let simulator = SymmetricProtocolSimulator::new(network_size, max_rounds); + let outputs = simulator.run_protocol(&protocol).expect("protocol run failed"); + // 4. Collect Signatures + let mut signature = Vec::new(); + for output in outputs { + match output.output { + EddsaSignatureOutput::Success { element: sig } => { + signature.push(sig); + } + EddsaSignatureOutput::Abort { reason } => { + panic!("Aborted with reason: {:?}", reason); + } + } + } + // 5. Check signatures from all parties are the same + if signature.iter().all(|sig| *sig != signature[0]) { + panic!("Parties provided different signatures"); + } + // 6. Verifies signature is valid under message_digest and public key + let pk = ThresholdPublicKey::::from_private_key(&sk); + let verifies = verify(pk, signature[0].clone(), message_to_sign); + assert!(verifies) +} diff --git a/libs/threshold-keypair/Cargo.toml b/libs/threshold-keypair/Cargo.toml index 291d4c1..f692de3 100644 --- a/libs/threshold-keypair/Cargo.toml +++ b/libs/threshold-keypair/Cargo.toml @@ -5,9 +5,9 @@ edition = "2021" description = "Utilities for Threshold signing" [dependencies] -key-share = { version = "0.5.0", default-features = false, features = ["spof"] } +key-share = { version = "0.6.0", default-features = false, features = ["spof"] } generic-ec = { version = "0.4.2", features = ["alloc", "curve-secp256k1", "curve-ed25519"] } -givre = { version = "0.2.0", features = ["ciphersuite-secp256k1", "ciphersuite-ed25519", "spof", "cggmp21-keygen"] } +givre = { version = "0.2.0", default-features = false, features = ["ciphersuite-ed25519"] } serde = { version = "1", default-features = false, features = ["derive"], optional = true } thiserror = "1" rand = { version = "0.8" } @@ -19,7 +19,7 @@ serde_with = { version = "3.12.0", optional = true } [dev-dependencies] rstest = "0.21" bincode = "1.3.3" -cggmp21 = "0.5.0" +cggmp21 = "0.6.0" sha2 = "0.10" [features] diff --git a/tests/functional/Cargo.toml b/tests/functional/Cargo.toml index e45beef..d707cf5 100644 --- a/tests/functional/Cargo.toml +++ b/tests/functional/Cargo.toml @@ -27,7 +27,7 @@ default-features = false [dev-dependencies] rand_chacha = "0.3.1" -cggmp21 = { version = "0.5.0", features = ["curve-secp256k1"] } +cggmp21 = { version = "0.6.0", features = ["curve-secp256k1"] } givre = { version = "0.2.0", features = ["ciphersuite-ed25519"] } sha2 = "0.10.8" k256 = { version = "0.13", features = ["ecdsa"] }