diff --git a/src/btc.rs b/src/btc.rs index 20ae1a9..2a76a7d 100644 --- a/src/btc.rs +++ b/src/btc.rs @@ -240,7 +240,8 @@ mod test { script::{Builder, PushBytesBuf}, }, opcodes::all::*, - taproot::{LeafVersion, TaprootBuilder}, + secp256k1::schnorr, + taproot::{self, LeafVersion, TaprootBuilder}, }; use rand_core::OsRng; @@ -332,7 +333,7 @@ mod test { let nums_x_data = hex::decode("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0") .unwrap(); - let nums_public_key = bitcoin::XOnlyPublicKey::from_slice(&nums_x_data).unwrap(); + let nums_public_key = XOnlyPublicKey::from_slice(&nums_x_data).unwrap(); let spend_info = TaprootBuilder::new() .add_leaf(1, accept_script.clone()) @@ -357,7 +358,7 @@ mod test { .expect("Failed to create message"); let schnorr_sig = secp.sign_schnorr(&message, &depositor_keypair); - let taproot_sig = bitcoin::taproot::Signature { + let taproot_sig = taproot::Signature { signature: schnorr_sig, sighash_type: TapSighashType::All, }; @@ -390,9 +391,9 @@ mod test { Ok(proof) => proof, }; let proof_bytes = proof.to_bytes(); - let schnorr_sig = bitcoin::secp256k1::schnorr::Signature::from_slice(&proof_bytes) + let schnorr_sig = schnorr::Signature::from_slice(&proof_bytes) .expect("Failed to parse Signature from slice"); - let taproot_sig = bitcoin::taproot::Signature { + let taproot_sig = taproot::Signature { signature: schnorr_sig, sighash_type: TapSighashType::All, }; @@ -448,7 +449,7 @@ mod test { // [1] Verify the correct signature, which should succeed. let schnorr_sig = secp.sign_schnorr(&message, &tweaked.to_keypair()); - let taproot_sig = bitcoin::taproot::Signature { + let taproot_sig = taproot::Signature { signature: schnorr_sig, sighash_type: TapSighashType::All, }; @@ -458,7 +459,7 @@ mod test { // [2] Verify the correct signature, but with a different sighash type, // which should fail. - let taproot_sig = bitcoin::taproot::Signature { + let taproot_sig = taproot::Signature { signature: schnorr_sig, sighash_type: TapSighashType::None, }; @@ -470,7 +471,7 @@ mod test { // which should fail. In this case we've created the signature using // the untweaked keypair. let schnorr_sig = secp.sign_schnorr(&message, &keypair); - let taproot_sig = bitcoin::taproot::Signature { + let taproot_sig = taproot::Signature { signature: schnorr_sig, sighash_type: TapSighashType::All, }; @@ -483,7 +484,7 @@ mod test { let secret_key = secp256k1::SecretKey::new(&mut OsRng); let keypair = secp256k1::Keypair::from_secret_key(&secp, &secret_key); let schnorr_sig = secp.sign_schnorr(&message, &keypair); - let taproot_sig = bitcoin::taproot::Signature { + let taproot_sig = taproot::Signature { signature: schnorr_sig, sighash_type: TapSighashType::All, }; @@ -494,7 +495,7 @@ mod test { // [5] Same as [4], but using its tweaked key. let tweaked = keypair.tap_tweak(&secp, merkle_root); let schnorr_sig = secp.sign_schnorr(&message, &tweaked.to_keypair()); - let taproot_sig = bitcoin::taproot::Signature { + let taproot_sig = taproot::Signature { signature: schnorr_sig, sighash_type: TapSighashType::All, }; @@ -573,9 +574,9 @@ mod test { assert!(proof_deser.verify(&tweaked_public_key.x(), message)); // [1] Verify the correct signature, which should succeed. - let schnorr_sig = bitcoin::secp256k1::schnorr::Signature::from_slice(&proof_bytes) + let schnorr_sig = schnorr::Signature::from_slice(&proof_bytes) .expect("Failed to parse Signature from slice"); - let taproot_sig = bitcoin::taproot::Signature { + let taproot_sig = taproot::Signature { signature: schnorr_sig, sighash_type: TapSighashType::All, }; @@ -585,7 +586,7 @@ mod test { // [2] Verify the correct signature, but with a different sighash type, // which should fail. - let taproot_sig = bitcoin::taproot::Signature { + let taproot_sig = taproot::Signature { signature: schnorr_sig, sighash_type: TapSighashType::None, }; diff --git a/src/compute.rs b/src/compute.rs index cf5249f..8a8e3bf 100644 --- a/src/compute.rs +++ b/src/compute.rs @@ -13,43 +13,77 @@ use crate::{ }; #[allow(non_snake_case)] -/// Compute a binding value from the party ID, public nonces, and signed message using XMD-based expansion. -pub fn binding(id: &Scalar, B: &[PublicNonce], msg: &[u8]) -> Scalar { - let prefix = b"WSTS/binding"; - - // Serialize all input into a buffer - let mut buf = Vec::new(); - buf.extend_from_slice(&id.to_bytes()); +/// Compute the group commitment from the list of PartyIDs and nonce commitments +pub fn group_commitment(commitment_list: &[(Scalar, PublicNonce)]) -> Scalar { + let mut hasher = Sha256::new(); + let prefix = "WSTS/group_commitment"; - for b in B { - buf.extend_from_slice(b.D.compress().as_bytes()); - buf.extend_from_slice(b.E.compress().as_bytes()); + hasher.update(prefix.as_bytes()); + for (id, public_nonce) in commitment_list { + hasher.update(id.to_bytes()); + hasher.update(public_nonce.D.compress().as_bytes()); + hasher.update(public_nonce.E.compress().as_bytes()); } - buf.extend_from_slice(msg); + hash_to_scalar(&mut hasher) +} + +#[allow(non_snake_case)] +/// Compute the group commitment from the list of PartyIDs and nonce commitments +pub fn group_commitment_compressed(commitment_list: &[(Scalar, Compressed, Compressed)]) -> Scalar { + let mut hasher = Sha256::new(); + let prefix = "WSTS/group_commitment"; + + hasher.update(prefix.as_bytes()); + for (id, hiding_commitment, binding_commitment) in commitment_list { + hasher.update(id.to_bytes()); + hasher.update(hiding_commitment.as_bytes()); + hasher.update(binding_commitment.as_bytes()); + } - expand_to_scalar(&buf, prefix) - .expect("FATAL: DST is less than 256 bytes so operation should not fail") + hash_to_scalar(&mut hasher) } #[allow(non_snake_case)] -/// Compute a binding value from the party ID, public nonces, and signed message using XMD-based expansion. -pub fn binding_compressed(id: &Scalar, B: &[(Compressed, Compressed)], msg: &[u8]) -> Scalar { - let prefix = b"WSTS/binding"; +/// Compute a binding value from the party ID, public nonces, and signed message +pub fn binding( + id: &Scalar, + group_public_key: Point, + commitment_list: &[(Scalar, PublicNonce)], + msg: &[u8], +) -> Scalar { + let mut hasher = Sha256::new(); + let prefix = "WSTS/binding"; + let encoded_group_commitment = group_commitment(commitment_list); - // Serialize all input into a buffer - let mut buf = Vec::new(); - buf.extend_from_slice(&id.to_bytes()); + hasher.update(prefix.as_bytes()); + hasher.update(group_public_key.compress().as_bytes()); + hasher.update(msg); + hasher.update(encoded_group_commitment.to_bytes()); + hasher.update(id.to_bytes()); - for (D, E) in B { - buf.extend_from_slice(D.as_bytes()); - buf.extend_from_slice(E.as_bytes()); - } + hash_to_scalar(&mut hasher) +} - buf.extend_from_slice(msg); +#[allow(non_snake_case)] +/// Compute a binding value from the party ID, public nonces, and signed message +pub fn binding_compressed( + id: &Scalar, + group_public_key: Point, + commitment_list: &[(Scalar, Compressed, Compressed)], + msg: &[u8], +) -> Scalar { + let mut hasher = Sha256::new(); + let prefix = "WSTS/binding"; + let encoded_group_commitment = group_commitment_compressed(commitment_list); + + hasher.update(prefix.as_bytes()); + hasher.update(group_public_key.compress().as_bytes()); + hasher.update(msg); + hasher.update(encoded_group_commitment.to_bytes()); + hasher.update(id.to_bytes()); - expand_to_scalar(&buf, prefix) - .expect("FATAL: DST is less than 256 bytes so operation should not fail") + hash_to_scalar(&mut hasher) } #[allow(non_snake_case)] @@ -82,10 +116,20 @@ pub fn lambda(i: u32, key_ids: &[u32]) -> Scalar { // Is this the best way to return these values? #[allow(non_snake_case)] /// Compute the intermediate values used in both the parties and the aggregator -pub fn intermediate(msg: &[u8], party_ids: &[u32], nonces: &[PublicNonce]) -> (Vec, Point) { +pub fn intermediate( + msg: &[u8], + group_key: Point, + party_ids: &[u32], + nonces: &[PublicNonce], +) -> (Vec, Point) { + let commitment_list: Vec<(Scalar, PublicNonce)> = party_ids + .iter() + .zip(nonces) + .map(|(i, nonce)| (Scalar::from(*i), nonce.clone())) + .collect(); let rhos: Vec = party_ids .iter() - .map(|&i| binding(&id(i), nonces, msg)) + .map(|i| binding(&id(*i), group_key, &commitment_list, msg)) .collect(); let R_vec: Vec = zip(nonces, rhos) .map(|(nonce, rho)| nonce.D + rho * nonce.E) @@ -99,19 +143,21 @@ pub fn intermediate(msg: &[u8], party_ids: &[u32], nonces: &[PublicNonce]) -> (V /// Compute the aggregate nonce pub fn aggregate_nonce( msg: &[u8], + group_key: Point, party_ids: &[u32], nonces: &[PublicNonce], ) -> Result { - let compressed_nonces: Vec<(Compressed, Compressed)> = nonces + let commitment_list: Vec<(Scalar, Compressed, Compressed)> = party_ids .iter() - .map(|nonce| (nonce.D.compress(), nonce.E.compress())) + .zip(nonces) + .map(|(id, nonce)| (Scalar::from(*id), nonce.D.compress(), nonce.E.compress())) .collect(); let scalars: Vec = party_ids .iter() .flat_map(|&i| { [ Scalar::from(1), - binding_compressed(&id(i), &compressed_nonces, msg), + binding_compressed(&id(i), group_key, &commitment_list, msg), ] }) .collect(); diff --git a/src/state_machine/coordinator/fire.rs b/src/state_machine/coordinator/fire.rs index 28a163e..fccadaf 100644 --- a/src/state_machine/coordinator/fire.rs +++ b/src/state_machine/coordinator/fire.rs @@ -1019,7 +1019,7 @@ impl Coordinator { if nonce_info.nonce_recv_key_ids.len() >= self.config.threshold as usize { // We have a winning message! self.message.clone_from(&nonce_response.message); - let aggregate_nonce = self.compute_aggregate_nonce(); + let aggregate_nonce = self.compute_aggregate_nonce()?; info!("Aggregate nonce: {aggregate_nonce}"); self.move_to(State::SigShareRequest(signature_type))?; @@ -1248,7 +1248,7 @@ impl Coordinator { } #[allow(non_snake_case)] - fn compute_aggregate_nonce(&self) -> Point { + fn compute_aggregate_nonce(&self) -> Result { // XXX this needs to be key_ids for v1 and signer_ids for v2 let public_nonces = self .message_nonces @@ -1266,9 +1266,14 @@ impl Coordinator { .cloned() .flat_map(|pn| pn.nonces) .collect::>(); - let (_, R) = compute::intermediate(&self.message, &party_ids, &nonces); - R + let Some(group_key) = self.aggregate_public_key else { + return Err(Error::MissingAggregatePublicKey); + }; + let (_, aggregate_nonce) = + compute::intermediate(&self.message, group_key, &party_ids, &nonces); + + Ok(aggregate_nonce) } fn compute_num_key_ids<'a, I>(&self, signer_ids: I) -> Result @@ -1592,11 +1597,11 @@ pub mod test { coordinator::{ fire::Coordinator as FireCoordinator, test::{ - bad_signature_share_request, check_signature_shares, coordinator_state_machine, - empty_private_shares, empty_public_shares, equal_after_save_load, - feedback_messages, feedback_mutated_messages, gen_nonces, invalid_nonce, - new_coordinator, run_dkg_sign, setup, setup_with_timeouts, start_dkg_round, - start_signing_round, verify_packet_sigs, + bad_signature_share_request, btc_sign_verify, check_signature_shares, + coordinator_state_machine, empty_private_shares, empty_public_shares, + equal_after_save_load, feedback_messages, feedback_mutated_messages, + gen_nonces, invalid_nonce, new_coordinator, run_dkg_sign, setup, + setup_with_timeouts, start_dkg_round, start_signing_round, verify_packet_sigs, }, Config, Coordinator as CoordinatorTrait, State, }, @@ -1828,6 +1833,7 @@ pub mod test { let signature_type = SignatureType::Frost; let message = vec![0u8]; coordinator.state = State::NonceGather(signature_type); + coordinator.aggregate_public_key = Some(Point::from(Scalar::random(&mut rng))); let nonce_response = NonceResponse { dkg_id: 0, @@ -3732,4 +3738,15 @@ pub mod test { fn verify_packet_sigs_v2() { verify_packet_sigs::, v2::Signer>(); } + + #[test] + #[cfg(feature = "with_v1")] + fn btc_sign_verify_v1() { + btc_sign_verify::, v1::Signer>(5, 2); + } + + #[test] + fn btc_sign_verify_v2() { + btc_sign_verify::, v2::Signer>(5, 2); + } } diff --git a/src/state_machine/coordinator/frost.rs b/src/state_machine/coordinator/frost.rs index 05cbdf5..37a0012 100644 --- a/src/state_machine/coordinator/frost.rs +++ b/src/state_machine/coordinator/frost.rs @@ -551,7 +551,7 @@ impl Coordinator { ); } if self.ids_to_await.is_empty() { - let aggregate_nonce = self.compute_aggregate_nonce(); + let aggregate_nonce = self.compute_aggregate_nonce()?; info!( %aggregate_nonce, "Aggregate nonce" @@ -733,7 +733,7 @@ impl Coordinator { } #[allow(non_snake_case)] - fn compute_aggregate_nonce(&self) -> Point { + fn compute_aggregate_nonce(&self) -> Result { // XXX this needs to be key_ids for v1 and signer_ids for v2 let party_ids = self .public_nonces @@ -745,9 +745,13 @@ impl Coordinator { .values() .flat_map(|pn| pn.nonces.clone()) .collect::>(); - let (_, R) = compute::intermediate(&self.message, &party_ids, &nonces); + let Some(group_key) = self.aggregate_public_key else { + return Err(Error::MissingAggregatePublicKey); + }; + let (_, aggregate_nonce) = + compute::intermediate(&self.message, group_key, &party_ids, &nonces); - R + Ok(aggregate_nonce) } } @@ -1011,10 +1015,10 @@ pub mod test { state_machine::coordinator::{ frost::Coordinator as FrostCoordinator, test::{ - bad_signature_share_request, check_signature_shares, coordinator_state_machine, - empty_private_shares, empty_public_shares, equal_after_save_load, invalid_nonce, - new_coordinator, run_dkg_sign, setup, start_dkg_round, start_signing_round, - verify_packet_sigs, + bad_signature_share_request, btc_sign_verify, check_signature_shares, + coordinator_state_machine, empty_private_shares, empty_public_shares, + equal_after_save_load, invalid_nonce, new_coordinator, run_dkg_sign, setup, + start_dkg_round, start_signing_round, verify_packet_sigs, }, Config, Coordinator as CoordinatorTrait, State, }, @@ -1284,6 +1288,7 @@ pub mod test { let signature_type = SignatureType::Frost; let message = vec![0u8]; coordinator.state = State::NonceGather(signature_type); + coordinator.aggregate_public_key = Some(Point::from(Scalar::random(&mut rng))); let nonce_response = NonceResponse { dkg_id: 0, @@ -1614,4 +1619,15 @@ pub mod test { fn verify_packet_sigs_v2() { verify_packet_sigs::, v2::Signer>(); } + + #[test] + #[cfg(feature = "with_v1")] + fn btc_sign_verify_v1() { + btc_sign_verify::, v1::Signer>(5, 2); + } + + #[test] + fn btc_sign_verify_v2() { + btc_sign_verify::, v2::Signer>(5, 2); + } } diff --git a/src/state_machine/coordinator/mod.rs b/src/state_machine/coordinator/mod.rs index cf37777..d0c043a 100644 --- a/src/state_machine/coordinator/mod.rs +++ b/src/state_machine/coordinator/mod.rs @@ -1885,4 +1885,111 @@ pub mod test { ); } } + + use crate::btc::UnsignedTx; + + use bitcoin::{ + blockdata::script::Builder, + opcodes::all::*, + secp256k1::{schnorr, Secp256k1, XOnlyPublicKey}, + taproot::{self, LeafVersion, TaprootBuilder}, + TapSighashType, Witness, + }; + + /// Create a taproot transaction with key and script spends, then sign/verify each spend path + pub fn btc_sign_verify( + num_signers: u32, + keys_per_signer: u32, + ) { + let (mut coordinators, mut signers) = + run_dkg::(num_signers, keys_per_signer); + + let aggregate_public_key = coordinators[0] + .get_aggregate_public_key() + .expect("public key"); + let aggregate_xonly_key = XOnlyPublicKey::from_slice(&aggregate_public_key.x().to_bytes()) + .expect("failed to create XOnlyPublicKey"); + + let secp = Secp256k1::new(); + let script = Builder::new() + .push_x_only_key(&aggregate_xonly_key) + .push_opcode(OP_CHECKSIG) + .into_script(); + let spend_info = TaprootBuilder::new() + .add_leaf(0, script.clone()) + .unwrap() + .finalize(&secp, aggregate_xonly_key) + .expect("failed to finalize taproot_spend_info"); + let merkle_root = spend_info.merkle_root(); + let internal_key = spend_info.internal_key(); + let unsigned = UnsignedTx::new(internal_key); + + // test the key spend + let sighash = unsigned + .compute_sighash(&secp, merkle_root) + .expect("failed to compute taproot sighash"); + let msg: &[u8] = sighash.as_ref(); + + let raw_merkle_root = merkle_root.map(|root| { + let bytes: [u8; 32] = *root.to_raw_hash().as_ref(); + bytes + }); + + let OperationResult::SignTaproot(proof) = run_sign::( + &mut coordinators, + &mut signers, + msg, + SignatureType::Taproot(raw_merkle_root), + ) else { + panic!("taproot signature failed"); + }; + + let schnorr_sig = schnorr::Signature::from_slice(&proof.to_bytes()) + .expect("Failed to parse Signature from slice"); + let taproot_sig = taproot::Signature { + signature: schnorr_sig, + sighash_type: TapSighashType::All, + }; + + unsigned + .verify_signature(&secp, &taproot_sig, merkle_root) + .expect("signature verification failed"); + + // test the script spend + let sighash = unsigned + .compute_script_sighash(&secp, merkle_root, &script) + .expect("failed to compute taproot sighash"); + let msg: &[u8] = sighash.as_ref(); + + let OperationResult::SignSchnorr(proof) = run_sign::( + &mut coordinators, + &mut signers, + msg, + SignatureType::Schnorr, + ) else { + panic!("schnorr signature failed"); + }; + + let schnorr_sig = schnorr::Signature::from_slice(&proof.to_bytes()) + .expect("Failed to parse Signature from slice"); + let taproot_sig = taproot::Signature { + signature: schnorr_sig, + sighash_type: TapSighashType::All, + }; + + let control_block = spend_info + .control_block(&(script.clone(), LeafVersion::TapScript)) + .expect("insert the accept script into the control block"); + + println!("ControlBlock {control_block:?}"); + + let mut witness = Witness::new(); + witness.push(taproot_sig.to_vec()); + witness.push(script.as_bytes()); + witness.push(control_block.serialize()); + + unsigned + .verify_witness(&secp, witness, merkle_root) + .expect("signature verification failed"); + } } diff --git a/src/traits.rs b/src/traits.rs index 68c074d..b6fdc21 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -108,6 +108,7 @@ pub trait Signer: Clone + Debug + PartialEq { /// Compute intermediate values fn compute_intermediate( + &self, msg: &[u8], signer_ids: &[u32], key_ids: &[u32], diff --git a/src/v1.rs b/src/v1.rs index a03f56a..bccc608 100644 --- a/src/v1.rs +++ b/src/v1.rs @@ -219,8 +219,14 @@ impl Party { /// Sign `msg` with this party's share of the group private key, using the set of `signers` and corresponding `nonces` pub fn sign(&self, msg: &[u8], signers: &[u32], nonces: &[PublicNonce]) -> SignatureShare { - let (_, aggregate_nonce) = compute::intermediate(msg, signers, nonces); - let mut z = &self.nonce.d + &self.nonce.e * compute::binding(&self.id(), nonces, msg); + let (_, aggregate_nonce) = compute::intermediate(msg, self.group_key, signers, nonces); + let commitment_list: Vec<(Scalar, PublicNonce)> = signers + .iter() + .zip(nonces) + .map(|(id, nonce)| (Scalar::from(*id), nonce.clone())) + .collect(); + let mut z = &self.nonce.d + + &self.nonce.e * compute::binding(&self.id(), self.group_key, &commitment_list, msg); z += compute::challenge(&self.group_key, &aggregate_nonce, msg) * &self.private_key * compute::lambda(self.id, signers); @@ -255,7 +261,13 @@ impl Party { aggregate_nonce: &Point, tweak: Option, ) -> SignatureShare { - let mut r = &self.nonce.d + &self.nonce.e * compute::binding(&self.id(), nonces, msg); + let commitment_list: Vec<(Scalar, PublicNonce)> = signers + .iter() + .zip(nonces) + .map(|(id, nonce)| (Scalar::from(*id), nonce.clone())) + .collect(); + let mut r = &self.nonce.d + + &self.nonce.e * compute::binding(&self.id(), self.group_key, &commitment_list, msg); if tweak.is_some() && !aggregate_nonce.has_even_y() { r = -r; } @@ -327,7 +339,6 @@ impl Aggregator { } let signers: Vec = sig_shares.iter().map(|ss| ss.id).collect(); - let (_Rs, R) = compute::intermediate(msg, &signers, nonces); let mut z = Scalar::zero(); let mut cx_sign = Scalar::one(); let aggregate_public_key = self.poly[0]; @@ -341,6 +352,7 @@ impl Aggregator { } _ => aggregate_public_key, }; + let (_Rs, R) = compute::intermediate(msg, aggregate_public_key, &signers, nonces); let c = compute::challenge(&tweaked_public_key, &R, msg); for sig_share in sig_shares { @@ -374,7 +386,6 @@ impl Aggregator { } let signers: Vec = sig_shares.iter().map(|ss| ss.id).collect(); - let (Rs, R) = compute::intermediate(msg, &signers, nonces); let mut bad_party_keys = Vec::new(); let mut bad_party_sigs = Vec::new(); let aggregate_public_key = self.poly[0]; @@ -384,6 +395,7 @@ impl Aggregator { } _ => aggregate_public_key, }; + let (Rs, R) = compute::intermediate(msg, aggregate_public_key, &signers, nonces); let c = compute::challenge(&tweaked_public_key, &R, msg); let mut r_sign = Scalar::one(); let mut cx_sign = Scalar::one(); @@ -689,12 +701,13 @@ impl traits::Signer for Signer { } fn compute_intermediate( + &self, msg: &[u8], _signer_ids: &[u32], key_ids: &[u32], nonces: &[PublicNonce], ) -> (Vec, Point) { - compute::intermediate(msg, key_ids, nonces) + compute::intermediate(msg, self.group_key, key_ids, nonces) } fn validate_party_id( @@ -715,7 +728,8 @@ impl traits::Signer for Signer { key_ids: &[u32], nonces: &[PublicNonce], ) -> Vec { - let aggregate_nonce = compute::aggregate_nonce(msg, key_ids, nonces).unwrap(); + let aggregate_nonce = + compute::aggregate_nonce(msg, self.group_key, key_ids, nonces).unwrap(); self.parties .iter() .map(|p| p.sign_precomputed(msg, key_ids, nonces, &aggregate_nonce)) @@ -730,7 +744,8 @@ impl traits::Signer for Signer { nonces: &[PublicNonce], merkle_root: Option<[u8; 32]>, ) -> Vec { - let aggregate_nonce = compute::aggregate_nonce(msg, key_ids, nonces).unwrap(); + let aggregate_nonce = + compute::aggregate_nonce(msg, self.group_key, key_ids, nonces).unwrap(); let tweak = compute::tweak(&self.parties[0].group_key, merkle_root); self.parties .iter() @@ -747,7 +762,8 @@ impl traits::Signer for Signer { key_ids: &[u32], nonces: &[PublicNonce], ) -> Vec { - let aggregate_nonce = compute::aggregate_nonce(msg, key_ids, nonces).unwrap(); + let aggregate_nonce = + compute::aggregate_nonce(msg, self.group_key, key_ids, nonces).unwrap(); self.parties .iter() .map(|p| { diff --git a/src/v2.rs b/src/v2.rs index 199f0aa..5cf5c32 100644 --- a/src/v2.rs +++ b/src/v2.rs @@ -256,13 +256,18 @@ impl Party { } else { self.group_key }; - let (_, R) = compute::intermediate(msg, party_ids, nonces); + let (_, R) = compute::intermediate(msg, self.group_key, party_ids, nonces); let c = compute::challenge(&tweaked_public_key, &R, msg); - let mut r = &self.nonce.d + &self.nonce.e * compute::binding(&self.id(), nonces, msg); + let commitment_list: Vec<(Scalar, PublicNonce)> = party_ids + .iter() + .zip(nonces) + .map(|(id, nonce)| (Scalar::from(*id), nonce.clone())) + .collect(); + let mut r = &self.nonce.d + + &self.nonce.e * compute::binding(&self.id(), self.group_key, &commitment_list, msg); if tweak.is_some() && !R.has_even_y() { r = -r; } - let mut cx = Scalar::zero(); for key_id in self.key_ids.iter() { cx += c * &self.private_keys[key_id] * compute::lambda(*key_id, key_ids); @@ -310,7 +315,7 @@ impl Aggregator { } let party_ids: Vec = sig_shares.iter().map(|ss| ss.id).collect(); - let (_Rs, R) = compute::intermediate(msg, &party_ids, nonces); + let (_Rs, R) = compute::intermediate(msg, self.poly[0], &party_ids, nonces); let mut z = Scalar::zero(); let mut cx_sign = Scalar::one(); let aggregate_public_key = self.poly[0]; @@ -361,7 +366,7 @@ impl Aggregator { } let party_ids: Vec = sig_shares.iter().map(|ss| ss.id).collect(); - let (Rs, R) = compute::intermediate(msg, &party_ids, nonces); + let (Rs, R) = compute::intermediate(msg, self.poly[0], &party_ids, nonces); let mut bad_party_keys = Vec::new(); let mut bad_party_sigs = Vec::new(); let aggregate_public_key = self.poly[0]; @@ -637,12 +642,13 @@ impl traits::Signer for Party { } fn compute_intermediate( + &self, msg: &[u8], signer_ids: &[u32], _key_ids: &[u32], nonces: &[PublicNonce], ) -> (Vec, Point) { - compute::intermediate(msg, signer_ids, nonces) + compute::intermediate(msg, self.group_key, signer_ids, nonces) } fn validate_party_id(